Compare commits
337 Commits
b71ea7d346
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 27d731a5b0 | |||
| b8f797f14d | |||
| 6feef53733 | |||
| 878e096c57 | |||
| 2ded140ad5 | |||
| e2ca249d13 | |||
| e782eab62a | |||
| d8e08b237d | |||
|
|
88cb1dc16a | ||
|
|
8d6a09213e | ||
|
|
54f412ced2 | ||
|
|
51b9c62188 | ||
|
|
bb5525778d | ||
|
|
ee793632df | ||
|
|
22bb585f60 | ||
|
|
ed7237c8dd | ||
|
|
f71bcf37e9 | ||
|
|
c7e366af60 | ||
| 86f4e3141e | |||
| 869ba9858f | |||
| 5dee104377 | |||
| 36e1d5fad1 | |||
| 304490d661 | |||
| ff4ab9efe2 | |||
| 470120e5e9 | |||
| ce35ef588f | |||
| df665e3b98 | |||
| 4bc6df4d91 | |||
| 383932e7e7 | |||
| e3494d50b7 | |||
| 5197ad1481 | |||
| dbfb7e7e47 | |||
| b9a40bb12e | |||
| 8ddaf1d13e | |||
| 14532a15bf | |||
| 0149a77f21 | |||
| a17d260c6c | |||
| b639df0a39 | |||
| 0fddf5669f | |||
| 9c46b9f2da | |||
| 87d9769d9b | |||
| 19ecf104fa | |||
| 5b5b034e78 | |||
| 5ebb3f72e3 | |||
| 3cfecbf598 | |||
| 4895b9c8f8 | |||
| 36fe78e152 | |||
| 489180f5a1 | |||
| b65810bbbb | |||
| 278fcfd75b | |||
| 41db75b183 | |||
| f4a921e268 | |||
| cdb52dc6fd | |||
| f14f6c1f15 | |||
| 6a24719342 | |||
| 631ab8cba5 | |||
| 872878b9d7 | |||
| 5a30b0ece4 | |||
| 37200617ea | |||
| 8ab66db1f2 | |||
| cbd52721ac | |||
| 3003559d7a | |||
| 6cabdbb6a3 | |||
| 4dd54e206e | |||
| 84cf5c8e4d | |||
| 84d6e7a511 | |||
| 89238cc2d1 | |||
| 7b177f21c8 | |||
| 2deb235c8d | |||
| c452724c9e | |||
| 13e65774cc | |||
| f2d2dc9a32 | |||
| 459620e1dd | |||
| df2541108b | |||
| 5dcd5313a5 | |||
| ef9d725f59 | |||
| 6f4ab073df | |||
| f6b95331e1 | |||
| 82de285891 | |||
|
|
cbd86de3e8 | ||
|
|
a5160b35dd | ||
|
|
60e1627494 | ||
|
|
b9f08bc21c | ||
|
|
758616c95e | ||
|
|
7376b49e38 | ||
|
|
b65c354ef0 | ||
|
|
38258a98c1 | ||
|
|
7666708ab5 | ||
|
|
d3b67bc429 | ||
|
|
97f992aef5 | ||
|
|
cc6f93ae1c | ||
|
|
402990bd3c | ||
|
|
43cdef4910 | ||
|
|
348a55fc60 | ||
|
|
719bc9c941 | ||
|
|
37381af042 | ||
|
|
34fe996d91 | ||
|
|
252fc10243 | ||
|
|
c8a9245b54 | ||
|
|
c56bcc198e | ||
|
|
538abec212 | ||
|
|
19666e649d | ||
|
|
1df9235036 | ||
| dd9c1c7ca2 | |||
| f9cc735318 | |||
| 2a8eb3c0ad | |||
| 7a4cdb3d1f | |||
| 453b6d1813 | |||
| bd2b5ff62f | |||
| 849de7a204 | |||
| c7423fb6fd | |||
| ad51e4b1eb | |||
| df2ebe0cc2 | |||
| 5c06f287ab | |||
| c3e3a0377d | |||
| cf34a54170 | |||
| 16f48f125b | |||
| e3faa2f570 | |||
| 0d30b5ff87 | |||
| 563375f6e3 | |||
| 3864b0f68b | |||
| 35e03269e7 | |||
| 8b212d541e | |||
| dd4cecc15d | |||
| 0dedb506e1 | |||
| 24f146ca26 | |||
| 2692553865 | |||
| d90c2fab96 | |||
| 854e36e71f | |||
| 1d31f2aff9 | |||
| 11206cf84f | |||
| b48ebd8e88 | |||
| 12d17e0808 | |||
| 1dee3180d5 | |||
| bd4046a6c1 | |||
| af6f94c1ed | |||
| 7bfb56b664 | |||
| 1a6eced316 | |||
| c82749bcbf | |||
| e8fa149532 | |||
| aaa7beb92a | |||
| 5cce52ec27 | |||
| 3f36f048b2 | |||
| 92e8d9e778 | |||
| 2d04670fef | |||
| c0085b4c18 | |||
| 59ea5e3e67 | |||
| a9f2c4c2f7 | |||
| 001f4bf2c5 | |||
| bfe6c12ee0 | |||
| e1260e49f0 | |||
| 8b86eca838 | |||
| fb12cb6c98 | |||
| 2635bfb223 | |||
| 5245cd04ff | |||
| 9d5334e7dc | |||
| e5bb61376a | |||
| d3aa8c715b | |||
| 6720e66b23 | |||
| 71470fc21d | |||
| 9191ec4179 | |||
| e0d83c0a14 | |||
| 03bcfb6fc9 | |||
| 284ced6059 | |||
| 88c6e6d214 | |||
| 6263848a0a | |||
| 83f173fdc4 | |||
| 7c687c0541 | |||
| 48e9812224 | |||
| 1199c61ae8 | |||
| a55b51e504 | |||
| 752f781f54 | |||
| 9b800dce20 | |||
| 0fa1a418de | |||
| cc54539aba | |||
| ac4c4cb69a | |||
| 73ccb9e43b | |||
| b6ab59ae4a | |||
| 868e11ff62 | |||
| 38d819adac | |||
| 9b3bb925f9 | |||
| a92d57d9cf | |||
| 39fcee2b50 | |||
| d0597e28e8 | |||
| d8d77652ac | |||
| 62612897bd | |||
| 99c50fb348 | |||
| 8aaf11f39d | |||
| 152189fefd | |||
| 5a4b8427be | |||
| e4a644a636 | |||
| 25c6c41b26 | |||
| c672a10c97 | |||
| 1e21218f31 | |||
| 68e7ee54f9 | |||
| 2a749267b3 | |||
| edc1de2034 | |||
| ae79a60605 | |||
| 6b8286a386 | |||
| 1fabc29e4f | |||
| 56fb34d987 | |||
| 47ddde239e | |||
| 5a4d2d8553 | |||
| 4e209e29fc | |||
| 78aaea67e6 | |||
| 98261f4e21 | |||
| aab8174500 | |||
| fb6d6af12b | |||
| f82f4d2c65 | |||
| 90ee3f6a5d | |||
| a24ec1ab3e | |||
| b8c30d520e | |||
| f69f323542 | |||
| 3621820060 | |||
| 0e0f27c124 | |||
| 5404530785 | |||
| 289b6109e4 | |||
| 679c065aaa | |||
| a02cac8778 | |||
| 42fd176fad | |||
| e529027587 | |||
| cc2adab5e5 | |||
| a0233fd876 | |||
| 0afe9870c0 | |||
| 784b4b1f05 | |||
| 1634b4b7b1 | |||
| 576b2d59d9 | |||
| 3da16ba640 | |||
| 71a0220c3f | |||
| f53603083a | |||
| 92e7d44d3b | |||
| f8211e9e9d | |||
| 1782844543 | |||
| 2aa7cabcbd | |||
| 28f35101f9 | |||
| f8c5502905 | |||
| 961b87de3d | |||
| 6b036f4f91 | |||
| 46b7ae29cd | |||
| 84e403f411 | |||
| 3b77345aee | |||
| 6d04a4afd1 | |||
| 1f250d55b0 | |||
| c422de445d | |||
| f9a73fbe0c | |||
| 282ce3a0b7 | |||
| 26f2da1313 | |||
| bc700e2cd2 | |||
| ea5389df85 | |||
| 87e1bb9187 | |||
| 68cc919bad | |||
| 374365d250 | |||
| 8c79b3a156 | |||
| 0583d07f26 | |||
| fd7744e94e | |||
| a5aac1d0ec | |||
| 165152b7cf | |||
| f1f9e8d791 | |||
| 35171add0c | |||
| 030dcf8b58 | |||
| 71411d4027 | |||
| da1b05347e | |||
| d932fb522c | |||
| 134a808633 | |||
|
|
00efb14f2c | ||
| fe40cd001f | |||
| f96b73bf38 | |||
| c8f3b29329 | |||
| 07fca00344 | |||
| e4aec494c8 | |||
| 009bb623b5 | |||
| f8e7f8c974 | |||
| 959b56c4bb | |||
| 5a56125444 | |||
| 48039b8fd5 | |||
| 31d1d9d171 | |||
| 1419455b36 | |||
| a6111cdc66 | |||
| 3caa6b9bd3 | |||
| fb08a45c57 | |||
| 0588ba33d8 | |||
| 535fdbb7b4 | |||
| 4206a962ff | |||
| ae059e4416 | |||
| d771dbbc9e | |||
| 73ffaaab08 | |||
| b79fcf936b | |||
| 674c14dd7c | |||
| b326e7e1b3 | |||
| 3f8ba7d76c | |||
| 13346b610a | |||
| 8bc9b85049 | |||
| 141e77f315 | |||
| f9a4d93495 | |||
| 79f771b3ea | |||
| 8738c15804 | |||
| 70c07c9595 | |||
| 96fe9c99da | |||
| 1e62a70866 | |||
| f4aa0b5965 | |||
| 1700fe978d | |||
| 37c88812e1 | |||
| 34efb662ec | |||
| dcbff90ed8 | |||
| 404a1c4793 | |||
| 1cdd28738a | |||
| 45c7259ce8 | |||
| 6d9985051e | |||
| b9f5a3f10c | |||
| 243cc97aa2 | |||
| bb43bfa064 | |||
| b4966585ae | |||
| ae548d530f | |||
|
|
9628b46ba0 | ||
|
|
1f8142852e | ||
|
|
bdd78be66c | ||
|
|
470902911e | ||
|
|
3f7ebdb632 | ||
|
|
23ef1a5797 | ||
|
|
4a7f2a41fa | ||
|
|
5f9e716ca6 | ||
|
|
91c8b98f44 | ||
|
|
10fc56b262 | ||
|
|
71368e5c85 | ||
|
|
fb649a5c68 | ||
|
|
f4abac1103 | ||
|
|
4c8e9be695 | ||
|
|
b190e4f5e9 | ||
|
|
4a1b221478 | ||
|
|
475acc0a56 | ||
|
|
f8dd16454f | ||
|
|
8cad1df95d | ||
|
|
4ee79d6cd8 | ||
|
|
c0c0650fee | ||
|
|
73059d4554 | ||
|
|
e774afc85e | ||
|
|
1fc4570210 |
33
ReC.sln
33
ReC.sln
@@ -15,6 +15,21 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReC.Application", "src\ReC.
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReC.Client", "src\ReC.Client\ReC.Client.csproj", "{DA3A6BDD-8045-478F-860B-D1F0EB97F02B}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReC.Client", "src\ReC.Client\ReC.Client.csproj", "{DA3A6BDD-8045-478F-860B-D1F0EB97F02B}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
assets\icon.png = assets\icon.png
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "infrastructure", "infrastructure", "{3F88DACC-CEC0-4D9A-8BAA-37F67B02DC04}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "presentation", "presentation", "{3D6EF9B9-D00D-432A-8477-067902B5CE8E}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{2CEF945E-94D6-4273-9BE1-20B628CD0A57}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{8353C9B1-CC4A-4097-A936-C06D4C618415}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReC.Tests", "tests\ReC.Tests\ReC.Tests.csproj", "{457ED5AC-F4A0-41C3-9758-4A3C272EDC11}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -41,16 +56,24 @@ Global
|
|||||||
{DA3A6BDD-8045-478F-860B-D1F0EB97F02B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{DA3A6BDD-8045-478F-860B-D1F0EB97F02B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{DA3A6BDD-8045-478F-860B-D1F0EB97F02B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{DA3A6BDD-8045-478F-860B-D1F0EB97F02B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{DA3A6BDD-8045-478F-860B-D1F0EB97F02B}.Release|Any CPU.Build.0 = Release|Any CPU
|
{DA3A6BDD-8045-478F-860B-D1F0EB97F02B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{457ED5AC-F4A0-41C3-9758-4A3C272EDC11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{457ED5AC-F4A0-41C3-9758-4A3C272EDC11}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{457ED5AC-F4A0-41C3-9758-4A3C272EDC11}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{457ED5AC-F4A0-41C3-9758-4A3C272EDC11}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{420218AD-3C27-4003-9A84-36C92352F175} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
{420218AD-3C27-4003-9A84-36C92352F175} = {3D6EF9B9-D00D-432A-8477-067902B5CE8E}
|
||||||
{2917BEA4-6C70-40CD-BD46-57D4ADB40296} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
{2917BEA4-6C70-40CD-BD46-57D4ADB40296} = {2CEF945E-94D6-4273-9BE1-20B628CD0A57}
|
||||||
{587A4D14-EFDA-4BE3-8912-D3AF84743079} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
{587A4D14-EFDA-4BE3-8912-D3AF84743079} = {3F88DACC-CEC0-4D9A-8BAA-37F67B02DC04}
|
||||||
{109645F5-441D-476B-B7D2-FBEAA8EBAE14} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
{109645F5-441D-476B-B7D2-FBEAA8EBAE14} = {2CEF945E-94D6-4273-9BE1-20B628CD0A57}
|
||||||
{DA3A6BDD-8045-478F-860B-D1F0EB97F02B} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
{DA3A6BDD-8045-478F-860B-D1F0EB97F02B} = {3D6EF9B9-D00D-432A-8477-067902B5CE8E}
|
||||||
|
{3F88DACC-CEC0-4D9A-8BAA-37F67B02DC04} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||||
|
{3D6EF9B9-D00D-432A-8477-067902B5CE8E} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||||
|
{2CEF945E-94D6-4273-9BE1-20B628CD0A57} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||||
|
{457ED5AC-F4A0-41C3-9758-4A3C272EDC11} = {8353C9B1-CC4A-4097-A936-C06D4C618415}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {F7B09104-4072-4635-9492-9C7C68D96ABD}
|
SolutionGuid = {F7B09104-4072-4635-9492-9C7C68D96ABD}
|
||||||
|
|||||||
BIN
assets/icon.png
Normal file
BIN
assets/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
33
src/ReC.API/Controllers/CommonController.cs
Normal file
33
src/ReC.API/Controllers/CommonController.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
|
||||||
|
namespace ReC.API.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class CommonController(IMediator mediator) : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> CreateObject([FromBody] InsertObjectProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var id = await mediator.Send(procedure, cancel);
|
||||||
|
return StatusCode(StatusCodes.Status201Created, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut]
|
||||||
|
public async Task<IActionResult> UpdateObject([FromBody] UpdateObjectProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var result = await mediator.Send(procedure, cancel);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
public async Task<IActionResult> DeleteObject([FromBody] DeleteObjectProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var result = await mediator.Send(procedure, cancel);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/ReC.API/Controllers/EndpointAuthController.cs
Normal file
56
src/ReC.API/Controllers/EndpointAuthController.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
using ReC.Application.EndpointAuth.Commands;
|
||||||
|
|
||||||
|
namespace ReC.API.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class EndpointAuthController(IMediator mediator, IConfiguration config) : ControllerBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts an endpoint authentication record via the ENDPOINT_AUTH insert procedure.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="procedure">InsertEndpointAuthProcedure payload.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>The created ENDPOINT_AUTH identifier.</returns>
|
||||||
|
[HttpPost]
|
||||||
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
|
public async Task<IActionResult> Post([FromBody] InsertEndpointAuthProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var id = await mediator.ExecuteInsertProcedure(procedure, config["AddedWho"], cancel);
|
||||||
|
return StatusCode(StatusCodes.Status201Created, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates an endpoint authentication record via the ENDPOINT_AUTH update procedure.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">ENDPOINT_AUTH identifier to update.</param>
|
||||||
|
/// <param name="procedure">UpdateEndpointAuthProcedure payload.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>No content on success.</returns>
|
||||||
|
[HttpPut("{id:long}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public async Task<IActionResult> Put([FromRoute] long id, [FromBody] UpdateEndpointAuthProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
await mediator.ExecuteUpdateProcedure(procedure, id, cancel: cancel);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes endpoint authentication records via the ENDPOINT_AUTH delete procedure for the specified id range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="procedure">DeleteEndpointAuthProcedure payload (Start, End, Force).</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>No content on success.</returns>
|
||||||
|
[HttpDelete]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public async Task<IActionResult> Delete([FromBody] DeleteEndpointAuthProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
await mediator.ExecuteDeleteProcedure(procedure, cancel);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/ReC.API/Controllers/EndpointParamsController.cs
Normal file
56
src/ReC.API/Controllers/EndpointParamsController.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
using ReC.Application.EndpointParams.Commands;
|
||||||
|
|
||||||
|
namespace ReC.API.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class EndpointParamsController(IMediator mediator, IConfiguration config) : ControllerBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts endpoint parameter records via the ENDPOINT_PARAMS insert procedure.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="procedure">InsertEndpointParamsProcedure payload.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>The created ENDPOINT_PARAMS identifier.</returns>
|
||||||
|
[HttpPost]
|
||||||
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
|
public async Task<IActionResult> Post([FromBody] InsertEndpointParamsProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var id = await mediator.ExecuteInsertProcedure(procedure, config["AddedWho"], cancel);
|
||||||
|
return StatusCode(StatusCodes.Status201Created, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates endpoint parameter records via the ENDPOINT_PARAMS update procedure.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">ENDPOINT_PARAMS identifier to update.</param>
|
||||||
|
/// <param name="procedure">UpdateEndpointParamsProcedure payload.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>No content on success.</returns>
|
||||||
|
[HttpPut("{id:long}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public async Task<IActionResult> Put([FromRoute] long id, [FromBody] UpdateEndpointParamsProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
await mediator.ExecuteUpdateProcedure(procedure, id, cancel: cancel);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes endpoint parameter records via the ENDPOINT_PARAMS delete procedure for the specified id range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="procedure">DeleteEndpointParamsProcedure payload (Start, End, Force).</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>No content on success.</returns>
|
||||||
|
[HttpDelete]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public async Task<IActionResult> Delete([FromBody] DeleteEndpointParamsProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
await mediator.ExecuteDeleteProcedure(procedure, cancel);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/ReC.API/Controllers/EndpointsController.cs
Normal file
56
src/ReC.API/Controllers/EndpointsController.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
using ReC.Application.Endpoints.Commands;
|
||||||
|
|
||||||
|
namespace ReC.API.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class EndpointsController(IMediator mediator, IConfiguration config) : ControllerBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts an endpoint via the ENDPOINT insert procedure.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="procedure">InsertEndpointProcedure payload.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>The created ENDPOINT identifier.</returns>
|
||||||
|
[HttpPost]
|
||||||
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
|
public async Task<IActionResult> Post([FromBody] InsertEndpointProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var id = await mediator.ExecuteInsertProcedure(procedure, config["AddedWho"], cancel);
|
||||||
|
return StatusCode(StatusCodes.Status201Created, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates an endpoint via the ENDPOINT update procedure.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">ENDPOINT identifier to update.</param>
|
||||||
|
/// <param name="procedure">UpdateEndpointProcedure payload.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>No content on success.</returns>
|
||||||
|
[HttpPut("{id:long}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public async Task<IActionResult> Put([FromRoute] long id, [FromBody] UpdateEndpointProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
await mediator.ExecuteUpdateProcedure(procedure, id, cancel: cancel);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes endpoints via the ENDPOINT delete procedure for the specified id range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="procedure">DeleteEndpointProcedure payload (Start, End, Force).</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>No content on success.</returns>
|
||||||
|
[HttpDelete]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public async Task<IActionResult> Delete([FromBody] DeleteEndpointProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
await mediator.ExecuteDeleteProcedure(procedure, cancel);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using ReC.API.Extensions;
|
|
||||||
using ReC.Application.OutResults.Queries;
|
|
||||||
|
|
||||||
namespace ReC.API.Controllers;
|
|
||||||
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ApiController]
|
|
||||||
public class OutResController(IMediator mediator, IConfiguration config) : ControllerBase
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets output results based on the provided query parameters.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="query">The query to filter output results.</param>
|
|
||||||
/// <param name="cancel">A token to cancel the operation.</param>
|
|
||||||
/// <returns>A list of output results matching the query.</returns>
|
|
||||||
[HttpGet]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
||||||
public async Task<IActionResult> Get([FromQuery] ReadOutResQuery query, CancellationToken cancel) => Ok(await mediator.Send(query, cancel));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets output results for a fake/test profile.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancel">A token to cancel the operation.</param>
|
|
||||||
/// <returns>A list of output results for the fake profile.</returns>
|
|
||||||
[HttpGet("fake")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
||||||
public async Task<IActionResult> Get(CancellationToken cancel) => Ok(await mediator.Send(new ReadOutResQuery()
|
|
||||||
{
|
|
||||||
ProfileId = config.GetFakeProfileId()
|
|
||||||
}, cancel));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a specific output result for a fake/test profile and action.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="actionId">The ID of the action to retrieve the result for.</param>
|
|
||||||
/// <param name="cancel">A token to cancel the operation.</param>
|
|
||||||
/// <param name="resultType">Specifies which part of the result to return (Full, Header, or Body).</param>
|
|
||||||
/// <returns>The requested output result or a part of it (header/body).</returns>
|
|
||||||
[HttpGet("fake/{actionId}")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
||||||
public async Task<IActionResult> Get([FromRoute] long actionId, CancellationToken cancel, ResultType resultType = ResultType.Full)
|
|
||||||
{
|
|
||||||
var res = (await mediator.Send(new ReadOutResQuery()
|
|
||||||
{
|
|
||||||
ProfileId = config.GetFakeProfileId(),
|
|
||||||
ActionId = actionId
|
|
||||||
}, cancel)).First();
|
|
||||||
|
|
||||||
return resultType switch
|
|
||||||
{
|
|
||||||
ResultType.Body => res.Body is null ? Ok(new object { }) : Ok(res.Body.JsonToDynamic()),
|
|
||||||
ResultType.Header => res.Header is null ? Ok(new object { }) : Ok(res.Header.JsonToDynamic()),
|
|
||||||
_ => Ok(res),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines the type of result to be returned from an output result query.
|
|
||||||
/// </summary>
|
|
||||||
public enum ResultType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Return the full result object.
|
|
||||||
/// </summary>
|
|
||||||
Full,
|
|
||||||
/// <summary>
|
|
||||||
/// Return only the header part of the result.
|
|
||||||
/// </summary>
|
|
||||||
Header,
|
|
||||||
/// <summary>
|
|
||||||
/// Return only the body part of the result.
|
|
||||||
/// </summary>
|
|
||||||
Body
|
|
||||||
}
|
|
||||||
63
src/ReC.API/Controllers/ProfileController.cs
Normal file
63
src/ReC.API/Controllers/ProfileController.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
using ReC.Application.Profile.Commands;
|
||||||
|
using ReC.Application.Profile.Queries;
|
||||||
|
|
||||||
|
namespace ReC.API.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class ProfileController(IMediator mediator) : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> Get([FromQuery] ReadProfileViewQuery query, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
return Ok(await mediator.Send(query, cancel));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts a profile via the PROFILE insert procedure.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="procedure">InsertProfileProcedure payload.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>The created profile identifier.</returns>
|
||||||
|
[HttpPost]
|
||||||
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
|
public async Task<IActionResult> Post([FromBody] InsertProfileProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var id = await mediator.ExecuteInsertProcedure(procedure, cancel: cancel);
|
||||||
|
return CreatedAtAction(nameof(Get), new { id }, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates a profile via the PROFILE update procedure.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">Profile identifier to update.</param>
|
||||||
|
/// <param name="procedure">UpdateProfileProcedure payload.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>No content on success.</returns>
|
||||||
|
[HttpPut("{id:long}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public async Task<IActionResult> Put([FromRoute] long id, [FromBody] UpdateProfileProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
await mediator.ExecuteUpdateProcedure(procedure, id, cancel: cancel);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes profile records via the PROFILE delete procedure for the specified id range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="procedure">DeleteProfileProcedure payload (Start, End, Force).</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>No content on success.</returns>
|
||||||
|
[HttpDelete]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public async Task<IActionResult> Delete([FromBody] DeleteProfileProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
await mediator.ExecuteDeleteProcedure(procedure, cancel);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using ReC.API.Extensions;
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
using ReC.API.Models;
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
using ReC.Application.RecActions.Commands;
|
using ReC.Application.RecActions.Commands;
|
||||||
using ReC.Application.RecActions.Queries;
|
using ReC.Application.RecActions.Queries;
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace ReC.API.Controllers;
|
namespace ReC.API.Controllers;
|
||||||
|
|
||||||
@@ -18,24 +18,11 @@ public class RecActionController(IMediator mediator, IConfiguration config) : Co
|
|||||||
/// <param name="profileId">The ID of the profile.</param>
|
/// <param name="profileId">The ID of the profile.</param>
|
||||||
/// <param name="cancel">A token to cancel the operation.</param>
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
/// <returns>An HTTP 202 Accepted response indicating the process has been started.</returns>
|
/// <returns>An HTTP 202 Accepted response indicating the process has been started.</returns>
|
||||||
[HttpPost("invoke/{cmd}")]
|
[HttpPost("invoke/{profileId}")]
|
||||||
[ProducesResponseType(StatusCodes.Status202Accepted)]
|
[ProducesResponseType(StatusCodes.Status202Accepted)]
|
||||||
public async Task<IActionResult> Invoke([FromRoute] int profileId, CancellationToken cancel)
|
public async Task<IActionResult> Invoke([FromRoute] int profileId, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
await mediator.InvokeBatchRecAction(profileId, cancel);
|
await mediator.InvokeBatchRecActionView(profileId, cancel);
|
||||||
return Accepted();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invokes a batch of RecActions for a fake/test profile.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancel">A token to cancel the operation.</param>
|
|
||||||
/// <returns>An HTTP 202 Accepted response indicating the process has been started.</returns>
|
|
||||||
[HttpPost("invoke/fake")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status202Accepted)]
|
|
||||||
public async Task<IActionResult> Invoke(CancellationToken cancel)
|
|
||||||
{
|
|
||||||
await mediator.InvokeBatchRecAction(config.GetFakeProfileId(), cancel);
|
|
||||||
return Accepted();
|
return Accepted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,26 +30,12 @@ public class RecActionController(IMediator mediator, IConfiguration config) : Co
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all RecActions for a given profile.
|
/// Gets all RecActions for a given profile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="profileId">The ID of the profile.</param>
|
/// <param name="query"></param>
|
||||||
/// <param name="cancel">A token to cancel the operation.</param>
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
/// <returns>A list of RecActions for the specified profile.</returns>
|
/// <returns>A list of RecActions for the specified profile.</returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
public async Task<IActionResult> Get([FromQuery] ReadRecActionQuery query, CancellationToken cancel) => Ok(await mediator.Send(query, cancel));
|
public async Task<IActionResult> Get([FromQuery] ReadRecActionViewQuery query, CancellationToken cancel) => Ok(await mediator.Send(query, cancel));
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets all RecActions for a fake/test profile.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancel">A token to cancel the operation.</param>
|
|
||||||
/// <param name="invoked"></param>
|
|
||||||
/// <returns>A list of RecActions for the fake profile.</returns>
|
|
||||||
[HttpGet("fake")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
||||||
public async Task<IActionResult> Get(CancellationToken cancel, [FromQuery] bool invoked = false) => Ok(await mediator.Send(new ReadRecActionQuery()
|
|
||||||
{
|
|
||||||
ProfileId = config.GetFakeProfileId(),
|
|
||||||
Invoked = invoked
|
|
||||||
}, cancel));
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new RecAction.
|
/// Creates a new RecAction.
|
||||||
@@ -72,79 +45,39 @@ public class RecActionController(IMediator mediator, IConfiguration config) : Co
|
|||||||
/// <returns>An HTTP 201 Created response.</returns>
|
/// <returns>An HTTP 201 Created response.</returns>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
public async Task<IActionResult> CreateAction([FromBody] CreateRecActionCommand command, CancellationToken cancel)
|
public async Task<IActionResult> Create([FromBody] InsertActionProcedure command, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
await mediator.Send(command, cancel);
|
await mediator.ExecuteInsertProcedure(command, config["AddedWho"], cancel);
|
||||||
|
|
||||||
return CreatedAtAction(nameof(CreateAction), null);
|
return StatusCode(StatusCodes.Status201Created);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new fake RecAction for testing purposes.
|
/// Updates a RecAction via the ACTION update procedure.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="id">RecAction identifier to update.</param>
|
||||||
|
/// <param name="procedure">UpdateActionProcedure payload.</param>
|
||||||
/// <param name="cancel">A token to cancel the operation.</param>
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
/// <param name="request">The optional request body and header for the fake action.</param>
|
/// <returns>No content on success.</returns>
|
||||||
/// <param name="endpointUri">The target endpoint URI.</param>
|
[HttpPut("{id:long}")]
|
||||||
/// <param name="endpointPath">The optional path to append to the endpoint URI.</param>
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
/// <param name="type">The HTTP method type (e.g., GET, POST).</param>
|
public async Task<IActionResult> Update([FromRoute] long id, [FromBody] UpdateActionProcedure procedure, CancellationToken cancel)
|
||||||
/// <returns>An HTTP 201 Created response.</returns>
|
|
||||||
[HttpPost("fake")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
|
||||||
public async Task<IActionResult> CreateFakeAction(
|
|
||||||
CancellationToken cancel,
|
|
||||||
[FromBody] FakeRequest? request = null,
|
|
||||||
[FromQuery] string endpointUri = "https://jsonplaceholder.typicode.com/posts",
|
|
||||||
[FromQuery] string? endpointPath = "1",
|
|
||||||
[FromQuery] string type = "GET")
|
|
||||||
{
|
{
|
||||||
if (endpointPath is not null)
|
await mediator.ExecuteUpdateProcedure(procedure, id, cancel: cancel);
|
||||||
endpointUri = new Uri(new Uri(endpointUri.TrimEnd('/') + "/"), endpointPath.TrimStart('/')).ToString();
|
return NoContent();
|
||||||
|
|
||||||
var bodyJson = request?.Body is not null ? JsonSerializer.Serialize(request.Body, options: new() { WriteIndented = false }) : null;
|
|
||||||
var headerJson = request?.Header is not null ? JsonSerializer.Serialize(request.Header, options: new() { WriteIndented = false }) : null;
|
|
||||||
|
|
||||||
await mediator.Send(new CreateRecActionCommand()
|
|
||||||
{
|
|
||||||
ProfileId = config.GetFakeProfileId(),
|
|
||||||
EndpointUri = endpointUri,
|
|
||||||
Type = type,
|
|
||||||
BodyQuery = $@"SELECT '{bodyJson ?? "NULL"}' AS REQUEST_BODY;",
|
|
||||||
HeaderQuery = headerJson is not null ? $@"SELECT '{headerJson}' AS REQUEST_HEADER;" : null,
|
|
||||||
Active = true,
|
|
||||||
EndpointAuthId = 4
|
|
||||||
}, cancel);
|
|
||||||
|
|
||||||
return CreatedAtAction(nameof(CreateFakeAction), null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes all RecActions associated with a specific profile.
|
/// Deletes RecActions via the ACTION delete procedure for the specified id range.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cmd"></param>
|
/// <param name="procedure">DeleteActionProcedure payload (Start, End, Force).</param>
|
||||||
/// <param name="cancel">A token to cancel the operation.</param>
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
/// <returns>An HTTP 204 No Content response upon successful deletion.</returns>
|
/// <returns>An HTTP 204 No Content response upon successful deletion.</returns>
|
||||||
[HttpDelete]
|
[HttpDelete]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
public async Task<IActionResult> Delete([FromQuery] DeleteRecActionsCommand cmd, CancellationToken cancel)
|
public async Task<IActionResult> Delete([FromBody] DeleteActionProcedure procedure, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
await mediator.Send(cmd, cancel);
|
await mediator.ExecuteDeleteProcedure(procedure, cancel);
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deletes all RecActions for a fake/test profile.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancel">A token to cancel the operation.</param>
|
|
||||||
/// <returns>An HTTP 204 No Content response upon successful deletion.</returns>
|
|
||||||
[HttpDelete("fake")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
||||||
public async Task<IActionResult> Delete(CancellationToken cancel)
|
|
||||||
{
|
|
||||||
await mediator.Send(new DeleteRecActionsCommand()
|
|
||||||
{
|
|
||||||
ProfileId = config.GetFakeProfileId()
|
|
||||||
}, cancel);
|
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
#endregion CRUD
|
#endregion CRUD
|
||||||
|
|||||||
69
src/ReC.API/Controllers/ResultController.cs
Normal file
69
src/ReC.API/Controllers/ResultController.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using ReC.API.Extensions;
|
||||||
|
using ReC.API.Models;
|
||||||
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
using ReC.Application.Results.Commands;
|
||||||
|
using ReC.Application.Results.Queries;
|
||||||
|
|
||||||
|
namespace ReC.API.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class ResultController(IMediator mediator) : ControllerBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets output results based on the provided query parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">The query to filter output results.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>A list of output results matching the query.</returns>
|
||||||
|
[HttpGet]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
public async Task<IActionResult> Get([FromQuery] ReadResultViewQuery query, CancellationToken cancel) => Ok(await mediator.Send(query, cancel));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts a RESULT record via the insert procedure.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="procedure">InsertResultProcedure payload.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>The created RESULT identifier.</returns>
|
||||||
|
[HttpPost]
|
||||||
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
|
public async Task<IActionResult> Post([FromBody] InsertResultProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var id = await mediator.ExecuteInsertProcedure(procedure, cancel: cancel);
|
||||||
|
return CreatedAtAction(nameof(Get), new { actionId = procedure.ActionId }, new { id, procedure.ActionId });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates a RESULT record via the update procedure.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">RESULT identifier to update.</param>
|
||||||
|
/// <param name="procedure">UpdateResultProcedure payload.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>No content on success.</returns>
|
||||||
|
[HttpPut("{id:long}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public async Task<IActionResult> Put([FromRoute] long id, [FromBody] UpdateResultProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
await mediator.ExecuteUpdateProcedure(procedure, id, cancel: cancel);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes RESULT records via the delete procedure for the specified id range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="procedure">DeleteResultProcedure payload (Start, End, Force).</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>No content on success.</returns>
|
||||||
|
[HttpDelete]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public async Task<IActionResult> Delete([FromBody] DeleteResultProcedure procedure, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
await mediator.ExecuteDeleteProcedure(procedure, cancel);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/ReC.API/Middleware/AuthScopedFilter.cs
Normal file
18
src/ReC.API/Middleware/AuthScopedFilter.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using ReC.Application.Common.Interfaces;
|
||||||
|
|
||||||
|
namespace ReC.API.Middleware;
|
||||||
|
|
||||||
|
public class AuthScopedFilter(IConfiguration config) : IAsyncActionFilter
|
||||||
|
{
|
||||||
|
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||||
|
{
|
||||||
|
if (context.ActionArguments.TryGetValue("command", out var command) && command is IAuthScoped authScopedCommand)
|
||||||
|
{
|
||||||
|
var addedWho = config["AddedWho"] ?? throw new InvalidOperationException("The required 'AddedWho' configuration is missing. Please contact a system administrator.");
|
||||||
|
authScopedCommand.Scope.AddedWho = addedWho;
|
||||||
|
}
|
||||||
|
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,10 +63,17 @@ public class ExceptionHandlingMiddleware
|
|||||||
switch (exception)
|
switch (exception)
|
||||||
{
|
{
|
||||||
case BadRequestException badRequestEx:
|
case BadRequestException badRequestEx:
|
||||||
|
if (badRequestEx.InnerException is not null)
|
||||||
|
{
|
||||||
|
logger.LogError(
|
||||||
|
badRequestEx.InnerException,
|
||||||
|
"BadRequestException inner exception captured.");
|
||||||
|
}
|
||||||
|
|
||||||
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||||
details = new()
|
details = new()
|
||||||
{
|
{
|
||||||
Title = "Bad Request",
|
Title = "Bad Procedure",
|
||||||
Detail = badRequestEx.Message
|
Detail = badRequestEx.Message
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
@@ -106,6 +113,57 @@ public class ExceptionHandlingMiddleware
|
|||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case InsertObjectFailedException insertFailedEx:
|
||||||
|
logger.LogError(
|
||||||
|
insertFailedEx,
|
||||||
|
"Insert operation failed during request processing. {procedure}",
|
||||||
|
JsonSerializer.Serialize(
|
||||||
|
insertFailedEx.Procedure,
|
||||||
|
options: new() { WriteIndented = true }
|
||||||
|
));
|
||||||
|
|
||||||
|
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||||
|
details = new()
|
||||||
|
{
|
||||||
|
Title = "Insert Operation Failed",
|
||||||
|
Detail = insertFailedEx.Message
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UpdateObjectFailedException updateFailedEx:
|
||||||
|
logger.LogError(
|
||||||
|
updateFailedEx,
|
||||||
|
"Update operation failed during request processing. {procedure}",
|
||||||
|
JsonSerializer.Serialize(
|
||||||
|
updateFailedEx.Procedure,
|
||||||
|
options: new() { WriteIndented = true }
|
||||||
|
));
|
||||||
|
|
||||||
|
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||||
|
details = new()
|
||||||
|
{
|
||||||
|
Title = "Update Operation Failed",
|
||||||
|
Detail = updateFailedEx.Message
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DeleteObjectFailedException deleteFailedEx:
|
||||||
|
logger.LogError(
|
||||||
|
deleteFailedEx,
|
||||||
|
"Delete operation failed during request processing. {procedure}",
|
||||||
|
JsonSerializer.Serialize(
|
||||||
|
deleteFailedEx.Procedure,
|
||||||
|
options: new() { WriteIndented = true }
|
||||||
|
));
|
||||||
|
|
||||||
|
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||||
|
details = new()
|
||||||
|
{
|
||||||
|
Title = "Delete Operation Failed",
|
||||||
|
Detail = deleteFailedEx.Message
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
logger.LogError(exception, "Unhandled exception occurred.");
|
logger.LogError(exception, "Unhandled exception occurred.");
|
||||||
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||||
@@ -120,4 +178,4 @@ public class ExceptionHandlingMiddleware
|
|||||||
if (details is not null)
|
if (details is not null)
|
||||||
await context.Response.WriteAsJsonAsync(details);
|
await context.Response.WriteAsJsonAsync(details);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
17
src/ReC.API/Models/ResultType.cs
Normal file
17
src/ReC.API/Models/ResultType.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace ReC.API.Models;
|
||||||
|
|
||||||
|
public enum ResultType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Return the full result object.
|
||||||
|
/// </summary>
|
||||||
|
Full,
|
||||||
|
/// <summary>
|
||||||
|
/// Return only the header part of the result.
|
||||||
|
/// </summary>
|
||||||
|
OnlyHeader,
|
||||||
|
/// <summary>
|
||||||
|
/// Return only the body part of the result.
|
||||||
|
/// </summary>
|
||||||
|
OnlyBody
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
|
using Microsoft.AspNetCore.Rewrite;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NLog.Web;
|
using NLog.Web;
|
||||||
using ReC.API.Middleware;
|
using ReC.API.Middleware;
|
||||||
using ReC.Application;
|
using ReC.Application;
|
||||||
using ReC.Infrastructure;
|
using ReC.Infrastructure;
|
||||||
|
using System.Reflection;
|
||||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||||
|
|
||||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
||||||
@@ -23,11 +25,19 @@ try
|
|||||||
|
|
||||||
var config = builder.Configuration;
|
var config = builder.Configuration;
|
||||||
|
|
||||||
|
Directory
|
||||||
|
.GetFiles(builder.Environment.ContentRootPath, "appsettings.*.json", SearchOption.TopDirectoryOnly)
|
||||||
|
.Where(file => Path.GetFileName(file) != $"appsettings.Development.json")
|
||||||
|
.Where(file => Path.GetFileName(file) != $"appsettings.migration.json")
|
||||||
|
.ToList()
|
||||||
|
.ForEach(file => config.AddJsonFile(file, true, true));
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddRecServices(options =>
|
builder.Services.AddRecServices(options =>
|
||||||
{
|
{
|
||||||
options.LuckyPennySoftwareLicenseKey = builder.Configuration["LuckyPennySoftwareLicenseKey"];
|
options.LuckyPennySoftwareLicenseKey = builder.Configuration["LuckyPennySoftwareLicenseKey"];
|
||||||
options.ConfigureRecActions(config.GetSection("RecAction"));
|
options.ConfigureRecActions(config.GetSection("RecAction"));
|
||||||
|
options.ConfigureSqlException(config.GetSection("SqlException"));
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddRecInfrastructure(options =>
|
builder.Services.AddRecInfrastructure(options =>
|
||||||
@@ -45,10 +55,18 @@ try
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers(options =>
|
||||||
|
{
|
||||||
|
options.Filters.Add<AuthScopedFilter>();
|
||||||
|
});
|
||||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddSwaggerGen();
|
builder.Services.AddSwaggerGen(c =>
|
||||||
|
{
|
||||||
|
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
||||||
|
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
|
||||||
|
c.IncludeXmlComments(xmlPath);
|
||||||
|
});
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
@@ -57,10 +75,13 @@ try
|
|||||||
#pragma warning restore CS0618
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment() || config.GetValue<bool>("UseSwagger"))
|
||||||
{
|
{
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
|
|
||||||
|
var rewriteOptions = new RewriteOptions().AddRedirect("^$", "swagger");
|
||||||
|
app.UseRewriter(rewriteOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|||||||
@@ -10,13 +10,15 @@
|
|||||||
<Product>ReC.API</Product>
|
<Product>ReC.API</Product>
|
||||||
<PackageIcon>Assets\icon.ico</PackageIcon>
|
<PackageIcon>Assets\icon.ico</PackageIcon>
|
||||||
<PackageTags>digital data rest-caller rec api</PackageTags>
|
<PackageTags>digital data rest-caller rec api</PackageTags>
|
||||||
<Version>1.0.0-beta</Version>
|
<Version>2.0.0-beta</Version>
|
||||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||||
<FileVersion>1.0.0.0</FileVersion>
|
<FileVersion>2.0.0.0</FileVersion>
|
||||||
<InformationalVersion>1.0.0-beta</InformationalVersion>
|
<InformationalVersion>2.0.0-beta</InformationalVersion>
|
||||||
<Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright>
|
<Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.11" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.11" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||||
|
|||||||
59
src/ReC.API/appsettings.Logging.json
Normal file
59
src/ReC.API/appsettings.Logging.json
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"NLog": {
|
||||||
|
"throwConfigExceptions": true,
|
||||||
|
"variables": {
|
||||||
|
"logDirectory": "E:\\LogFiles\\Digital Data\\Rec.API",
|
||||||
|
"logFileNamePrefix": "${shortdate}.Rec.API"
|
||||||
|
},
|
||||||
|
"targets": {
|
||||||
|
"infoLogs": {
|
||||||
|
"type": "File",
|
||||||
|
"fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log",
|
||||||
|
"maxArchiveDays": 30
|
||||||
|
},
|
||||||
|
"warningLogs": {
|
||||||
|
"type": "File",
|
||||||
|
"fileName": "${logDirectory}\\${logFileNamePrefix}-Warning.log",
|
||||||
|
"maxArchiveDays": 30
|
||||||
|
},
|
||||||
|
"errorLogs": {
|
||||||
|
"type": "File",
|
||||||
|
"fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log",
|
||||||
|
"maxArchiveDays": 30
|
||||||
|
},
|
||||||
|
"criticalLogs": {
|
||||||
|
"type": "File",
|
||||||
|
"fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log",
|
||||||
|
"maxArchiveDays": 30
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"logger": "*",
|
||||||
|
"level": "Info",
|
||||||
|
"writeTo": "infoLogs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"logger": "*",
|
||||||
|
"level": "Warn",
|
||||||
|
"writeTo": "warningLogs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"logger": "*",
|
||||||
|
"level": "Error",
|
||||||
|
"writeTo": "errorLogs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"logger": "*",
|
||||||
|
"level": "Fatal",
|
||||||
|
"writeTo": "criticalLogs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,5 @@
|
|||||||
{
|
{
|
||||||
"Logging": {
|
"UseSwagger": true,
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"Default": "Server=SDD-VMP04-SQL19\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
|
"Default": "Server=SDD-VMP04-SQL19\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
|
||||||
},
|
},
|
||||||
@@ -13,57 +8,11 @@
|
|||||||
"RecAction": {
|
"RecAction": {
|
||||||
"MaxConcurrentInvocations": 5
|
"MaxConcurrentInvocations": 5
|
||||||
},
|
},
|
||||||
|
// Bad request SqlException numbers numbers can be updated at runtime; no restart required.
|
||||||
|
"SqlException": {
|
||||||
|
// https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlexception.number
|
||||||
|
"BadRequestSqlExceptionNumbers": [ 515, 547, 2601, 2627, 50000 ]
|
||||||
|
},
|
||||||
"AddedWho": "ReC.API",
|
"AddedWho": "ReC.API",
|
||||||
"FakeProfileId": 2,
|
"FakeProfileId": 2
|
||||||
"NLog": {
|
|
||||||
"throwConfigExceptions": true,
|
|
||||||
"variables": {
|
|
||||||
"logDirectory": "E:\\LogFiles\\Digital Data\\Rec.API",
|
|
||||||
"logFileNamePrefix": "${shortdate}.Rec.API"
|
|
||||||
},
|
|
||||||
"targets": {
|
|
||||||
"infoLogs": {
|
|
||||||
"type": "File",
|
|
||||||
"fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log",
|
|
||||||
"maxArchiveDays": 30
|
|
||||||
},
|
|
||||||
"warningLogs": {
|
|
||||||
"type": "File",
|
|
||||||
"fileName": "${logDirectory}\\${logFileNamePrefix}-Warning.log",
|
|
||||||
"maxArchiveDays": 30
|
|
||||||
},
|
|
||||||
"errorLogs": {
|
|
||||||
"type": "File",
|
|
||||||
"fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log",
|
|
||||||
"maxArchiveDays": 30
|
|
||||||
},
|
|
||||||
"criticalLogs": {
|
|
||||||
"type": "File",
|
|
||||||
"fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log",
|
|
||||||
"maxArchiveDays": 30
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rules": [
|
|
||||||
{
|
|
||||||
"logger": "*",
|
|
||||||
"level": "Info",
|
|
||||||
"writeTo": "infoLogs"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"logger": "*",
|
|
||||||
"level": "Warn",
|
|
||||||
"writeTo": "warningLogs"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"logger": "*",
|
|
||||||
"level": "Error",
|
|
||||||
"writeTo": "errorLogs"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"logger": "*",
|
|
||||||
"level": "Fatal",
|
|
||||||
"writeTo": "criticalLogs"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
namespace ReC.Application.Common.Behaviors;
|
namespace ReC.Application.Common.Behaviors;
|
||||||
|
|
||||||
public class BodyQueryBehavior<TRequest, TResponse>(IRecDbContext dbContext) : IPipelineBehavior<TRequest, TResponse>
|
public class BodyQueryBehavior<TRequest, TResponse>(IRecDbContext dbContext) : IPipelineBehavior<TRequest, TResponse>
|
||||||
where TRequest : RecActionDto
|
where TRequest : RecActionViewDto
|
||||||
where TResponse : notnull
|
where TResponse : notnull
|
||||||
{
|
{
|
||||||
public async Task<TResponse> Handle(TRequest action, RequestHandlerDelegate<TResponse> next, CancellationToken cancel)
|
public async Task<TResponse> Handle(TRequest action, RequestHandlerDelegate<TResponse> next, CancellationToken cancel)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using System.Text.Json;
|
|||||||
namespace ReC.Application.Common.Behaviors;
|
namespace ReC.Application.Common.Behaviors;
|
||||||
|
|
||||||
public class HeaderQueryBehavior<TRequest, TResponse>(IRecDbContext dbContext, ILogger<HeaderQueryBehavior<TRequest, TResponse>>? logger = null) : IPipelineBehavior<TRequest, TResponse>
|
public class HeaderQueryBehavior<TRequest, TResponse>(IRecDbContext dbContext, ILogger<HeaderQueryBehavior<TRequest, TResponse>>? logger = null) : IPipelineBehavior<TRequest, TResponse>
|
||||||
where TRequest : RecActionDto
|
where TRequest : RecActionViewDto
|
||||||
where TResponse : notnull
|
where TResponse : notnull
|
||||||
{
|
{
|
||||||
public async Task<TResponse> Handle(TRequest action, RequestHandlerDelegate<TResponse> next, CancellationToken cancel)
|
public async Task<TResponse> Handle(TRequest action, RequestHandlerDelegate<TResponse> next, CancellationToken cancel)
|
||||||
|
|||||||
@@ -1,30 +1,29 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace ReC.Application.Common.Behaviors
|
namespace ReC.Application.Common.Behaviors;
|
||||||
|
|
||||||
|
public class ValidationBehavior<TRequest, TResponse>(IEnumerable<IValidator<TRequest>> validators) : IPipelineBehavior<TRequest, TResponse>
|
||||||
|
where TRequest : notnull
|
||||||
{
|
{
|
||||||
public class ValidationBehavior<TRequest, TResponse>(IEnumerable<IValidator<TRequest>> validators) : IPipelineBehavior<TRequest, TResponse>
|
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancel)
|
||||||
where TRequest : notnull
|
|
||||||
{
|
{
|
||||||
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancel)
|
if (validators.Any())
|
||||||
{
|
{
|
||||||
if (validators.Any())
|
var context = new ValidationContext<TRequest>(request);
|
||||||
{
|
|
||||||
var context = new ValidationContext<TRequest>(request);
|
|
||||||
|
|
||||||
var validationResults = await Task.WhenAll(
|
var validationResults = await Task.WhenAll(
|
||||||
validators.Select(v =>
|
validators.Select(v =>
|
||||||
v.ValidateAsync(context, cancel)));
|
v.ValidateAsync(context, cancel)));
|
||||||
|
|
||||||
var failures = validationResults
|
var failures = validationResults
|
||||||
.SelectMany(r => r.Errors)
|
.SelectMany(r => r.Errors)
|
||||||
.Where(f => f != null)
|
.Where(f => f != null)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
if (failures.Count != 0)
|
if (failures.Count != 0)
|
||||||
throw new ValidationException(failures);
|
throw new ValidationException(failures);
|
||||||
}
|
|
||||||
return await next(cancel);
|
|
||||||
}
|
}
|
||||||
|
return await next(cancel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
6
src/ReC.Application/Common/Constants/Http.cs
Normal file
6
src/ReC.Application/Common/Constants/Http.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace ReC.Application.Common.Constants;
|
||||||
|
|
||||||
|
public static class Http
|
||||||
|
{
|
||||||
|
public static readonly string ClientName = "HttpClient-" + Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
32
src/ReC.Application/Common/Dto/ConnectionDto.cs
Normal file
32
src/ReC.Application/Common/Dto/ConnectionDto.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
namespace ReC.Application.Common.Dto;
|
||||||
|
|
||||||
|
public record ConnectionDto
|
||||||
|
{
|
||||||
|
public short? Id { get; set; }
|
||||||
|
|
||||||
|
public string? Bezeichnung { get; set; }
|
||||||
|
|
||||||
|
public string? SqlProvider { get; set; }
|
||||||
|
|
||||||
|
public string? Server { get; set; }
|
||||||
|
|
||||||
|
public string? Datenbank { get; set; }
|
||||||
|
|
||||||
|
public string? Username { get; set; }
|
||||||
|
|
||||||
|
public string? Password { get; set; }
|
||||||
|
|
||||||
|
public string? Bemerkung { get; set; }
|
||||||
|
|
||||||
|
public bool? Aktiv { get; set; }
|
||||||
|
|
||||||
|
public string? ErstelltWer { get; set; }
|
||||||
|
|
||||||
|
public DateTime? ErstelltWann { get; set; }
|
||||||
|
|
||||||
|
public string? GeandertWer { get; set; }
|
||||||
|
|
||||||
|
public DateTime? GeaendertWann { get; set; }
|
||||||
|
|
||||||
|
public bool? SysConnection { get; set; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using ReC.Domain.Entities;
|
using ReC.Domain.Views;
|
||||||
|
|
||||||
namespace ReC.Application.Common.Dto;
|
namespace ReC.Application.Common.Dto;
|
||||||
|
|
||||||
@@ -6,7 +6,8 @@ public class DtoMappingProfile : AutoMapper.Profile
|
|||||||
{
|
{
|
||||||
public DtoMappingProfile()
|
public DtoMappingProfile()
|
||||||
{
|
{
|
||||||
CreateMap<RecActionView, RecActionDto>();
|
CreateMap<RecActionView, RecActionViewDto>();
|
||||||
CreateMap<OutRes, OutResDto>();
|
CreateMap<ResultView, ResultViewDto>();
|
||||||
|
CreateMap<ProfileView, ProfileViewDto>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
src/ReC.Application/Common/Dto/EndpointAuthDto.cs
Normal file
39
src/ReC.Application/Common/Dto/EndpointAuthDto.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using ReC.Domain.Constants;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Dto;
|
||||||
|
|
||||||
|
public record EndpointAuthDto
|
||||||
|
{
|
||||||
|
public long? Id { get; set; }
|
||||||
|
|
||||||
|
public bool? Active { get; set; }
|
||||||
|
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
public EndpointAuthType? Type { get; set; }
|
||||||
|
|
||||||
|
public string? ApiKey { get; set; }
|
||||||
|
|
||||||
|
public string? ApiValue { get; set; }
|
||||||
|
|
||||||
|
public ApiKeyLocation? ApiKeyAddTo { get; set; }
|
||||||
|
|
||||||
|
public string? Token { get; set; }
|
||||||
|
|
||||||
|
public string? Username { get; set; }
|
||||||
|
|
||||||
|
public string? Password { get; set; }
|
||||||
|
|
||||||
|
public string? Domain { get; set; }
|
||||||
|
|
||||||
|
public string? Workstation { get; set; }
|
||||||
|
|
||||||
|
public string? AddedWho { get; set; }
|
||||||
|
|
||||||
|
public DateTime? AddedWhen { get; set; }
|
||||||
|
|
||||||
|
public string? ChangedWho { get; set; }
|
||||||
|
|
||||||
|
public DateTime? ChangedWhen { get; set; }
|
||||||
|
}
|
||||||
22
src/ReC.Application/Common/Dto/EndpointDto.cs
Normal file
22
src/ReC.Application/Common/Dto/EndpointDto.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Dto;
|
||||||
|
|
||||||
|
public record EndpointDto
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
public bool? Active { get; set; }
|
||||||
|
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
public string? Uri { get; set; }
|
||||||
|
|
||||||
|
public string? AddedWho { get; set; }
|
||||||
|
|
||||||
|
public DateTime? AddedWhen { get; set; }
|
||||||
|
|
||||||
|
public string? ChangedWho { get; set; }
|
||||||
|
|
||||||
|
public DateTime? ChangedWhen { get; set; }
|
||||||
|
}
|
||||||
@@ -1,47 +1,33 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace ReC.Domain.Entities;
|
namespace ReC.Application.Common.Dto;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the TBREC_CFG_ENDPOINT_PARAMS table.
|
/// Represents the TBREC_CFG_ENDPOINT_PARAMS table.
|
||||||
/// All properties are nullable to provide flexibility on the database side,
|
/// All properties are nullable to provide flexibility on the database side,
|
||||||
/// preventing breaking changes if columns are altered to be nullable in production.
|
/// preventing breaking changes if columns are altered to be nullable in production.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Table("TBREC_CFG_ENDPOINT_PARAMS", Schema = "dbo")]
|
public record EndpointParamDto
|
||||||
public class EndpointParam
|
|
||||||
{
|
{
|
||||||
[Key]
|
|
||||||
[Column("GUID")]
|
|
||||||
public long? Id { get; set; }
|
public long? Id { get; set; }
|
||||||
|
|
||||||
[Column("ACTIVE")]
|
|
||||||
public bool? Active { get; set; }
|
public bool? Active { get; set; }
|
||||||
|
|
||||||
[Column("DESCRIPTION")]
|
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
|
|
||||||
[Column("GROUP_ID")]
|
|
||||||
public short? GroupId { get; set; }
|
public short? GroupId { get; set; }
|
||||||
|
|
||||||
[Column("SEQUENCE")]
|
|
||||||
public byte? Sequence { get; set; }
|
public byte? Sequence { get; set; }
|
||||||
|
|
||||||
[Column("KEY")]
|
|
||||||
public string? Key { get; set; }
|
public string? Key { get; set; }
|
||||||
|
|
||||||
[Column("VALUE")]
|
|
||||||
public string? Value { get; set; }
|
public string? Value { get; set; }
|
||||||
|
|
||||||
[Column("ADDED_WHO")]
|
|
||||||
public string? AddedWho { get; set; }
|
public string? AddedWho { get; set; }
|
||||||
|
|
||||||
[Column("ADDED_WHEN")]
|
|
||||||
public DateTime? AddedWhen { get; set; }
|
public DateTime? AddedWhen { get; set; }
|
||||||
|
|
||||||
[Column("CHANGED_WHO")]
|
|
||||||
public string? ChangedWho { get; set; }
|
public string? ChangedWho { get; set; }
|
||||||
|
|
||||||
[Column("CHANGED_WHEN")]
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
public DateTime? ChangedWhen { get; set; }
|
||||||
}
|
}
|
||||||
30
src/ReC.Application/Common/Dto/ProfileDto.cs
Normal file
30
src/ReC.Application/Common/Dto/ProfileDto.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Dto;
|
||||||
|
|
||||||
|
public record ProfileDto
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
public bool? Active { get; set; }
|
||||||
|
|
||||||
|
public string? Type { get; set; }
|
||||||
|
|
||||||
|
public string? Mandantor { get; set; }
|
||||||
|
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
public string? LogLevel { get; set; }
|
||||||
|
|
||||||
|
public string? Language { get; set; }
|
||||||
|
|
||||||
|
public string? AddedWho { get; set; }
|
||||||
|
|
||||||
|
public DateTime? AddedWhen { get; set; }
|
||||||
|
|
||||||
|
public string? ChangedWho { get; set; }
|
||||||
|
|
||||||
|
public DateTime? ChangedWhen { get; set; }
|
||||||
|
}
|
||||||
42
src/ReC.Application/Common/Dto/ProfileViewDto.cs
Normal file
42
src/ReC.Application/Common/Dto/ProfileViewDto.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
namespace ReC.Application.Common.Dto;
|
||||||
|
|
||||||
|
public class ProfileViewDto
|
||||||
|
{
|
||||||
|
public long Id { get; init; }
|
||||||
|
|
||||||
|
public IEnumerable<RecActionViewDto>? RecActions { get; set; }
|
||||||
|
|
||||||
|
public bool Active { get; init; }
|
||||||
|
|
||||||
|
public byte TypeId { get; init; }
|
||||||
|
|
||||||
|
public string? Type { get; init; }
|
||||||
|
|
||||||
|
public string? Mandantor { get; init; }
|
||||||
|
|
||||||
|
public string? ProfileName { get; init; }
|
||||||
|
|
||||||
|
public string? Description { get; init; }
|
||||||
|
|
||||||
|
public byte LogLevelId { get; init; }
|
||||||
|
|
||||||
|
public string? LogLevel { get; init; }
|
||||||
|
|
||||||
|
public short LanguageId { get; init; }
|
||||||
|
|
||||||
|
public string? Language { get; init; }
|
||||||
|
|
||||||
|
public string? AddedWho { get; init; }
|
||||||
|
|
||||||
|
public DateTime AddedWhen { get; init; }
|
||||||
|
|
||||||
|
public string? ChangedWho { get; init; }
|
||||||
|
|
||||||
|
public DateTime? ChangedWhen { get; init; }
|
||||||
|
|
||||||
|
public DateTime? FirstRun { get; init; }
|
||||||
|
|
||||||
|
public DateTime? LastRun { get; init; }
|
||||||
|
|
||||||
|
public string? LastResult { get; init; }
|
||||||
|
}
|
||||||
@@ -1,76 +1,53 @@
|
|||||||
namespace ReC.Application.Common.Dto;
|
using ReC.Domain.Constants;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Dto;
|
||||||
|
|
||||||
public record RecActionDto
|
public record RecActionDto
|
||||||
{
|
{
|
||||||
public required long Id { get; init; }
|
public long? Id { get; set; }
|
||||||
|
|
||||||
public long? ProfileId { get; init; }
|
public long? ProfileId { get; set; }
|
||||||
|
|
||||||
public string? ProfileName { get; init; }
|
public ProfileDto? Profile { get; set; }
|
||||||
|
|
||||||
public string? ProfileType { get; init; }
|
public bool? Active { get; set; }
|
||||||
|
|
||||||
public byte? ProfileSequence { get; init; }
|
public byte? Sequence { get; set; }
|
||||||
|
|
||||||
public long? EndpointId { get; init; }
|
public long? EndpointId { get; set; }
|
||||||
|
|
||||||
public string? EndpointUri { get; init; }
|
public EndpointDto? Endpoint { get; set; }
|
||||||
|
|
||||||
public long? EndpointAuthId { get; init; }
|
public long? EndpointAuthId { get; set; }
|
||||||
|
|
||||||
public string? EndpointAuthType { get; init; }
|
public EndpointAuthDto? EndpointAuth { get; set; }
|
||||||
|
|
||||||
public string? EndpointAuthApiKey { get; init; }
|
public short? EndpointParamsId { get; set; }
|
||||||
|
|
||||||
public string? EndpointAuthApiValue { get; init; }
|
public short? SqlConnectionId { get; set; }
|
||||||
|
|
||||||
public string? EndpointAuthApiKeyAddTo { get; init; }
|
public ConnectionDto? SqlConnection { get; set; }
|
||||||
|
|
||||||
public string? EndpointAuthToken { get; init; }
|
public string? Type { get; set; }
|
||||||
|
|
||||||
public string? EndpointAuthUsername { get; init; }
|
public string? PreprocessingQuery { get; set; }
|
||||||
|
|
||||||
public string? EndpointAuthPassword { get; init; }
|
public string? HeaderQuery { get; set; }
|
||||||
|
|
||||||
public string? EndpointAuthDomain { get; init; }
|
public string? BodyQuery { get; set; }
|
||||||
|
|
||||||
public string? EndpointAuthWorkstation { get; init; }
|
public string? PostprocessingQuery { get; set; }
|
||||||
|
|
||||||
public short? EndpointParamsId { get; init; }
|
public ErrorAction? ErrorAction { get; set; }
|
||||||
|
|
||||||
public short? SqlConnectionId { get; init; }
|
public string? AddedWho { get; set; }
|
||||||
|
|
||||||
public string? SqlConnectionServer { get; init; }
|
public DateTime? AddedWhen { get; set; }
|
||||||
|
|
||||||
public string? SqlConnectionDb { get; init; }
|
public string? ChangedWho { get; set; }
|
||||||
|
|
||||||
public string? SqlConnectionUsername { get; init; }
|
public DateTime? ChangedWhen { get; set; }
|
||||||
|
|
||||||
public string? SqlConnectionPassword { get; init; }
|
public OutResDto? OutRes { get; set; }
|
||||||
|
|
||||||
public string? RestType { get; init; }
|
|
||||||
|
|
||||||
public string? PreprocessingQuery { get; init; }
|
|
||||||
|
|
||||||
public string? HeaderQuery { get; init; }
|
|
||||||
|
|
||||||
public Dictionary<string, string>? Headers { get; set; }
|
|
||||||
|
|
||||||
public string? BodyQuery { get; init; }
|
|
||||||
|
|
||||||
public string? Body { get; set; }
|
|
||||||
|
|
||||||
public string? PostprocessingQuery { get; init; }
|
|
||||||
|
|
||||||
public UriBuilder ToEndpointUriBuilder()
|
|
||||||
{
|
|
||||||
var builder = EndpointUri is null ? new UriBuilder() : new UriBuilder(EndpointUri);
|
|
||||||
|
|
||||||
builder.Port = -1;
|
|
||||||
|
|
||||||
if (ProfileType is not null)
|
|
||||||
builder.Scheme = ProfileType;
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
88
src/ReC.Application/Common/Dto/RecActionViewDto.cs
Normal file
88
src/ReC.Application/Common/Dto/RecActionViewDto.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
using ReC.Domain.Constants;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Dto;
|
||||||
|
|
||||||
|
public record RecActionViewDto
|
||||||
|
{
|
||||||
|
public required long Id { get; init; }
|
||||||
|
|
||||||
|
public long? ProfileId { get; init; }
|
||||||
|
|
||||||
|
public string? ProfileName { get; init; }
|
||||||
|
|
||||||
|
public ProfileType? ProfileType { get; init; }
|
||||||
|
|
||||||
|
public byte? Sequence { get; init; }
|
||||||
|
|
||||||
|
public long? EndpointId { get; init; }
|
||||||
|
|
||||||
|
public string? EndpointUri { get; init; }
|
||||||
|
|
||||||
|
public long? EndpointAuthId { get; init; }
|
||||||
|
|
||||||
|
public EndpointAuthType EndpointAuthType { get; init; } = EndpointAuthType.NoAuth;
|
||||||
|
|
||||||
|
public string? EndpointAuthTypeName { get; init; }
|
||||||
|
|
||||||
|
public string? EndpointAuthApiKey { get; init; }
|
||||||
|
|
||||||
|
public string? EndpointAuthApiValue { get; init; }
|
||||||
|
|
||||||
|
public ApiKeyLocation? EndpointAuthApiKeyAddTo { get; init; }
|
||||||
|
|
||||||
|
public string? EndpointAuthApiKeyAddToName { get; init; }
|
||||||
|
|
||||||
|
public string? EndpointAuthToken { get; init; }
|
||||||
|
|
||||||
|
public string? EndpointAuthUsername { get; init; }
|
||||||
|
|
||||||
|
public string? EndpointAuthPassword { get; init; }
|
||||||
|
|
||||||
|
public string? EndpointAuthDomain { get; init; }
|
||||||
|
|
||||||
|
public string? EndpointAuthWorkstation { get; init; }
|
||||||
|
|
||||||
|
public short? EndpointParamsId { get; init; }
|
||||||
|
|
||||||
|
public short? SqlConnectionId { get; init; }
|
||||||
|
|
||||||
|
public string? SqlConnectionServer { get; init; }
|
||||||
|
|
||||||
|
public string? SqlConnectionDb { get; init; }
|
||||||
|
|
||||||
|
public string? SqlConnectionUsername { get; init; }
|
||||||
|
|
||||||
|
public string? SqlConnectionPassword { get; init; }
|
||||||
|
|
||||||
|
public RestType? RestType { get; init; }
|
||||||
|
|
||||||
|
public string? RestTypeName { get; init; }
|
||||||
|
|
||||||
|
public string? PreprocessingQuery { get; init; }
|
||||||
|
|
||||||
|
public string? HeaderQuery { get; init; }
|
||||||
|
|
||||||
|
public Dictionary<string, string>? Headers { get; set; }
|
||||||
|
|
||||||
|
public string? BodyQuery { get; init; }
|
||||||
|
|
||||||
|
public string? Body { get; set; }
|
||||||
|
|
||||||
|
public string? PostprocessingQuery { get; init; }
|
||||||
|
|
||||||
|
public ErrorAction? ErrorAction { get; init; }
|
||||||
|
|
||||||
|
public string? ErrorActionName { get; init; }
|
||||||
|
|
||||||
|
public UriBuilder ToEndpointUriBuilder()
|
||||||
|
{
|
||||||
|
var builder = EndpointUri is null ? new UriBuilder() : new UriBuilder(EndpointUri);
|
||||||
|
|
||||||
|
builder.Port = -1;
|
||||||
|
|
||||||
|
if (ProfileType is ProfileType type)
|
||||||
|
builder.Scheme = type.ToUriBuilderScheme();
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/ReC.Application/Common/Dto/ResultViewDto.cs
Normal file
34
src/ReC.Application/Common/Dto/ResultViewDto.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
namespace ReC.Application.Common.Dto;
|
||||||
|
|
||||||
|
public record ResultViewDto
|
||||||
|
{
|
||||||
|
public long Id { get; init; }
|
||||||
|
|
||||||
|
public OutResDto? Root { get; init; }
|
||||||
|
|
||||||
|
public long? ActionId { get; init; }
|
||||||
|
|
||||||
|
public RecActionViewDto? Action { get; init; }
|
||||||
|
|
||||||
|
public long? ProfileId { get; init; }
|
||||||
|
|
||||||
|
public ProfileViewDto? Profile { get; init; }
|
||||||
|
|
||||||
|
public string? ProfileName { get; init; }
|
||||||
|
|
||||||
|
public short? StatusCode { get; init; }
|
||||||
|
|
||||||
|
public string? StatusName { get; init; }
|
||||||
|
|
||||||
|
public string? Header { get; init; }
|
||||||
|
|
||||||
|
public string? Body { get; init; }
|
||||||
|
|
||||||
|
public string? AddedWho { get; init; }
|
||||||
|
|
||||||
|
public DateTime? AddedWhen { get; init; }
|
||||||
|
|
||||||
|
public string? ChangedWho { get; init; }
|
||||||
|
|
||||||
|
public DateTime? ChangedWhen { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Exceptions;
|
||||||
|
|
||||||
|
public class DeleteObjectFailedException : Exception
|
||||||
|
{
|
||||||
|
public DeleteObjectProcedure Procedure { get; }
|
||||||
|
|
||||||
|
public DeleteObjectFailedException(DeleteObjectProcedure procedure) : base()
|
||||||
|
{
|
||||||
|
Procedure = procedure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeleteObjectFailedException(DeleteObjectProcedure procedure, string? message) : base(message)
|
||||||
|
{
|
||||||
|
Procedure = procedure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeleteObjectFailedException(DeleteObjectProcedure procedure, string? message, Exception? innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
Procedure = procedure;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Exceptions;
|
||||||
|
|
||||||
|
public class InsertObjectFailedException : Exception
|
||||||
|
{
|
||||||
|
public InsertObjectProcedure Procedure { get; }
|
||||||
|
|
||||||
|
public InsertObjectFailedException(InsertObjectProcedure procedure) : base()
|
||||||
|
{
|
||||||
|
Procedure = procedure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InsertObjectFailedException(InsertObjectProcedure procedure, string? message) : base(message)
|
||||||
|
{
|
||||||
|
Procedure = procedure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InsertObjectFailedException(InsertObjectProcedure procedure, string? message, Exception? innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
Procedure = procedure;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Exceptions;
|
||||||
|
|
||||||
|
public class UpdateObjectFailedException : Exception
|
||||||
|
{
|
||||||
|
public UpdateObjectProcedure Procedure { get; }
|
||||||
|
|
||||||
|
public UpdateObjectFailedException(UpdateObjectProcedure procedure) : base()
|
||||||
|
{
|
||||||
|
Procedure = procedure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UpdateObjectFailedException(UpdateObjectProcedure procedure, string? message) : base(message)
|
||||||
|
{
|
||||||
|
Procedure = procedure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UpdateObjectFailedException(UpdateObjectProcedure procedure, string? message, Exception? innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
Procedure = procedure;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +1,28 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using ReC.Domain.Constants;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace ReC.Application.Common;
|
namespace ReC.Application.Common;
|
||||||
|
|
||||||
public static class HttpExtensions
|
public static class HttpExtensions
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, HttpMethod> _methods = new(StringComparer.OrdinalIgnoreCase)
|
private static readonly Dictionary<RestType, HttpMethod> _methods = new()
|
||||||
{
|
{
|
||||||
["GET"] = HttpMethod.Get,
|
[RestType.Get] = HttpMethod.Get,
|
||||||
["POST"] = HttpMethod.Post,
|
[RestType.Post] = HttpMethod.Post,
|
||||||
["PUT"] = HttpMethod.Put,
|
[RestType.Put] = HttpMethod.Put,
|
||||||
["DELETE"] = HttpMethod.Delete,
|
[RestType.Delete] = HttpMethod.Delete,
|
||||||
["PATCH"] = HttpMethod.Patch,
|
[RestType.Patch] = HttpMethod.Patch,
|
||||||
["HEAD"] = HttpMethod.Head,
|
[RestType.Head] = HttpMethod.Head,
|
||||||
["OPTIONS"] = HttpMethod.Options,
|
[RestType.Options] = HttpMethod.Options,
|
||||||
["TRACE"] = HttpMethod.Trace,
|
[RestType.Trace] = HttpMethod.Trace,
|
||||||
["CONNECT"] = HttpMethod.Connect
|
[RestType.Connect] = HttpMethod.Connect
|
||||||
};
|
};
|
||||||
|
|
||||||
public static HttpMethod ToHttpMethod(this string method) => _methods.TryGetValue(method, out var httpMethod)
|
public static HttpMethod ToHttpMethod(this RestType method) => !method.IsValid()
|
||||||
? httpMethod
|
? throw new ArgumentOutOfRangeException(nameof(method), $"The RestType value '{method}' is not valid.")
|
||||||
: new HttpMethod(method);
|
: _methods.TryGetValue(method, out var httpMethod)
|
||||||
|
? httpMethod
|
||||||
|
: new HttpMethod(method.ToHttpMethodName());
|
||||||
|
|
||||||
public static HttpRequestMessage ToHttpRequestMessage(this HttpMethod method, [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri)
|
public static HttpRequestMessage ToHttpRequestMessage(this HttpMethod method, [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri)
|
||||||
=> new(method, requestUri);
|
=> new(method, requestUri);
|
||||||
|
|||||||
13
src/ReC.Application/Common/Interfaces/IAuthScoped.cs
Normal file
13
src/ReC.Application/Common/Interfaces/IAuthScoped.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Interfaces;
|
||||||
|
|
||||||
|
public record AuthScope
|
||||||
|
{
|
||||||
|
public string? AddedWho { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IAuthScoped : IScoped<AuthScope>
|
||||||
|
{
|
||||||
|
public string? AddedWho => Scope.AddedWho;
|
||||||
|
}
|
||||||
@@ -1,19 +1,24 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using ReC.Domain.Entities;
|
using ReC.Domain.QueryOutput;
|
||||||
|
using ReC.Domain.Views;
|
||||||
|
|
||||||
namespace ReC.Application.Common.Interfaces;
|
namespace ReC.Application.Common.Interfaces;
|
||||||
|
|
||||||
public interface IRecDbContext
|
public interface IRecDbContext
|
||||||
{
|
{
|
||||||
public DbSet<EndpointParam> EndpointParams { get; }
|
#region DbSets
|
||||||
|
public DbSet<RecActionView> RecActionViews { get; set; }
|
||||||
|
|
||||||
public DbSet<RecActionView> Actions { get; }
|
public DbSet<ProfileView> ProfileViews { get; set; }
|
||||||
|
|
||||||
public DbSet<OutRes> OutRes { get; }
|
public DbSet<ResultView> RecResultViews { get; set; }
|
||||||
|
|
||||||
public DbSet<HeaderQueryResult> HeaderQueryResults { get; }
|
public DbSet<HeaderQueryResult> HeaderQueryResults { get; set; }
|
||||||
|
|
||||||
public DbSet<BodyQueryResult> BodyQueryResults { get; }
|
public DbSet<BodyQueryResult> BodyQueryResults { get; set; }
|
||||||
|
|
||||||
|
public DbSet<InsertObjectResult> RecResults { get; set; }
|
||||||
|
#endregion DbSets
|
||||||
|
|
||||||
public Task<int> SaveChangesAsync(CancellationToken cancel = default);
|
public Task<int> SaveChangesAsync(CancellationToken cancel = default);
|
||||||
}
|
}
|
||||||
10
src/ReC.Application/Common/Interfaces/IScoped.cs
Normal file
10
src/ReC.Application/Common/Interfaces/IScoped.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using MediatR;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Interfaces;
|
||||||
|
|
||||||
|
public interface IScoped<TScope> where TScope : notnull
|
||||||
|
{
|
||||||
|
[JsonIgnore]
|
||||||
|
public TScope Scope { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace ReC.Application.Common.Options;
|
||||||
|
|
||||||
|
public class SqlExceptionOptions
|
||||||
|
{
|
||||||
|
public HashSet<int> BadRequestSqlExceptionNumbers { get; set; } = [];
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using ReC.Application.Common.Exceptions;
|
||||||
|
using ReC.Application.Common.Options;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
|
||||||
|
public record DeleteObjectProcedure : IRequest<int>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Target entity: ACTION, ENDPOINT, ENDPOINT_AUTH, ENDPOINT_PARAMS, PROFILE, RESULT
|
||||||
|
/// </summary>
|
||||||
|
public string Entity { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start GUID/ID (inclusive)
|
||||||
|
/// </summary>
|
||||||
|
public long Start { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End GUID/ID (inclusive). If 0, will be set to Start value.
|
||||||
|
/// </summary>
|
||||||
|
public long End { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, delete even if dependent data exists
|
||||||
|
/// </summary>
|
||||||
|
public bool Force { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DeleteObjectProcedureExtensions
|
||||||
|
{
|
||||||
|
public static Task<int> ExecuteDeleteProcedure(this ISender sender, IDeleteProcedure procedure, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
return sender.Send(procedure.ToObjectProcedure(), cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<int> ExecuteDeleteProcedure(this ISender sender, string entity, long start, long end = 0, bool force = false, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
return sender.Send(new DeleteObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = entity,
|
||||||
|
Start = start,
|
||||||
|
End = end,
|
||||||
|
Force = force
|
||||||
|
}, cancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DeleteObjectProcedureHandler(IRepository repo, IOptionsMonitor<SqlExceptionOptions> sqlExOpt) : IRequestHandler<DeleteObjectProcedure, int>
|
||||||
|
{
|
||||||
|
public async Task<int> Handle(DeleteObjectProcedure request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var parameters = new[]
|
||||||
|
{
|
||||||
|
new SqlParameter("@pENTITY", request.Entity ?? (object)DBNull.Value),
|
||||||
|
new SqlParameter("@pSTART", request.Start.ToString()),
|
||||||
|
new SqlParameter("@pEND", request.End.ToString()),
|
||||||
|
new SqlParameter("@pFORCE", (object?)request.Force ?? DBNull.Value)
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await repo.ExecuteQueryRawAsync(
|
||||||
|
"DECLARE @RC SMALLINT = 0; " +
|
||||||
|
"EXEC @RC = [dbo].[PRREC_DELETE_OBJECT] " +
|
||||||
|
"@pENTITY, @pSTART, @pEND, @pFORCE; " +
|
||||||
|
"SELECT @RC;",
|
||||||
|
parameters,
|
||||||
|
cancel);
|
||||||
|
|
||||||
|
// The stored procedure returns 0 on success, error codes > 0 on failure
|
||||||
|
if (result > 0)
|
||||||
|
{
|
||||||
|
throw new DeleteObjectFailedException(request, $"DeleteObject stored procedure failed with error code: {result}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (SqlException ex)
|
||||||
|
{
|
||||||
|
if (sqlExOpt.CurrentValue.BadRequestSqlExceptionNumbers.Contains(ex.Number))
|
||||||
|
throw new BadRequestException(ex.Message, ex);
|
||||||
|
else
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
|
||||||
|
public interface IDeleteProcedure
|
||||||
|
{
|
||||||
|
public DeleteObjectProcedure ToObjectProcedure();
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
|
||||||
|
public interface IInsertProcedure
|
||||||
|
{
|
||||||
|
public InsertObjectProcedure ToObjectProcedure(string? addedWho = null);
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using ReC.Application.Common.Exceptions;
|
||||||
|
using ReC.Application.Common.Options;
|
||||||
|
using ReC.Application.EndpointAuth.Commands;
|
||||||
|
using ReC.Application.EndpointParams.Commands;
|
||||||
|
using ReC.Application.Endpoints.Commands;
|
||||||
|
using ReC.Application.Profile.Commands;
|
||||||
|
using ReC.Application.RecActions.Commands;
|
||||||
|
using ReC.Application.Results.Commands;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
|
||||||
|
public record InsertObjectProcedure : IRequest<long>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Target entity: ACTION, ENDPOINT, ENDPOINT_AUTH, ENDPOINT_PARAMS, PROFILE, RESULT
|
||||||
|
/// </summary>
|
||||||
|
public string Entity { get; set; } = null!;
|
||||||
|
|
||||||
|
internal string? AddedWho { get; private set; }
|
||||||
|
|
||||||
|
public InsertObjectProcedure AddedBy(string? addedWho = null)
|
||||||
|
{
|
||||||
|
AddedWho = addedWho ?? "ReC.API";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InsertActionProcedure Action { get; set; } = new();
|
||||||
|
public InsertEndpointProcedure Endpoint { get; set; } = new();
|
||||||
|
public InsertEndpointAuthProcedure EndpointAuth { get; set; } = new();
|
||||||
|
public InsertProfileProcedure Profile { get; set; } = new();
|
||||||
|
public InsertResultProcedure Result { get; set; } = new();
|
||||||
|
public InsertEndpointParamsProcedure EndpointParams { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InsertObjectProcedureExtensions
|
||||||
|
{
|
||||||
|
public static Task<long> ExecuteInsertProcedure(this ISender sender, IInsertProcedure procedure, string? addedWho = null, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
return sender.Send(procedure.ToObjectProcedure(addedWho ?? "Rec.API"), cancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InsertObjectProcedureHandler(IRepository repo, IOptionsMonitor<SqlExceptionOptions> sqlExOpt) : IRequestHandler<InsertObjectProcedure, long>
|
||||||
|
{
|
||||||
|
public async Task<long> Handle(InsertObjectProcedure request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var parameters = new[]
|
||||||
|
{
|
||||||
|
new SqlParameter("@pENTITY", request.Entity ?? (object)DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pADDED_WHO", (object?)request.AddedWho ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pADDED_WHEN", (object?)DateTime.UtcNow ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pACTION_PROFILE_ID", (object?)request.Action.ProfileId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_ACTIVE", (object?)request.Action.Active ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_SEQUENCE", (object?)request.Action.Sequence ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_ENDPOINT_ID", (object?)request.Action.EndpointId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_ENDPOINT_AUTH_ID", (object?)request.Action.EndpointAuthId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_ENDPOINT_PARAMS_ID", (object?)request.Action.EndpointParamsId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_SQL_CONNECTION_ID", (object?)request.Action.SqlConnectionId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_TYPE_ID", (object?)(byte?)request.Action.TypeId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_PRE_SQL", (object?)request.Action.PreSql ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_HEADER_SQL", (object?)request.Action.HeaderSql ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_BODY_SQL", (object?)request.Action.BodySql ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_POST_SQL", (object?)request.Action.PostSql ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_ERROR_ACTION_ID", (object?)request.Action.ErrorActionId ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pENDPOINT_ACTIVE", (object?)request.Endpoint.Active ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_DESCRIPTION", (object?)request.Endpoint.Description ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_URI", (object?)request.Endpoint.Uri ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_ACTIVE", (object?)request.EndpointAuth.Active ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_DESCRIPTION", (object?)request.EndpointAuth.Description ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_TYPE_ID", (object?)request.EndpointAuth.TypeId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_API_KEY", (object?)request.EndpointAuth.ApiKey ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_API_VALUE", (object?)request.EndpointAuth.ApiValue ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_API_KEY_ADD_TO_ID", (object?)request.EndpointAuth.ApiKeyAddToId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_TOKEN", (object?)request.EndpointAuth.Token ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_USERNAME", (object?)request.EndpointAuth.Username ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_PASSWORD", (object?)request.EndpointAuth.Password ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_DOMAIN", (object?)request.EndpointAuth.Domain ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_WORKSTATION", (object?)request.EndpointAuth.Workstation ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pPROFILE_ACTIVE", (object?)request.Profile.Active ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_TYPE_ID", (object?)request.Profile.TypeId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_MANDANTOR", (object?)request.Profile.Mandantor ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_NAME", (object?)request.Profile.Name ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_DESCRIPTION", (object?)request.Profile.Description ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_LOG_LEVEL_ID", (object?)request.Profile.LogLevelId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_LANGUAGE_ID", (object?)request.Profile.LanguageId ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pRESULT_ACTION_ID", (object?)request.Result.ActionId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pRESULT_STATUS_ID", (object?)request.Result.StatusId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pRESULT_HEADER", (object?)request.Result.Header ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pRESULT_BODY", (object?)request.Result.Body ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pENDPOINT_PARAMS_ACTIVE", (object?)request.EndpointParams.Active ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_PARAMS_DESCRIPTION", (object?)request.EndpointParams.Description ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_PARAMS_GROUP_ID", (object?)request.EndpointParams.GroupId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_PARAMS_SEQUENCE", (object?)request.EndpointParams.Sequence ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_PARAMS_KEY", (object?)request.EndpointParams.Key ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_PARAMS_VALUE", (object?)request.EndpointParams.Value ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter
|
||||||
|
{
|
||||||
|
ParameterName = "@oGUID",
|
||||||
|
SqlDbType = System.Data.SqlDbType.BigInt,
|
||||||
|
Direction = System.Data.ParameterDirection.Output
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await repo.ExecuteQueryRawAsync(
|
||||||
|
"EXEC [dbo].[PRREC_INSERT_OBJECT] " +
|
||||||
|
"@pENTITY, @pADDED_WHO, @pADDED_WHEN, " +
|
||||||
|
"@pACTION_PROFILE_ID, @pACTION_ACTIVE, @pACTION_SEQUENCE, @pACTION_ENDPOINT_ID, @pACTION_ENDPOINT_AUTH_ID, @pACTION_ENDPOINT_PARAMS_ID, @pACTION_SQL_CONNECTION_ID, @pACTION_TYPE_ID, @pACTION_PRE_SQL, @pACTION_HEADER_SQL, @pACTION_BODY_SQL, @pACTION_POST_SQL, @pACTION_ERROR_ACTION_ID, " +
|
||||||
|
"@pENDPOINT_ACTIVE, @pENDPOINT_DESCRIPTION, @pENDPOINT_URI, " +
|
||||||
|
"@pENDPOINT_AUTH_ACTIVE, @pENDPOINT_AUTH_DESCRIPTION, @pENDPOINT_AUTH_TYPE_ID, @pENDPOINT_AUTH_API_KEY, @pENDPOINT_AUTH_API_VALUE, @pENDPOINT_AUTH_API_KEY_ADD_TO_ID, @pENDPOINT_AUTH_TOKEN, @pENDPOINT_AUTH_USERNAME, @pENDPOINT_AUTH_PASSWORD, @pENDPOINT_AUTH_DOMAIN, @pENDPOINT_AUTH_WORKSTATION, " +
|
||||||
|
"@pPROFILE_ACTIVE, @pPROFILE_TYPE_ID, @pPROFILE_MANDANTOR, @pPROFILE_NAME, @pPROFILE_DESCRIPTION, @pPROFILE_LOG_LEVEL_ID, @pPROFILE_LANGUAGE_ID, " +
|
||||||
|
"@pRESULT_ACTION_ID, @pRESULT_STATUS_ID, @pRESULT_HEADER, @pRESULT_BODY, " +
|
||||||
|
"@pENDPOINT_PARAMS_ACTIVE, @pENDPOINT_PARAMS_DESCRIPTION, @pENDPOINT_PARAMS_GROUP_ID, @pENDPOINT_PARAMS_SEQUENCE, @pENDPOINT_PARAMS_KEY, @pENDPOINT_PARAMS_VALUE, " +
|
||||||
|
"@oGUID OUTPUT",
|
||||||
|
parameters,
|
||||||
|
cancel);
|
||||||
|
}
|
||||||
|
catch (SqlException ex)
|
||||||
|
{
|
||||||
|
if (sqlExOpt.CurrentValue.BadRequestSqlExceptionNumbers.Contains(ex.Number))
|
||||||
|
throw new BadRequestException(ex.Message, ex);
|
||||||
|
else
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
var guidParam = parameters.Last();
|
||||||
|
|
||||||
|
if (guidParam.Value != DBNull.Value)
|
||||||
|
if (guidParam.Value is long longValue)
|
||||||
|
return longValue;
|
||||||
|
else if (long.TryParse(guidParam.Value.ToString(), out var guid))
|
||||||
|
return guid;
|
||||||
|
|
||||||
|
throw new InsertObjectFailedException(request, "InsertObject stored procedure did not return a valid identifier.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
|
||||||
|
public interface IUpdateProcedure
|
||||||
|
{
|
||||||
|
public UpdateObjectProcedure ToObjectProcedure(long id, string? changedWho = null);
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using ReC.Application.Common.Exceptions;
|
||||||
|
using ReC.Application.Common.Options;
|
||||||
|
using ReC.Application.EndpointAuth.Commands;
|
||||||
|
using ReC.Application.EndpointParams.Commands;
|
||||||
|
using ReC.Application.Endpoints.Commands;
|
||||||
|
using ReC.Application.Profile.Commands;
|
||||||
|
using ReC.Application.RecActions.Commands;
|
||||||
|
using ReC.Application.Results.Commands;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
|
||||||
|
public record UpdateObjectProcedure : IRequest<int>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Target entity: ACTION, ENDPOINT, ENDPOINT_AUTH, ENDPOINT_PARAMS, PROFILE, RESULT
|
||||||
|
/// </summary>
|
||||||
|
public string Entity { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Target GUID to update (required)
|
||||||
|
/// </summary>
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
internal string? ChangedWho { get; private set; }
|
||||||
|
|
||||||
|
public UpdateObjectProcedure ChangedBy(string? changedWho = null)
|
||||||
|
{
|
||||||
|
ChangedWho = changedWho ?? "ReC.API";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UpdateActionProcedure Action { get; set; } = new();
|
||||||
|
public UpdateEndpointProcedure Endpoint { get; set; } = new();
|
||||||
|
public UpdateEndpointAuthProcedure EndpointAuth { get; set; } = new();
|
||||||
|
public UpdateProfileProcedure Profile { get; set; } = new();
|
||||||
|
public UpdateResultProcedure Result { get; set; } = new();
|
||||||
|
public UpdateEndpointParamsProcedure EndpointParams { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UpdateObjectProcedureExtensions
|
||||||
|
{
|
||||||
|
public static Task<int> ExecuteUpdateProcedure(this ISender sender, IUpdateProcedure procedure, long id, string? changedWho = null, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
return sender.Send(procedure.ToObjectProcedure(id, changedWho ?? "ReC.API"), cancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UpdateObjectProcedureHandler(IRepository repo, IOptionsMonitor<SqlExceptionOptions> sqlExOpt) : IRequestHandler<UpdateObjectProcedure, int>
|
||||||
|
{
|
||||||
|
public async Task<int> Handle(UpdateObjectProcedure request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var parameters = new[]
|
||||||
|
{
|
||||||
|
new SqlParameter("@pENTITY", request.Entity ?? (object)DBNull.Value),
|
||||||
|
new SqlParameter("@pGUID", (object?)request.Id ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pCHANGED_WHO", (object?)request.ChangedWho ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pCHANGED_WHEN", (object?)DateTime.UtcNow ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pACTION_PROFILE_ID", (object?)request.Action.ProfileId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_ACTIVE", (object?)request.Action.Active ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_SEQUENCE", (object?)request.Action.Sequence ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_ENDPOINT_ID", (object?)request.Action.EndpointId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_ENDPOINT_AUTH_ID", (object?)request.Action.EndpointAuthId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_ENDPOINT_PARAMS_ID", (object?)request.Action.EndpointParamsId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_SQL_CONNECTION_ID", (object?)request.Action.SqlConnectionId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_TYPE_ID", (object?)request.Action.TypeId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_PRE_SQL", (object?)request.Action.PreSql ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_HEADER_SQL", (object?)request.Action.HeaderSql ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_BODY_SQL", (object?)request.Action.BodySql ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_POST_SQL", (object?)request.Action.PostSql ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pACTION_ERROR_ACTION_ID", (object?)request.Action.ErrorActionId ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pENDPOINT_ACTIVE", (object?)request.Endpoint.Active ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_DESCRIPTION", (object?)request.Endpoint.Description ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_URI", (object?)request.Endpoint.Uri ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_ACTIVE", (object?)request.EndpointAuth.Active ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_DESCRIPTION", (object?)request.EndpointAuth.Description ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_TYPE_ID", (object?)request.EndpointAuth.TypeId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_API_KEY", (object?)request.EndpointAuth.ApiKey ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_API_VALUE", (object?)request.EndpointAuth.ApiValue ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_API_KEY_ADD_TO_ID", (object?)request.EndpointAuth.ApiKeyAddToId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_TOKEN", (object?)request.EndpointAuth.Token ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_USERNAME", (object?)request.EndpointAuth.Username ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_PASSWORD", (object?)request.EndpointAuth.Password ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_DOMAIN", (object?)request.EndpointAuth.Domain ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_AUTH_WORKSTATION", (object?)request.EndpointAuth.Workstation ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pENDPOINT_PARAMS_ACTIVE", (object?)request.EndpointParams.Active ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_PARAMS_DESCRIPTION", (object?)request.EndpointParams.Description ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_PARAMS_GROUP_ID", (object?)request.EndpointParams.GroupId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_PARAMS_SEQUENCE", (object?)request.EndpointParams.Sequence ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_PARAMS_KEY", (object?)request.EndpointParams.Key ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pENDPOINT_PARAMS_VALUE", (object?)request.EndpointParams.Value ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pPROFILE_ACTIVE", (object?)request.Profile.Active ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_TYPE_ID", (object?)request.Profile.TypeId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_MANDANTOR", (object?)request.Profile.Mandantor ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_NAME", (object?)request.Profile.Name ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_DESCRIPTION", (object?)request.Profile.Description ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_LOG_LEVEL_ID", (object?)request.Profile.LogLevelId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_LANGUAGE_ID", (object?)request.Profile.LanguageId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_FIRST_RUN", (object?)request.Profile.FirstRun ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_LAST_RUN", (object?)request.Profile.LastRun ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pPROFILE_LAST_RESULT", (object?)request.Profile.LastResult ?? DBNull.Value),
|
||||||
|
|
||||||
|
new SqlParameter("@pRESULT_ACTION_ID", (object?)request.Result.ActionId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pRESULT_STATUS_ID", (object?)request.Result.StatusId ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pRESULT_HEADER", (object?)request.Result.Header ?? DBNull.Value),
|
||||||
|
new SqlParameter("@pRESULT_BODY", (object?)request.Result.Body ?? DBNull.Value)
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await repo.ExecuteQueryRawAsync(
|
||||||
|
"DECLARE @RC SMALLINT = 0; " +
|
||||||
|
"EXEC @RC = [dbo].[PRREC_UPDATE_OBJECT] " +
|
||||||
|
"@pENTITY, @pGUID, @pCHANGED_WHO, @pCHANGED_WHEN, " +
|
||||||
|
"@pACTION_PROFILE_ID, @pACTION_ACTIVE, @pACTION_SEQUENCE, @pACTION_ENDPOINT_ID, @pACTION_ENDPOINT_AUTH_ID, @pACTION_ENDPOINT_PARAMS_ID, @pACTION_SQL_CONNECTION_ID, @pACTION_TYPE_ID, @pACTION_PRE_SQL, @pACTION_HEADER_SQL, @pACTION_BODY_SQL, @pACTION_POST_SQL, @pACTION_ERROR_ACTION_ID, " +
|
||||||
|
"@pENDPOINT_ACTIVE, @pENDPOINT_DESCRIPTION, @pENDPOINT_URI, " +
|
||||||
|
"@pENDPOINT_AUTH_ACTIVE, @pENDPOINT_AUTH_DESCRIPTION, @pENDPOINT_AUTH_TYPE_ID, @pENDPOINT_AUTH_API_KEY, @pENDPOINT_AUTH_API_VALUE, @pENDPOINT_AUTH_API_KEY_ADD_TO_ID, @pENDPOINT_AUTH_TOKEN, @pENDPOINT_AUTH_USERNAME, @pENDPOINT_AUTH_PASSWORD, @pENDPOINT_AUTH_DOMAIN, @pENDPOINT_AUTH_WORKSTATION, " +
|
||||||
|
"@pENDPOINT_PARAMS_ACTIVE, @pENDPOINT_PARAMS_DESCRIPTION, @pENDPOINT_PARAMS_GROUP_ID, @pENDPOINT_PARAMS_SEQUENCE, @pENDPOINT_PARAMS_KEY, @pENDPOINT_PARAMS_VALUE, " +
|
||||||
|
"@pPROFILE_ACTIVE, @pPROFILE_TYPE_ID, @pPROFILE_MANDANTOR, @pPROFILE_NAME, @pPROFILE_DESCRIPTION, @pPROFILE_LOG_LEVEL_ID, @pPROFILE_LANGUAGE_ID, @pPROFILE_FIRST_RUN, @pPROFILE_LAST_RUN, @pPROFILE_LAST_RESULT, " +
|
||||||
|
"@pRESULT_ACTION_ID, @pRESULT_STATUS_ID, @pRESULT_HEADER, @pRESULT_BODY; " +
|
||||||
|
"SELECT @RC;",
|
||||||
|
parameters,
|
||||||
|
cancel);
|
||||||
|
|
||||||
|
// The stored procedure returns 0 on success, error codes > 0 on failure
|
||||||
|
if (result > 0)
|
||||||
|
{
|
||||||
|
throw new UpdateObjectFailedException(request, $"UpdateObject stored procedure failed with error code: {result}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (SqlException ex)
|
||||||
|
{
|
||||||
|
if (sqlExOpt.CurrentValue.BadRequestSqlExceptionNumbers.Contains(ex.Number))
|
||||||
|
throw new BadRequestException(ex.Message, ex);
|
||||||
|
else
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using ReC.Application.Common.Interfaces;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Validations;
|
||||||
|
|
||||||
|
public class AuthScopedValidator : AbstractValidator<IAuthScoped>
|
||||||
|
{
|
||||||
|
public AuthScopedValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.AddedWho)
|
||||||
|
.NotEmpty()
|
||||||
|
.WithMessage("The 'AddedWho' field is required. A missing value may indicate an API configuration issue. Please contact your system administrator for assistance.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Validations;
|
||||||
|
|
||||||
|
public class InsertObjectProcedureValidator : AbstractValidator<InsertObjectProcedure>
|
||||||
|
{
|
||||||
|
public InsertObjectProcedureValidator()
|
||||||
|
{
|
||||||
|
// ENTITY must be one of the allowed values
|
||||||
|
RuleFor(x => x.Entity)
|
||||||
|
.NotEmpty()
|
||||||
|
.Must(e => e is "ACTION" or "ENDPOINT" or "ENDPOINT_AUTH" or "ENDPOINT_PARAMS" or "PROFILE" or "RESULT")
|
||||||
|
.WithMessage("ENTITY must be one of: ACTION, ENDPOINT, ENDPOINT_AUTH, ENDPOINT_PARAMS, PROFILE, RESULT.");
|
||||||
|
|
||||||
|
// ACTION validation
|
||||||
|
When(x => x.Entity == "ACTION", () =>
|
||||||
|
{
|
||||||
|
RuleFor(x => x.Action.ProfileId)
|
||||||
|
.NotNull()
|
||||||
|
.WithMessage("ACTION requires ActionProfileId (maps to @pACTION_PROFILE_ID).");
|
||||||
|
|
||||||
|
RuleFor(x => x.Action.EndpointId)
|
||||||
|
.NotNull()
|
||||||
|
.WithMessage("ACTION requires ActionEndpointId (maps to @pACTION_ENDPOINT_ID).");
|
||||||
|
});
|
||||||
|
|
||||||
|
// ENDPOINT validation
|
||||||
|
When(x => x.Entity == "ENDPOINT", () =>
|
||||||
|
{
|
||||||
|
RuleFor(x => x.Endpoint.Uri)
|
||||||
|
.NotEmpty()
|
||||||
|
.WithMessage("ENDPOINT requires EndpointUri (maps to @pENDPOINT_URI).")
|
||||||
|
.MaximumLength(2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
// PROFILE validation
|
||||||
|
When(x => x.Entity == "PROFILE", () =>
|
||||||
|
{
|
||||||
|
RuleFor(x => x.Profile.Name)
|
||||||
|
.NotEmpty()
|
||||||
|
.WithMessage("PROFILE requires ProfileName (maps to @pPROFILE_NAME).")
|
||||||
|
.MaximumLength(50);
|
||||||
|
|
||||||
|
RuleFor(x => x.Profile.Mandantor)
|
||||||
|
.MaximumLength(50)
|
||||||
|
.When(x => x.Profile.Mandantor != null);
|
||||||
|
|
||||||
|
RuleFor(x => x.Profile.Description)
|
||||||
|
.MaximumLength(250)
|
||||||
|
.When(x => x.Profile.Description != null);
|
||||||
|
});
|
||||||
|
|
||||||
|
// RESULT validation
|
||||||
|
When(x => x.Entity == "RESULT", () =>
|
||||||
|
{
|
||||||
|
RuleFor(x => x.Result.ActionId)
|
||||||
|
.NotNull()
|
||||||
|
.WithMessage("RESULT requires ResultActionId (maps to @pRESULT_ACTION_ID).");
|
||||||
|
|
||||||
|
RuleFor(x => x.Result.StatusId)
|
||||||
|
.NotNull()
|
||||||
|
.WithMessage("RESULT requires ResultStatusId (maps to @pRESULT_STATUS_ID).");
|
||||||
|
});
|
||||||
|
|
||||||
|
// ENDPOINT_PARAMS validation
|
||||||
|
When(x => x.Entity == "ENDPOINT_PARAMS", () =>
|
||||||
|
{
|
||||||
|
RuleFor(x => x.EndpointParams.GroupId)
|
||||||
|
.NotNull()
|
||||||
|
.WithMessage("ENDPOINT_PARAMS requires EndpointParamsGroupId (maps to @pENDPOINT_PARAMS_GROUP_ID).");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Simple length guards for some string fields (optional but cheap)
|
||||||
|
RuleFor(x => x.AddedWho)
|
||||||
|
.MaximumLength(50)
|
||||||
|
.When(x => x.AddedWho != null);
|
||||||
|
|
||||||
|
RuleFor(x => x.Endpoint.Description)
|
||||||
|
.MaximumLength(250)
|
||||||
|
.When(x => x.Endpoint.Description != null);
|
||||||
|
|
||||||
|
RuleFor(x => x.EndpointAuth.Description)
|
||||||
|
.MaximumLength(250)
|
||||||
|
.When(x => x.EndpointAuth.Description != null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
using MediatR;
|
using FluentValidation;
|
||||||
|
using MediatR;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using ReC.Application.Common.Behaviors;
|
using ReC.Application.Common.Behaviors;
|
||||||
|
using ReC.Application.Common.Constants;
|
||||||
using ReC.Application.Common.Options;
|
using ReC.Application.Common.Options;
|
||||||
|
using ReC.Application.Common.Procedures;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using FluentValidation;
|
|
||||||
|
|
||||||
namespace ReC.Application;
|
namespace ReC.Application;
|
||||||
|
|
||||||
@@ -35,7 +38,11 @@ public static class DependencyInjection
|
|||||||
cfg.LicenseKey = configOpt.LuckyPennySoftwareLicenseKey;
|
cfg.LicenseKey = configOpt.LuckyPennySoftwareLicenseKey;
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddHttpClient();
|
services.AddHttpClient(Http.ClientName)
|
||||||
|
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
|
||||||
|
{
|
||||||
|
UseDefaultCredentials = false
|
||||||
|
});
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@@ -111,5 +118,23 @@ public static class DependencyInjection
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
#endregion ConfigureRecActions
|
#endregion ConfigureRecActions
|
||||||
|
|
||||||
|
#region ConfigureSqlException
|
||||||
|
public ConfigurationOptions ConfigureSqlException(Action<SqlExceptionOptions> configure)
|
||||||
|
{
|
||||||
|
_configActions.Enqueue(services => services.Configure(configure));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationOptions ConfigureSqlException(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_configActions.Enqueue(services =>
|
||||||
|
{
|
||||||
|
services.Configure<SqlExceptionOptions>(configuration);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
#endregion ConfigureSqlException
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.EndpointAuth.Commands;
|
||||||
|
|
||||||
|
public record DeleteEndpointAuthProcedure : IDeleteProcedure
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Start GUID/ID (inclusive)
|
||||||
|
/// </summary>
|
||||||
|
public long Start { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End GUID/ID (inclusive). If 0, will be set to Start value.
|
||||||
|
/// </summary>
|
||||||
|
public long End { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, delete even if dependent ACTION data exists
|
||||||
|
/// </summary>
|
||||||
|
public bool Force { get; set; }
|
||||||
|
|
||||||
|
public DeleteObjectProcedure ToObjectProcedure()
|
||||||
|
{
|
||||||
|
return new DeleteObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "ENDPOINT_AUTH",
|
||||||
|
Start = Start,
|
||||||
|
End = End,
|
||||||
|
Force = Force
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.EndpointAuth.Commands;
|
||||||
|
|
||||||
|
public record InsertEndpointAuthProcedure : IInsertProcedure
|
||||||
|
{
|
||||||
|
public bool? Active { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public byte? TypeId { get; set; }
|
||||||
|
public string? ApiKey { get; set; }
|
||||||
|
public string? ApiValue { get; set; }
|
||||||
|
public bool? ApiKeyAddToId { get; set; }
|
||||||
|
public string? Token { get; set; }
|
||||||
|
public string? Username { get; set; }
|
||||||
|
public string? Password { get; set; }
|
||||||
|
public string? Domain { get; set; }
|
||||||
|
public string? Workstation { get; set; }
|
||||||
|
|
||||||
|
public InsertObjectProcedure ToObjectProcedure(string? addedWho = null)
|
||||||
|
{
|
||||||
|
return new InsertObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "ENDPOINT_AUTH",
|
||||||
|
EndpointAuth = this
|
||||||
|
}.AddedBy(addedWho);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.EndpointAuth.Commands;
|
||||||
|
|
||||||
|
public record UpdateEndpointAuthProcedure : IUpdateProcedure
|
||||||
|
{
|
||||||
|
public bool? Active { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public byte? TypeId { get; set; }
|
||||||
|
public string? ApiKey { get; set; }
|
||||||
|
public string? ApiValue { get; set; }
|
||||||
|
public bool? ApiKeyAddToId { get; set; }
|
||||||
|
public string? Token { get; set; }
|
||||||
|
public string? Username { get; set; }
|
||||||
|
public string? Password { get; set; }
|
||||||
|
public string? Domain { get; set; }
|
||||||
|
public string? Workstation { get; set; }
|
||||||
|
|
||||||
|
public UpdateObjectProcedure ToObjectProcedure(long id, string? changedWho = null)
|
||||||
|
{
|
||||||
|
return new UpdateObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "ENDPOINT_AUTH",
|
||||||
|
Id = id,
|
||||||
|
EndpointAuth = this
|
||||||
|
}.ChangedBy(changedWho);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.EndpointParams.Commands;
|
||||||
|
|
||||||
|
public record DeleteEndpointParamsProcedure : IDeleteProcedure
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Start GUID/ID (inclusive)
|
||||||
|
/// </summary>
|
||||||
|
public long Start { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End GUID/ID (inclusive). If 0, will be set to Start value.
|
||||||
|
/// </summary>
|
||||||
|
public long End { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, delete even if dependent ACTION data exists
|
||||||
|
/// </summary>
|
||||||
|
public bool Force { get; set; }
|
||||||
|
|
||||||
|
public DeleteObjectProcedure ToObjectProcedure()
|
||||||
|
{
|
||||||
|
return new DeleteObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "ENDPOINT_PARAMS",
|
||||||
|
Start = Start,
|
||||||
|
End = End,
|
||||||
|
Force = Force
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.EndpointParams.Commands;
|
||||||
|
|
||||||
|
public record InsertEndpointParamsProcedure : IInsertProcedure
|
||||||
|
{
|
||||||
|
public bool? Active { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public short? GroupId { get; set; }
|
||||||
|
public byte? Sequence { get; set; }
|
||||||
|
public string? Key { get; set; }
|
||||||
|
public string? Value { get; set; }
|
||||||
|
|
||||||
|
public InsertObjectProcedure ToObjectProcedure(string? addedWho = null)
|
||||||
|
{
|
||||||
|
return new InsertObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "ENDPOINT_PARAMS",
|
||||||
|
EndpointParams = this
|
||||||
|
}.AddedBy(addedWho);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.EndpointParams.Commands;
|
||||||
|
|
||||||
|
public record UpdateEndpointParamsProcedure : IUpdateProcedure
|
||||||
|
{
|
||||||
|
public bool? Active { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public short? GroupId { get; set; }
|
||||||
|
public byte? Sequence { get; set; }
|
||||||
|
public string? Key { get; set; }
|
||||||
|
public string? Value { get; set; }
|
||||||
|
|
||||||
|
public UpdateObjectProcedure ToObjectProcedure(long id, string? changedWho = null)
|
||||||
|
{
|
||||||
|
return new UpdateObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "ENDPOINT_PARAMS",
|
||||||
|
Id = id,
|
||||||
|
EndpointParams = this
|
||||||
|
}.ChangedBy(changedWho);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.Endpoints.Commands;
|
||||||
|
|
||||||
|
public record DeleteEndpointProcedure : IDeleteProcedure
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Start GUID/ID (inclusive)
|
||||||
|
/// </summary>
|
||||||
|
public long Start { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End GUID/ID (inclusive). If 0, will be set to Start value.
|
||||||
|
/// </summary>
|
||||||
|
public long End { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, delete even if dependent ACTION data exists
|
||||||
|
/// </summary>
|
||||||
|
public bool Force { get; set; }
|
||||||
|
|
||||||
|
public DeleteObjectProcedure ToObjectProcedure()
|
||||||
|
{
|
||||||
|
return new DeleteObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "ENDPOINT",
|
||||||
|
Start = Start,
|
||||||
|
End = End,
|
||||||
|
Force = Force
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.Endpoints.Commands;
|
||||||
|
|
||||||
|
public record InsertEndpointProcedure : IInsertProcedure
|
||||||
|
{
|
||||||
|
public bool? Active { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public string? Uri { get; set; }
|
||||||
|
|
||||||
|
public InsertObjectProcedure ToObjectProcedure(string? addedWho = null)
|
||||||
|
{
|
||||||
|
return new InsertObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "ENDPOINT",
|
||||||
|
Endpoint = this
|
||||||
|
}.AddedBy(addedWho);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using ReC.Domain.Entities;
|
|
||||||
|
|
||||||
namespace ReC.Application.Endpoints.Commands;
|
|
||||||
|
|
||||||
public class ObtainEndpointCommand : IRequest<Endpoint>
|
|
||||||
{
|
|
||||||
public string Uri { get; init; } = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ObtainEndpointCommandHandler(IRepository<Endpoint> repo) : IRequestHandler<ObtainEndpointCommand, Endpoint>
|
|
||||||
{
|
|
||||||
public async Task<Endpoint> Handle(ObtainEndpointCommand request, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var endpoint = await repo.Where(e => e.Uri == request.Uri).FirstOrDefaultAsync(cancel);
|
|
||||||
|
|
||||||
if (endpoint is not null)
|
|
||||||
return endpoint;
|
|
||||||
|
|
||||||
endpoint = await repo.CreateAsync(request, cancel);
|
|
||||||
return endpoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.Endpoints.Commands;
|
||||||
|
|
||||||
|
public record UpdateEndpointProcedure : IUpdateProcedure
|
||||||
|
{
|
||||||
|
public bool? Active { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public string? Uri { get; set; }
|
||||||
|
|
||||||
|
public UpdateObjectProcedure ToObjectProcedure(long id, string? changedWho = null)
|
||||||
|
{
|
||||||
|
return new UpdateObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "ENDPOINT",
|
||||||
|
Id = id,
|
||||||
|
Endpoint = this
|
||||||
|
}.ChangedBy(changedWho);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using ReC.Application.Endpoints.Commands;
|
|
||||||
using ReC.Domain.Entities;
|
|
||||||
|
|
||||||
namespace ReC.Application.Endpoints;
|
|
||||||
|
|
||||||
// TODO: update to inject AddedWho from the current host/user contex
|
|
||||||
public class MappingProfile : AutoMapper.Profile
|
|
||||||
{
|
|
||||||
public MappingProfile()
|
|
||||||
{
|
|
||||||
CreateMap<ObtainEndpointCommand, Endpoint>()
|
|
||||||
.ForMember(e => e.Active, exp => exp.MapFrom(cmd => true))
|
|
||||||
.ForMember(e => e.AddedWhen, exp => exp.MapFrom(cmd => DateTime.UtcNow))
|
|
||||||
.ForMember(e => e.AddedWho, exp => exp.MapFrom(cmd => "ReC.API"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using MediatR;
|
|
||||||
using ReC.Domain.Entities;
|
|
||||||
|
|
||||||
namespace ReC.Application.OutResults.Commands;
|
|
||||||
|
|
||||||
public class CreateOutResCommand : IRequest
|
|
||||||
{
|
|
||||||
public required long ActionId { get; set; }
|
|
||||||
|
|
||||||
public string? Header { get; set; }
|
|
||||||
|
|
||||||
public string? Body { get; set; }
|
|
||||||
|
|
||||||
public string? AddedWho { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CreateOutResCommandHandler(IRepository<OutRes> repo) : IRequestHandler<CreateOutResCommand>
|
|
||||||
{
|
|
||||||
public Task Handle(CreateOutResCommand request, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
return repo.CreateAsync(request, cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using ReC.Application.OutResults.Commands;
|
|
||||||
using ReC.Domain.Entities;
|
|
||||||
|
|
||||||
namespace ReC.Application.OutResults;
|
|
||||||
|
|
||||||
// TODO: update to inject AddedWho from the current host/user contex
|
|
||||||
public class MappingProfiles : AutoMapper.Profile
|
|
||||||
{
|
|
||||||
public MappingProfiles()
|
|
||||||
{
|
|
||||||
CreateMap<CreateOutResCommand, OutRes>()
|
|
||||||
.ForMember(e => e.AddedWhen, exp => exp.MapFrom(cmd => DateTime.UtcNow))
|
|
||||||
.ForMember(e => e.AddedWho, exp => exp.MapFrom(cmd => "ReC.API"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using ReC.Application.Common.Dto;
|
|
||||||
using ReC.Domain.Entities;
|
|
||||||
|
|
||||||
namespace ReC.Application.OutResults.Queries;
|
|
||||||
|
|
||||||
public record ReadOutResQuery : IRequest<IEnumerable<OutResDto>>
|
|
||||||
{
|
|
||||||
public long? ProfileId { get; init; }
|
|
||||||
|
|
||||||
public long? ActionId { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ReadOutResHandler(IRepository<OutRes> repo, IMapper mapper) : IRequestHandler<ReadOutResQuery, IEnumerable<OutResDto>>
|
|
||||||
{
|
|
||||||
public async Task<IEnumerable<OutResDto>> Handle(ReadOutResQuery request, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var q = repo.Query;
|
|
||||||
|
|
||||||
if(request.ActionId is long actionId)
|
|
||||||
q = q.Where(res => res.ActionId == actionId);
|
|
||||||
|
|
||||||
if(request.ProfileId is long profileId)
|
|
||||||
q = q.Where(res => res.Action!.ProfileId == profileId);
|
|
||||||
|
|
||||||
var resList = await q.ToListAsync(cancel);
|
|
||||||
|
|
||||||
if (resList.Count == 0)
|
|
||||||
throw new NotFoundException();
|
|
||||||
|
|
||||||
return mapper.Map<IEnumerable<OutResDto>>(resList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using FluentValidation;
|
|
||||||
|
|
||||||
namespace ReC.Application.OutResults.Queries;
|
|
||||||
|
|
||||||
public class ReadOutResQueryValidator : AbstractValidator<ReadOutResQuery>
|
|
||||||
{
|
|
||||||
public ReadOutResQueryValidator()
|
|
||||||
{
|
|
||||||
RuleFor(x => x)
|
|
||||||
.Must(x => x.ActionId.HasValue || x.ProfileId.HasValue)
|
|
||||||
.WithMessage("At least one of ActionId or ProfileId must be provided.")
|
|
||||||
.WithName("Identifier");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.Profile.Commands;
|
||||||
|
|
||||||
|
public record DeleteProfileProcedure : IDeleteProcedure
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Start GUID/ID (inclusive)
|
||||||
|
/// </summary>
|
||||||
|
public long Start { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End GUID/ID (inclusive). If 0, will be set to Start value.
|
||||||
|
/// </summary>
|
||||||
|
public long End { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, delete even if dependent ACTION data exists
|
||||||
|
/// </summary>
|
||||||
|
public bool Force { get; set; }
|
||||||
|
|
||||||
|
public DeleteObjectProcedure ToObjectProcedure()
|
||||||
|
{
|
||||||
|
return new DeleteObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "PROFILE",
|
||||||
|
Start = Start,
|
||||||
|
End = End,
|
||||||
|
Force = Force
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.Profile.Commands;
|
||||||
|
|
||||||
|
public record InsertProfileProcedure : IInsertProcedure
|
||||||
|
{
|
||||||
|
public bool? Active { get; set; }
|
||||||
|
public byte? TypeId { get; set; }
|
||||||
|
public string? Mandantor { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public byte? LogLevelId { get; set; }
|
||||||
|
public short? LanguageId { get; set; }
|
||||||
|
|
||||||
|
public InsertObjectProcedure ToObjectProcedure(string? addedWho = null)
|
||||||
|
{
|
||||||
|
return new InsertObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "PROFILE",
|
||||||
|
Profile = this
|
||||||
|
}.AddedBy(addedWho);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.Profile.Commands;
|
||||||
|
|
||||||
|
public record UpdateProfileProcedure : IUpdateProcedure
|
||||||
|
{
|
||||||
|
public bool? Active { get; set; }
|
||||||
|
public byte? TypeId { get; set; }
|
||||||
|
public string? Mandantor { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public byte? LogLevelId { get; set; }
|
||||||
|
public short? LanguageId { get; set; }
|
||||||
|
public DateTime? FirstRun { get; set; }
|
||||||
|
public DateTime? LastRun { get; set; }
|
||||||
|
public string? LastResult { get; set; }
|
||||||
|
|
||||||
|
public UpdateObjectProcedure ToObjectProcedure(long id, string? changedWho = null)
|
||||||
|
{
|
||||||
|
return new UpdateObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "PROFILE",
|
||||||
|
Id = id,
|
||||||
|
Profile = this
|
||||||
|
}.ChangedBy(changedWho);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/ReC.Application/Profile/Queries/ReadProfileViewQuery.cs
Normal file
36
src/ReC.Application/Profile/Queries/ReadProfileViewQuery.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using ReC.Application.Common.Dto;
|
||||||
|
using ReC.Domain.Views;
|
||||||
|
|
||||||
|
namespace ReC.Application.Profile.Queries;
|
||||||
|
|
||||||
|
public record ReadProfileViewQuery : IRequest<IEnumerable<ProfileViewDto>>
|
||||||
|
{
|
||||||
|
public long? Id { get; init; } = null;
|
||||||
|
|
||||||
|
public bool IncludeActions { get; init; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReadProfileViewQueryHandler(IRepository<ProfileView> repo, IMapper mapper)
|
||||||
|
: IRequestHandler<ReadProfileViewQuery, IEnumerable<ProfileViewDto>>
|
||||||
|
{
|
||||||
|
public async Task<IEnumerable<ProfileViewDto>> Handle(ReadProfileViewQuery request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var query = request.IncludeActions
|
||||||
|
? repo.Query.Include(p => p.Actions)
|
||||||
|
: repo.Query;
|
||||||
|
|
||||||
|
if (request.Id is long id)
|
||||||
|
query = query.Where(p => p.Id == id);
|
||||||
|
|
||||||
|
var profiles = await query.ToListAsync(cancel);
|
||||||
|
|
||||||
|
return profiles is null || profiles.Count == 0
|
||||||
|
? throw new NotFoundException($"Profile {request.Id} not found.")
|
||||||
|
: mapper.Map<IEnumerable<ProfileViewDto>>(profiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,9 +8,9 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper" Version="15.1.0" />
|
<PackageReference Include="AutoMapper" Version="15.1.0" />
|
||||||
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.5.0" />
|
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.6.0" />
|
||||||
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
|
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
|
||||||
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
|
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.1" />
|
||||||
<PackageReference Include="FluentValidation" Version="12.1.0" />
|
<PackageReference Include="FluentValidation" Version="12.1.0" />
|
||||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.0" />
|
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.0" />
|
||||||
<PackageReference Include="MediatR" Version="13.1.0" />
|
<PackageReference Include="MediatR" Version="13.1.0" />
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using MediatR;
|
|
||||||
using ReC.Application.Endpoints.Commands;
|
|
||||||
using ReC.Domain.Entities;
|
|
||||||
|
|
||||||
namespace ReC.Application.RecActions.Commands;
|
|
||||||
|
|
||||||
public record CreateRecActionCommand : IRequest
|
|
||||||
{
|
|
||||||
public long ProfileId { get; init; }
|
|
||||||
|
|
||||||
public bool Active { get; init; } = true;
|
|
||||||
|
|
||||||
public long? EndpointId { get; set; }
|
|
||||||
|
|
||||||
public string? EndpointUri { get; init; }
|
|
||||||
|
|
||||||
public string Type { get; init; } = null!;
|
|
||||||
|
|
||||||
public string? HeaderQuery { get; init; }
|
|
||||||
|
|
||||||
public string BodyQuery { get; init; } = null!;
|
|
||||||
|
|
||||||
public byte Sequence { get; set; } = 1;
|
|
||||||
|
|
||||||
public long? EndpointAuthId { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CreateRecActionCommandHandler(ISender sender, IRepository<RecAction> repo) : IRequestHandler<CreateRecActionCommand>
|
|
||||||
{
|
|
||||||
public async Task Handle(CreateRecActionCommand request, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
if(request.EndpointId is null)
|
|
||||||
if(request.EndpointUri is string endpointUri)
|
|
||||||
{
|
|
||||||
var endpoint = await sender.Send(new ObtainEndpointCommand { Uri = endpointUri }, cancel);
|
|
||||||
request.EndpointId = endpoint.Id;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw new BadRequestException("Either EndpointId or EndpointUri must be provided.");
|
|
||||||
|
|
||||||
await repo.CreateAsync(request, cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.RecActions.Commands;
|
||||||
|
|
||||||
|
public record DeleteActionProcedure : IDeleteProcedure
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Start GUID/ID (inclusive)
|
||||||
|
/// </summary>
|
||||||
|
public long Start { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End GUID/ID (inclusive). If 0, will be set to Start value.
|
||||||
|
/// </summary>
|
||||||
|
public long End { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, delete even if dependent RESULT data exists
|
||||||
|
/// </summary>
|
||||||
|
public bool Force { get; set; }
|
||||||
|
|
||||||
|
public DeleteObjectProcedure ToObjectProcedure()
|
||||||
|
{
|
||||||
|
return new DeleteObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "ACTION",
|
||||||
|
Start = Start,
|
||||||
|
End = End,
|
||||||
|
Force = Force
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using ReC.Domain.Entities;
|
|
||||||
|
|
||||||
namespace ReC.Application.RecActions.Commands;
|
|
||||||
|
|
||||||
public class DeleteRecActionsCommand : IRequest
|
|
||||||
{
|
|
||||||
public required long ProfileId { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DeleteRecActionsCommandHandler(IRepository<RecAction> repo) : IRequestHandler<DeleteRecActionsCommand>
|
|
||||||
{
|
|
||||||
public async Task Handle(DeleteRecActionsCommand request, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
// TODO: update DeleteAsync (in Core) to return number of deleted records
|
|
||||||
if (!await repo.Where(act => act.ProfileId == request.ProfileId).AnyAsync(cancel))
|
|
||||||
throw new NotFoundException();
|
|
||||||
|
|
||||||
await repo.DeleteAsync(act => act.ProfileId == request.ProfileId, cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
using ReC.Domain.Constants;
|
||||||
|
|
||||||
|
namespace ReC.Application.RecActions.Commands;
|
||||||
|
|
||||||
|
public record InsertActionProcedure : IInsertProcedure
|
||||||
|
{
|
||||||
|
public long? ProfileId { get; set; }
|
||||||
|
public bool? Active { get; set; }
|
||||||
|
public byte? Sequence { get; set; }
|
||||||
|
public long? EndpointId { get; set; }
|
||||||
|
public long? EndpointAuthId { get; set; }
|
||||||
|
public short? EndpointParamsId { get; set; }
|
||||||
|
public short? SqlConnectionId { get; set; }
|
||||||
|
public RestType? TypeId { get; set; }
|
||||||
|
public string? PreSql { get; set; }
|
||||||
|
public string? HeaderSql { get; set; }
|
||||||
|
public string? BodySql { get; set; }
|
||||||
|
public string? PostSql { get; set; }
|
||||||
|
public byte? ErrorActionId { get; set; }
|
||||||
|
|
||||||
|
public InsertObjectProcedure ToObjectProcedure(string? addedWho = null)
|
||||||
|
{
|
||||||
|
return new InsertObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "ACTION",
|
||||||
|
Action = this
|
||||||
|
}.AddedBy(addedWho);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using MediatR;
|
||||||
|
using ReC.Application.RecActions.Queries;
|
||||||
|
using ReC.Domain.Constants;
|
||||||
|
|
||||||
|
namespace ReC.Application.RecActions.Commands;
|
||||||
|
|
||||||
|
public record InvokeBatchRecActionViewsCommand : IRequest
|
||||||
|
{
|
||||||
|
public long ProfileId { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InvokeBatchRecActionViewsCommandExtensions
|
||||||
|
{
|
||||||
|
public static Task InvokeBatchRecActionView(this ISender sender, long profileId, CancellationToken cancel = default)
|
||||||
|
=> sender.Send(new InvokeBatchRecActionViewsCommand { ProfileId = profileId }, cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InvokeRecActionViewsCommandHandler(ISender sender) : IRequestHandler<InvokeBatchRecActionViewsCommand>
|
||||||
|
{
|
||||||
|
public async Task Handle(InvokeBatchRecActionViewsCommand request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var actions = await sender.Send(new ReadRecActionViewQuery() { ProfileId = request.ProfileId, Invoked = false }, cancel);
|
||||||
|
|
||||||
|
foreach (var action in actions)
|
||||||
|
{
|
||||||
|
var ok = await sender.Send(action.ToInvokeCommand(), cancel);
|
||||||
|
if (!ok)
|
||||||
|
switch (action.ErrorAction)
|
||||||
|
{
|
||||||
|
case ErrorAction.Continue:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using ReC.Application.RecActions.Queries;
|
|
||||||
|
|
||||||
namespace ReC.Application.RecActions.Commands;
|
|
||||||
|
|
||||||
public record InvokeBatchRecActionsCommand : ReadRecActionQueryBase, IRequest;
|
|
||||||
|
|
||||||
public static class InvokeBatchRecActionsCommandExtensions
|
|
||||||
{
|
|
||||||
public static Task InvokeBatchRecAction(this ISender sender, long profileId, CancellationToken cancel = default)
|
|
||||||
=> sender.Send(new InvokeBatchRecActionsCommand { ProfileId = profileId }, cancel);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class InvokeRecActionsCommandHandler(ISender sender, IServiceScopeFactory scopeFactory, IHttpClientFactory clientFactory, ILogger<InvokeRecActionsCommandHandler>? logger = null) : IRequestHandler<InvokeBatchRecActionsCommand>
|
|
||||||
{
|
|
||||||
public async Task Handle(InvokeBatchRecActionsCommand request, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var actions = await sender.Send(request.ToReadQuery(q => q.Invoked = false), cancel);
|
|
||||||
|
|
||||||
var http = clientFactory.CreateClient();
|
|
||||||
|
|
||||||
using var semaphore = new SemaphoreSlim(5);
|
|
||||||
|
|
||||||
var tasks = actions.Select(async action =>
|
|
||||||
{
|
|
||||||
await semaphore.WaitAsync(cancel);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var scope = scopeFactory.CreateScope();
|
|
||||||
var sender = scope.ServiceProvider.GetRequiredService<ISender>();
|
|
||||||
await sender.Send(action.ToInvokeCommand(), cancel);
|
|
||||||
}
|
|
||||||
catch(Exception ex)
|
|
||||||
{
|
|
||||||
logger?.LogError(
|
|
||||||
ex,
|
|
||||||
"Error invoking Rec action. ProfileId: {ProfileId}, Id: {Id}",
|
|
||||||
action.ProfileId,
|
|
||||||
action.Id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
semaphore.Release();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await Task.WhenAll(tasks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using ReC.Application.Common;
|
|
||||||
using ReC.Application.Common.Dto;
|
|
||||||
using ReC.Application.Common.Exceptions;
|
|
||||||
using ReC.Application.OutResults.Commands;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace ReC.Application.RecActions.Commands;
|
|
||||||
|
|
||||||
public record InvokeRecActionCommand : IRequest
|
|
||||||
{
|
|
||||||
public RecActionDto Action { get; set; } = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InvokeRecActionCommandExtensions
|
|
||||||
{
|
|
||||||
public static InvokeRecActionCommand ToInvokeCommand(this RecActionDto dto) => new() { Action = dto };
|
|
||||||
}
|
|
||||||
|
|
||||||
public class InvokeRecActionCommandHandler(
|
|
||||||
ISender sender,
|
|
||||||
IHttpClientFactory clientFactory,
|
|
||||||
IConfiguration? config = null
|
|
||||||
) : IRequestHandler<InvokeRecActionCommand>
|
|
||||||
{
|
|
||||||
public async Task Handle(InvokeRecActionCommand request, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var action = request.Action;
|
|
||||||
using var http = clientFactory.CreateClient();
|
|
||||||
|
|
||||||
if (action.RestType is null)
|
|
||||||
throw new DataIntegrityException(
|
|
||||||
$"Rec action could not be invoked because the RestType value is null. " +
|
|
||||||
$"ProfileId: {action.ProfileId}, " +
|
|
||||||
$"Id: {action.Id}"
|
|
||||||
);
|
|
||||||
|
|
||||||
using var httpReq = action.RestType
|
|
||||||
.ToHttpMethod()
|
|
||||||
.ToHttpRequestMessage(action.EndpointUri);
|
|
||||||
|
|
||||||
if(action.Body is not null)
|
|
||||||
{
|
|
||||||
using var reqBody = new StringContent(action.Body);
|
|
||||||
httpReq.Content = reqBody;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.Headers is not null)
|
|
||||||
foreach (var header in action.Headers)
|
|
||||||
httpReq.Headers.Add(header.Key, header.Value);
|
|
||||||
|
|
||||||
using var response = await http.SendAsync(httpReq, cancel);
|
|
||||||
var resBody = await response.Content.ReadAsStringAsync(cancel);
|
|
||||||
var resHeaders = response.Headers.ToDictionary();
|
|
||||||
|
|
||||||
await sender.Send(new CreateOutResCommand
|
|
||||||
{
|
|
||||||
ActionId = action.Id,
|
|
||||||
Header = JsonSerializer.Serialize(resHeaders, options: new() { WriteIndented = false }),
|
|
||||||
Body = resBody,
|
|
||||||
AddedWho = config?["AddedWho"]
|
|
||||||
}, cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using ReC.Application.Common;
|
||||||
|
using ReC.Application.Common.Constants;
|
||||||
|
using ReC.Application.Common.Dto;
|
||||||
|
using ReC.Application.Common.Exceptions;
|
||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
using ReC.Application.Results.Commands;
|
||||||
|
using ReC.Domain.Constants;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace ReC.Application.RecActions.Commands;
|
||||||
|
|
||||||
|
public record InvokeRecActionViewCommand : IRequest<bool>
|
||||||
|
{
|
||||||
|
public RecActionViewDto Action { get; set; } = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InvokeRecActionViewCommandExtensions
|
||||||
|
{
|
||||||
|
public static InvokeRecActionViewCommand ToInvokeCommand(this RecActionViewDto dto) => new() { Action = dto };
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InvokeRecActionViewCommandHandler(
|
||||||
|
ISender sender,
|
||||||
|
IHttpClientFactory clientFactory,
|
||||||
|
IConfiguration? config = null
|
||||||
|
) : IRequestHandler<InvokeRecActionViewCommand, bool>
|
||||||
|
{
|
||||||
|
public async Task<bool> Handle(InvokeRecActionViewCommand request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var action = request.Action;
|
||||||
|
|
||||||
|
using var http = clientFactory.CreateClient(Http.ClientName);
|
||||||
|
|
||||||
|
if (action.RestType is not RestType restType)
|
||||||
|
throw new DataIntegrityException(
|
||||||
|
$"Rec action could not be invoked because the RestType value is null. " +
|
||||||
|
$"ProfileId: {action.ProfileId}, " +
|
||||||
|
$"Id: {action.Id}"
|
||||||
|
);
|
||||||
|
|
||||||
|
using var httpReq = restType
|
||||||
|
.ToHttpMethod()
|
||||||
|
.ToHttpRequestMessage(action.EndpointUri);
|
||||||
|
|
||||||
|
if (action.Body is not null)
|
||||||
|
{
|
||||||
|
using var reqBody = new StringContent(action.Body);
|
||||||
|
httpReq.Content = reqBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.Headers is not null)
|
||||||
|
foreach (var header in action.Headers)
|
||||||
|
httpReq.Headers.Add(header.Key, header.Value);
|
||||||
|
|
||||||
|
switch (action.EndpointAuthType)
|
||||||
|
{
|
||||||
|
case EndpointAuthType.NoAuth:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EndpointAuthType.ApiKey:
|
||||||
|
if (action.EndpointAuthApiKey is string apiKey && action.EndpointAuthApiValue is string apiValue)
|
||||||
|
{
|
||||||
|
switch (action.EndpointAuthApiKeyAddTo)
|
||||||
|
{
|
||||||
|
case ApiKeyLocation.Header:
|
||||||
|
httpReq.Headers.Add(apiKey, apiValue);
|
||||||
|
break;
|
||||||
|
case ApiKeyLocation.Query:
|
||||||
|
var uriBuilder = new UriBuilder(httpReq.RequestUri!);
|
||||||
|
var query = System.Web.HttpUtility.ParseQueryString(uriBuilder.Query);
|
||||||
|
query[apiKey] = apiValue;
|
||||||
|
uriBuilder.Query = query.ToString();
|
||||||
|
httpReq.RequestUri = uriBuilder.Uri;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new DataIntegrityException(
|
||||||
|
$"The API key location '{action.EndpointAuthApiKeyAddTo}' is not supported. " +
|
||||||
|
$"ProfileId: {action.ProfileId}, " +
|
||||||
|
$"Id: {action.Id}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EndpointAuthType.BearerToken:
|
||||||
|
case EndpointAuthType.JwtBearer:
|
||||||
|
case EndpointAuthType.OAuth2:
|
||||||
|
if (action.EndpointAuthToken is string authToken)
|
||||||
|
httpReq.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EndpointAuthType.BasicAuth:
|
||||||
|
if (action.EndpointAuthUsername is string authUsername && action.EndpointAuthPassword is string authPassword)
|
||||||
|
{
|
||||||
|
var basicAuth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{authUsername}:{authPassword}"));
|
||||||
|
httpReq.Headers.Authorization = new AuthenticationHeaderValue("Basic", basicAuth);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EndpointAuthType.NtlmAuth:
|
||||||
|
if (!string.IsNullOrWhiteSpace(action.EndpointAuthUsername))
|
||||||
|
{
|
||||||
|
var credentials = new NetworkCredential(
|
||||||
|
action.EndpointAuthUsername,
|
||||||
|
action.EndpointAuthPassword,
|
||||||
|
action.EndpointAuthDomain);
|
||||||
|
var credentialCache = new CredentialCache { { httpReq.RequestUri!, "NTLM", credentials } };
|
||||||
|
httpReq.Options.Set(new HttpRequestOptionsKey<CredentialCache>("Credentials"), credentialCache);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EndpointAuthType.DigestAuth:
|
||||||
|
case EndpointAuthType.OAuth1:
|
||||||
|
case EndpointAuthType.AwsSignature:
|
||||||
|
// These authentication methods require more complex implementations,
|
||||||
|
// often involving multi-step handshakes or specialized libraries.
|
||||||
|
// They are left as placeholders for future implementation.
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException(
|
||||||
|
$"The authentication type '{action.EndpointAuthType}' is not supported yet. " +
|
||||||
|
$"ProfileId: {action.ProfileId}, " +
|
||||||
|
$"Id: {action.Id}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var response = await http.SendAsync(httpReq, cancel);
|
||||||
|
var resBody = await response.Content.ReadAsStringAsync(cancel);
|
||||||
|
var resHeaders = response.Headers.ToDictionary();
|
||||||
|
|
||||||
|
var statusCode = (short)response.StatusCode;
|
||||||
|
|
||||||
|
await sender.ExecuteInsertProcedure(new InsertResultProcedure()
|
||||||
|
{
|
||||||
|
StatusId = statusCode,
|
||||||
|
ActionId = action.Id,
|
||||||
|
Header = JsonSerializer.Serialize(resHeaders, options: new() { WriteIndented = false }),
|
||||||
|
Body = resBody
|
||||||
|
}, config?["AddedWho"], cancel);
|
||||||
|
|
||||||
|
return response.IsSuccessStatusCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.RecActions.Commands;
|
||||||
|
|
||||||
|
public record UpdateActionProcedure : IUpdateProcedure
|
||||||
|
{
|
||||||
|
public long? ProfileId { get; set; }
|
||||||
|
public bool? Active { get; set; }
|
||||||
|
public byte? Sequence { get; set; }
|
||||||
|
public long? EndpointId { get; set; }
|
||||||
|
public long? EndpointAuthId { get; set; }
|
||||||
|
public short? EndpointParamsId { get; set; }
|
||||||
|
public short? SqlConnectionId { get; set; }
|
||||||
|
public byte? TypeId { get; set; }
|
||||||
|
public string? PreSql { get; set; }
|
||||||
|
public string? HeaderSql { get; set; }
|
||||||
|
public string? BodySql { get; set; }
|
||||||
|
public string? PostSql { get; set; }
|
||||||
|
public byte? ErrorActionId { get; set; }
|
||||||
|
|
||||||
|
public UpdateObjectProcedure ToObjectProcedure(long id, string? changedWho = null)
|
||||||
|
{
|
||||||
|
return new UpdateObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "ACTION",
|
||||||
|
Id = id,
|
||||||
|
Action = this
|
||||||
|
}.ChangedBy(changedWho);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using ReC.Application.RecActions.Commands;
|
|
||||||
using ReC.Domain.Entities;
|
|
||||||
|
|
||||||
namespace ReC.Application.RecActions;
|
|
||||||
|
|
||||||
// TODO: update to inject AddedWho from the current host/user contex
|
|
||||||
public class MappingProfile : AutoMapper.Profile
|
|
||||||
{
|
|
||||||
public MappingProfile()
|
|
||||||
{
|
|
||||||
CreateMap<CreateRecActionCommand, RecAction>()
|
|
||||||
.ForMember(e => e.Active, exp => exp.MapFrom(cmd => true))
|
|
||||||
.ForMember(e => e.AddedWhen, exp => exp.MapFrom(cmd => DateTime.UtcNow))
|
|
||||||
.ForMember(e => e.AddedWho, exp => exp.MapFrom(cmd => "ReC.API"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using ReC.Domain.Entities;
|
|
||||||
using AutoMapper;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using ReC.Application.Common.Dto;
|
|
||||||
|
|
||||||
namespace ReC.Application.RecActions.Queries;
|
|
||||||
|
|
||||||
public record ReadRecActionQueryBase
|
|
||||||
{
|
|
||||||
public long ProfileId { get; init; }
|
|
||||||
|
|
||||||
public ReadRecActionQuery ToReadQuery(Action<ReadRecActionQuery> modify)
|
|
||||||
{
|
|
||||||
ReadRecActionQuery query = new(this);
|
|
||||||
modify(query);
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public record ReadRecActionQuery : ReadRecActionQueryBase, IRequest<IEnumerable<RecActionDto>>
|
|
||||||
{
|
|
||||||
public ReadRecActionQuery(ReadRecActionQueryBase root) : base(root) { }
|
|
||||||
|
|
||||||
public bool? Invoked { get; set; } = null;
|
|
||||||
|
|
||||||
public ReadRecActionQuery() { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ReadRecActionQueryHandler(IRepository<RecActionView> repo, IMapper mapper) : IRequestHandler<ReadRecActionQuery, IEnumerable<RecActionDto>>
|
|
||||||
{
|
|
||||||
public async Task<IEnumerable<RecActionDto>> Handle(ReadRecActionQuery request, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var query = repo.Where(act => act.ProfileId == request.ProfileId);
|
|
||||||
|
|
||||||
if (request.Invoked is bool invoked)
|
|
||||||
query = invoked ? query.Where(act => act.Root!.OutRes != null) : query.Where(act => act.Root!.OutRes == null);
|
|
||||||
|
|
||||||
var actions = await query.ToListAsync(cancel);
|
|
||||||
|
|
||||||
if(actions.Count == 0)
|
|
||||||
throw new NotFoundException($"No actions found for the profile {request.ProfileId}.");
|
|
||||||
|
|
||||||
return mapper.Map<IEnumerable<RecActionDto>>(actions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using MediatR;
|
||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using AutoMapper;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
using ReC.Application.Common.Dto;
|
||||||
|
using ReC.Domain.Views;
|
||||||
|
|
||||||
|
namespace ReC.Application.RecActions.Queries;
|
||||||
|
|
||||||
|
public record ReadRecActionViewQuery : IRequest<IEnumerable<RecActionViewDto>>
|
||||||
|
{
|
||||||
|
public long? ProfileId { get; init; } = null;
|
||||||
|
|
||||||
|
public bool? Invoked { get; set; } = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReadRecActionViewQueryHandler(IRepository<RecActionView> repo, IMapper mapper) : IRequestHandler<ReadRecActionViewQuery, IEnumerable<RecActionViewDto>>
|
||||||
|
{
|
||||||
|
public async Task<IEnumerable<RecActionViewDto>> Handle(ReadRecActionViewQuery request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var query = repo.Query;
|
||||||
|
|
||||||
|
if (request.ProfileId is long profileId)
|
||||||
|
query = repo.Where(act => act.ProfileId == profileId);
|
||||||
|
|
||||||
|
if (request.Invoked is bool invoked)
|
||||||
|
query = invoked
|
||||||
|
? query.Where(act => act.Results!.Any())
|
||||||
|
: query.Where(act => !act.Results!.Any());
|
||||||
|
|
||||||
|
var actions = await query.ToListAsync(cancel);
|
||||||
|
|
||||||
|
if (actions.Count == 0)
|
||||||
|
throw new NotFoundException($"No actions found for the profile {request.ProfileId}.");
|
||||||
|
|
||||||
|
return mapper.Map<IEnumerable<RecActionViewDto>>(actions);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.Results.Commands;
|
||||||
|
|
||||||
|
public record DeleteResultProcedure : IDeleteProcedure
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Start GUID/ID (inclusive)
|
||||||
|
/// </summary>
|
||||||
|
public long Start { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End GUID/ID (inclusive). If 0, will be set to Start value.
|
||||||
|
/// </summary>
|
||||||
|
public long End { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Force parameter (not used for RESULT entity as it has no dependencies)
|
||||||
|
/// </summary>
|
||||||
|
public bool Force { get; set; }
|
||||||
|
|
||||||
|
public DeleteObjectProcedure ToObjectProcedure()
|
||||||
|
{
|
||||||
|
return new DeleteObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "RESULT",
|
||||||
|
Start = Start,
|
||||||
|
End = End,
|
||||||
|
Force = Force
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.Results.Commands;
|
||||||
|
|
||||||
|
public record InsertResultProcedure : IInsertProcedure
|
||||||
|
{
|
||||||
|
public long? ActionId { get; set; }
|
||||||
|
public short? StatusId { get; set; }
|
||||||
|
public string? Header { get; set; }
|
||||||
|
public string? Body { get; set; }
|
||||||
|
|
||||||
|
public InsertObjectProcedure ToObjectProcedure(string? addedWho = null)
|
||||||
|
{
|
||||||
|
return new InsertObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "RESULT",
|
||||||
|
Result = this
|
||||||
|
}.AddedBy(addedWho ?? "Rec.API");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||||
|
|
||||||
|
namespace ReC.Application.Results.Commands;
|
||||||
|
|
||||||
|
public record UpdateResultProcedure : IUpdateProcedure
|
||||||
|
{
|
||||||
|
public long? ActionId { get; set; }
|
||||||
|
public short? StatusId { get; set; }
|
||||||
|
public string? Header { get; set; }
|
||||||
|
public string? Body { get; set; }
|
||||||
|
|
||||||
|
public UpdateObjectProcedure ToObjectProcedure(long id, string? changedWho = null)
|
||||||
|
{
|
||||||
|
return new UpdateObjectProcedure
|
||||||
|
{
|
||||||
|
Entity = "RESULT",
|
||||||
|
Id = id,
|
||||||
|
Result = this
|
||||||
|
}.ChangedBy(changedWho);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/ReC.Application/Results/Queries/ReadResultViewQuery.cs
Normal file
49
src/ReC.Application/Results/Queries/ReadResultViewQuery.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using ReC.Application.Common.Dto;
|
||||||
|
using ReC.Domain.Views;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace ReC.Application.Results.Queries;
|
||||||
|
|
||||||
|
public record ReadResultViewQuery : IRequest<IEnumerable<ResultViewDto>>
|
||||||
|
{
|
||||||
|
public long? Id { get; init; } = null;
|
||||||
|
|
||||||
|
public long? ActionId { get; init; } = null;
|
||||||
|
|
||||||
|
public long? ProfileId { get; init; } = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReadResultViewQueryHandler(IRepository<ResultView> repo, IMapper mapper) : IRequestHandler<ReadResultViewQuery, IEnumerable<ResultViewDto>>
|
||||||
|
{
|
||||||
|
public async Task<IEnumerable<ResultViewDto>> Handle(ReadResultViewQuery request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var q = repo.Query;
|
||||||
|
|
||||||
|
if(request.Id is long id)
|
||||||
|
q = q.Where(rv => rv.Id == id);
|
||||||
|
|
||||||
|
if(request.ActionId is long actionId)
|
||||||
|
q = q.Where(rv => rv.ActionId == actionId);
|
||||||
|
|
||||||
|
if(request.ProfileId is long profileId)
|
||||||
|
q = q.Where(rv => rv.ProfileId == profileId);
|
||||||
|
|
||||||
|
var entities = await q.ToListAsync(cancel);
|
||||||
|
|
||||||
|
if (entities.Count == 0)
|
||||||
|
throw new NotFoundException($"No result views found for the given criteria. Criteria: {
|
||||||
|
JsonSerializer.Serialize(request, options: new()
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
WriteIndented = true
|
||||||
|
})}"
|
||||||
|
);
|
||||||
|
|
||||||
|
return mapper.Map<IEnumerable<ResultViewDto>>(entities);
|
||||||
|
}
|
||||||
|
}
|
||||||
71
src/ReC.Client/Api/BaseCrudApi.cs
Normal file
71
src/ReC.Client/Api/BaseCrudApi.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ReC.Client.Api
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides shared CRUD operations for API resources.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class BaseCrudApi
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The HTTP client used to send requests.
|
||||||
|
/// </summary>
|
||||||
|
protected readonly HttpClient Http;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The base resource path for the API endpoint.
|
||||||
|
/// </summary>
|
||||||
|
protected readonly string ResourcePath;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="BaseCrudApi"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="http">The HTTP client used for requests.</param>
|
||||||
|
/// <param name="resourcePath">The base resource path for the API endpoint.</param>
|
||||||
|
protected BaseCrudApi(HttpClient http, string resourcePath)
|
||||||
|
{
|
||||||
|
Http = http ?? throw new ArgumentNullException(nameof(http));
|
||||||
|
ResourcePath = resourcePath ?? throw new ArgumentNullException(nameof(resourcePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a resource.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The payload type.</typeparam>
|
||||||
|
/// <param name="payload">The payload to send.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>The HTTP response message.</returns>
|
||||||
|
public Task<HttpResponseMessage> CreateAsync<T>(T payload, CancellationToken cancel = default)
|
||||||
|
=> Http.PostAsync(ResourcePath, ReCClientHelpers.ToJsonContent(payload), cancel);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates a resource by identifier.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The payload type.</typeparam>
|
||||||
|
/// <param name="id">The resource identifier.</param>
|
||||||
|
/// <param name="payload">The payload to send.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>The HTTP response message.</returns>
|
||||||
|
public Task<HttpResponseMessage> UpdateAsync<T>(long id, T payload, CancellationToken cancel = default)
|
||||||
|
=> Http.PutAsync($"{ResourcePath}/{id}", ReCClientHelpers.ToJsonContent(payload), cancel);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes resources with identifiers supplied in the payload.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The payload type containing identifiers.</typeparam>
|
||||||
|
/// <param name="payload">The payload to send.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>The HTTP response message.</returns>
|
||||||
|
public Task<HttpResponseMessage> DeleteAsync<T>(T payload, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Delete, ResourcePath)
|
||||||
|
{
|
||||||
|
Content = ReCClientHelpers.ToJsonContent(payload)
|
||||||
|
};
|
||||||
|
return Http.SendAsync(request, cancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/ReC.Client/Api/CommonApi.cs
Normal file
20
src/ReC.Client/Api/CommonApi.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ReC.Client.Api
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to common object endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public class CommonApi : BaseCrudApi
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="CommonApi"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="http">The HTTP client used for requests.</param>
|
||||||
|
public CommonApi(HttpClient http) : base(http, "api/Common")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/ReC.Client/Api/EndpointAuthApi.cs
Normal file
20
src/ReC.Client/Api/EndpointAuthApi.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ReC.Client.Api
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to endpoint authentication endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public class EndpointAuthApi : BaseCrudApi
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="EndpointAuthApi"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="http">The HTTP client used for requests.</param>
|
||||||
|
public EndpointAuthApi(HttpClient http) : base(http, "api/EndpointAuth")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/ReC.Client/Api/EndpointParamsApi.cs
Normal file
20
src/ReC.Client/Api/EndpointParamsApi.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ReC.Client.Api
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to endpoint parameter endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public class EndpointParamsApi : BaseCrudApi
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="EndpointParamsApi"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="http">The HTTP client used for requests.</param>
|
||||||
|
public EndpointParamsApi(HttpClient http) : base(http, "api/EndpointParams")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/ReC.Client/Api/EndpointsApi.cs
Normal file
20
src/ReC.Client/Api/EndpointsApi.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ReC.Client.Api
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to endpoint definitions.
|
||||||
|
/// </summary>
|
||||||
|
public class EndpointsApi : BaseCrudApi
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="EndpointsApi"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="http">The HTTP client used for requests.</param>
|
||||||
|
public EndpointsApi(HttpClient http) : base(http, "api/Endpoints")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/ReC.Client/Api/ProfileApi.cs
Normal file
33
src/ReC.Client/Api/ProfileApi.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ReC.Client.Api
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to profile endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public class ProfileApi : BaseCrudApi
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ProfileApi"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="http">The HTTP client used for requests.</param>
|
||||||
|
public ProfileApi(HttpClient http) : base(http, "api/Profile")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a profile by identifier.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The profile identifier.</param>
|
||||||
|
/// <param name="includeActions">Whether to include related actions.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>The HTTP response message.</returns>
|
||||||
|
public Task<HttpResponseMessage> GetAsync(long id, bool includeActions = false, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
var query = ReCClientHelpers.BuildQuery(("Id", id), ("IncludeActions", includeActions));
|
||||||
|
return Http.GetAsync($"{ResourcePath}{query}", cancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/ReC.Client/Api/RecActionApi.cs
Normal file
45
src/ReC.Client/Api/RecActionApi.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ReC.Client.Api
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to RecAction endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public class RecActionApi : BaseCrudApi
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RecActionApi"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="http">The HTTP client used for requests.</param>
|
||||||
|
public RecActionApi(HttpClient http) : base(http, "api/RecAction")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes a ReC action for the specified profile.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="profileId">The profile identifier.</param>
|
||||||
|
/// <param name="cancellationToken">A token to cancel the operation.</param>
|
||||||
|
/// <returns><see langword="true"/> if the request succeeds; otherwise, <see langword="false"/>.</returns>
|
||||||
|
public async Task<bool> InvokeAsync(int profileId, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var resp = await Http.PostAsync($"{ResourcePath}/invoke/{profileId}", content: null, cancellationToken);
|
||||||
|
return resp.IsSuccessStatusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves Rec actions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="profileId">Optional profile filter.</param>
|
||||||
|
/// <param name="invoked">Optional invoked filter.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>The HTTP response message.</returns>
|
||||||
|
public Task<HttpResponseMessage> GetAsync(long? profileId = null, bool? invoked = null, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
var query = ReCClientHelpers.BuildQuery(("ProfileId", profileId), ("Invoked", invoked));
|
||||||
|
return Http.GetAsync($"{ResourcePath}{query}", cancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/ReC.Client/Api/ResultApi.cs
Normal file
34
src/ReC.Client/Api/ResultApi.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ReC.Client.Api
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to output result endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public class ResultApi : BaseCrudApi
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ResultApi"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="http">The HTTP client used for requests.</param>
|
||||||
|
public ResultApi(HttpClient http) : base(http, "api/Result")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves results with optional filters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">Optional result identifier.</param>
|
||||||
|
/// <param name="actionId">Optional action identifier.</param>
|
||||||
|
/// <param name="profileId">Optional profile identifier.</param>
|
||||||
|
/// <param name="cancel">A token to cancel the operation.</param>
|
||||||
|
/// <returns>The HTTP response message.</returns>
|
||||||
|
public Task<HttpResponseMessage> GetAsync(long? id = null, long? actionId = null, long? profileId = null, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
var query = ReCClientHelpers.BuildQuery(("Id", id), ("ActionId", actionId), ("ProfileId", profileId));
|
||||||
|
return Http.GetAsync($"{ResourcePath}{query}", cancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/ReC.Client/DependencyInjection.cs
Normal file
41
src/ReC.Client/DependencyInjection.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
#if NETFRAMEWORK
|
||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ReC.Client
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for setting up the ReC client in an <see cref="IServiceCollection"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class DependencyInjection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds and configures the <see cref="HttpClient"/> for the <see cref="ReCClient"/> to the specified <see cref="IServiceCollection"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
|
||||||
|
/// <param name="apiUri">The base URI of the ReC API.</param>
|
||||||
|
/// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns>
|
||||||
|
public static IHttpClientBuilder AddRecClient(this IServiceCollection services, string apiUri)
|
||||||
|
{
|
||||||
|
services.AddScoped<ReCClient>();
|
||||||
|
return services.AddHttpClient(ReCClient.ClientName, client =>
|
||||||
|
{
|
||||||
|
client.BaseAddress = new Uri(apiUri);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds and configures the <see cref="HttpClient"/> for the <see cref="ReCClient"/> to the specified <see cref="IServiceCollection"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
|
||||||
|
/// <param name="configureClient">An action to configure the <see cref="HttpClient"/>.</param>
|
||||||
|
/// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns>
|
||||||
|
public static IHttpClientBuilder AddRecClient(this IServiceCollection services, Action<HttpClient> configureClient)
|
||||||
|
{
|
||||||
|
services.AddScoped<ReCClient>();
|
||||||
|
return services.AddHttpClient(ReCClient.ClientName, configureClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,10 +2,37 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net462;net8.0</TargetFrameworks>
|
<TargetFrameworks>net462;net8.0</TargetFrameworks>
|
||||||
|
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(MSBuildProjectName).xml</DocumentationFile>
|
||||||
|
<PackageId>ReC.Client</PackageId>
|
||||||
|
<Authors>Digital Data GmbH</Authors>
|
||||||
|
<Company>Digital Data GmbH</Company>
|
||||||
|
<Product>ReC.Client</Product>
|
||||||
|
<Copyright>Copyright 2025</Copyright>
|
||||||
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
|
<RepositoryUrl>http://git.dd:3000/AppStd/Rec.git</RepositoryUrl>
|
||||||
|
<PackageTags>digital data rec api</PackageTags>
|
||||||
|
<Version>1.0.0-beta</Version>
|
||||||
|
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||||
|
<FileVersion>1.0.0.0</FileVersion>
|
||||||
|
<Description>Client-Bibliothek für die Interaktion mit der ReC.API, die typisierten HTTP-Zugriff und DI-Integration bietet.</Description>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)' != 'net462'">
|
<PropertyGroup Condition="'$(TargetFramework)' != 'net462'">
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\..\assets\icon.png">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath>\</PackagePath>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||||
|
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||||
|
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
150
src/ReC.Client/ReCClient.cs
Normal file
150
src/ReC.Client/ReCClient.cs
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using ReC.Client.Api;
|
||||||
|
|
||||||
|
namespace ReC.Client
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A client for interacting with the ReC API.
|
||||||
|
/// </summary>
|
||||||
|
public class ReCClient
|
||||||
|
{
|
||||||
|
private readonly HttpClient _http;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A unique name for the HttpClient used by the ReCClient.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly string ClientName = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to RecAction endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public RecActionApi RecActions { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to Result endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public ResultApi Results { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to Profile endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public ProfileApi Profiles { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to EndpointAuth endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public EndpointAuthApi EndpointAuth { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to EndpointParams endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public EndpointParamsApi EndpointParams { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to Endpoints endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public EndpointsApi Endpoints { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to Common endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public CommonApi Common { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ReCClient"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpClientFactory">The factory to create HttpClients.</param>
|
||||||
|
public ReCClient(IHttpClientFactory httpClientFactory)
|
||||||
|
{
|
||||||
|
_http = httpClientFactory.CreateClient(ClientName);
|
||||||
|
RecActions = new RecActionApi(_http);
|
||||||
|
Results = new ResultApi(_http);
|
||||||
|
Profiles = new ProfileApi(_http);
|
||||||
|
EndpointAuth = new EndpointAuthApi(_http);
|
||||||
|
EndpointParams = new EndpointParamsApi(_http);
|
||||||
|
Endpoints = new EndpointsApi(_http);
|
||||||
|
Common = new CommonApi(_http);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Static
|
||||||
|
private static readonly IServiceCollection Services = new ServiceCollection();
|
||||||
|
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
private static IServiceProvider? Provider = null;
|
||||||
|
#else
|
||||||
|
private static IServiceProvider Provider = null;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures and builds the static <see cref="IServiceProvider"/> for creating <see cref="ReCClient"/> instances.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method should only be called once during application startup.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="apiUri">The base URI of the ReC API.</param>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown if the static provider has already been built.</exception>
|
||||||
|
[Obsolete("Use a local service collection instead of the static provider.")]
|
||||||
|
public static void BuildStaticClient(string apiUri)
|
||||||
|
{
|
||||||
|
if(Provider != null)
|
||||||
|
throw new InvalidOperationException("Static Provider is already built.");
|
||||||
|
|
||||||
|
Services.AddRecClient(apiUri);
|
||||||
|
Provider = Services.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures and builds the static <see cref="IServiceProvider"/> for creating <see cref="ReCClient"/> instances.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method should only be called once during application startup.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="configureClient">An action to configure the <see cref="HttpClient"/>.</param>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown if the static provider has already been built.</exception>
|
||||||
|
[Obsolete("Use a local service collection instead of the static provider.")]
|
||||||
|
public static void BuildStaticClient(Action<HttpClient> configureClient)
|
||||||
|
{
|
||||||
|
if (Provider != null)
|
||||||
|
throw new InvalidOperationException("Static Provider is already built.");
|
||||||
|
|
||||||
|
Services.AddRecClient(configureClient);
|
||||||
|
Provider = Services.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ReCClient"/> instance using the statically configured provider.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A new instance of the <see cref="ReCClient"/>.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown if <see cref="BuildStaticClient(string)"/> has not been called yet.</exception>
|
||||||
|
[Obsolete("Use a local service collection instead of the static provider.")]
|
||||||
|
public static ReCClient Create()
|
||||||
|
{
|
||||||
|
if (Provider == null)
|
||||||
|
throw new InvalidOperationException("Static Provider is not built. Call BuildStaticClient first.");
|
||||||
|
|
||||||
|
return Provider.GetRequiredService<ReCClient>();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies which part of the result to return for result view endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public enum ResultType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns both header and body.
|
||||||
|
/// </summary>
|
||||||
|
Full,
|
||||||
|
/// <summary>
|
||||||
|
/// Returns only the header portion of the result.
|
||||||
|
/// </summary>
|
||||||
|
OnlyHeader,
|
||||||
|
/// <summary>
|
||||||
|
/// Returns only the body portion of the result.
|
||||||
|
/// </summary>
|
||||||
|
OnlyBody
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/ReC.Client/ReCClientHelpers.cs
Normal file
49
src/ReC.Client/ReCClientHelpers.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
|
||||||
|
#if NETFRAMEWORK
|
||||||
|
using System.Net.Http;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ReC.Client
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides shared helpers for composing requests.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ReCClientHelpers
|
||||||
|
{
|
||||||
|
#if NETFRAMEWORK
|
||||||
|
/// <summary>
|
||||||
|
/// Builds a query string from the provided key/value pairs, skipping null values.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters">The key/value pairs to include in the query string.</param>
|
||||||
|
/// <returns>A query string beginning with '?', or an empty string if no values are provided.</returns>
|
||||||
|
public static string BuildQuery(params (string Key, object Value)[] parameters)
|
||||||
|
#else
|
||||||
|
/// <summary>
|
||||||
|
/// Builds a query string from the provided key/value pairs, skipping null values.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters">The key/value pairs to include in the query string.</param>
|
||||||
|
/// <returns>A query string beginning with '?', or an empty string if no values are provided.</returns>
|
||||||
|
public static string BuildQuery(params (string Key, object? Value)[] parameters)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
var parts = parameters
|
||||||
|
.Where(p => p.Value != null)
|
||||||
|
.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(Convert.ToString(p.Value, CultureInfo.InvariantCulture) ?? string.Empty)}");
|
||||||
|
|
||||||
|
var query = string.Join("&", parts);
|
||||||
|
return string.IsNullOrWhiteSpace(query) ? string.Empty : $"?{query}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a JSON content payload from the provided object.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the payload.</typeparam>
|
||||||
|
/// <param name="payload">The payload to serialize.</param>
|
||||||
|
/// <returns>A <see cref="JsonContent"/> instance ready for HTTP requests.</returns>
|
||||||
|
public static JsonContent ToJsonContent<T>(T payload) => JsonContent.Create(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/ReC.Client/TaskSyncExtensions.cs
Normal file
26
src/ReC.Client/TaskSyncExtensions.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#if NETFRAMEWORK
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ReC.Client
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides synchronous wrappers for Task-based operations.
|
||||||
|
/// </summary>
|
||||||
|
public static class TaskSyncExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Blocks until the task completes and propagates any exception.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="task">The task to wait for.</param>
|
||||||
|
public static void Sync(this Task task) => task.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Blocks until the task completes and returns its result, propagating any exception.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TResult">The type of the task result.</typeparam>
|
||||||
|
/// <param name="task">The task to wait for.</param>
|
||||||
|
/// <returns>The result of the completed task.</returns>
|
||||||
|
public static TResult Sync<TResult>(this Task<TResult> task) => task.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/ReC.Domain/Attributes/MustConfiguredAttribute.cs
Normal file
6
src/ReC.Domain/Attributes/MustConfiguredAttribute.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace ReC.Domain.Attributes;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
|
public class MustConfiguredAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
7
src/ReC.Domain/Constants/ApiKeyLocation.cs
Normal file
7
src/ReC.Domain/Constants/ApiKeyLocation.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace ReC.Domain.Constants;
|
||||||
|
|
||||||
|
public enum ApiKeyLocation : byte
|
||||||
|
{
|
||||||
|
Header = 0,
|
||||||
|
Query = 1
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user