Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff69aee6d3 | ||
|
|
4fa379f74e | ||
|
|
58f8006ab2 | ||
|
|
d7806ff784 | ||
|
|
2bd0b12335 | ||
|
|
0fe6f40b2a | ||
|
|
c8ca8ac95b | ||
|
|
c3ef1c56c2 | ||
|
|
6ea2ba9975 | ||
|
|
1418914242 | ||
|
|
067af804ef | ||
|
|
04b5656fd1 | ||
|
|
7f686fa8c6 | ||
|
|
6114d8f457 | ||
|
|
b8c3201c58 | ||
|
|
a8daea830f | ||
|
|
5be7c8c56a | ||
|
|
d24b0dafb1 | ||
|
|
cd47a2b93a | ||
|
|
c11535ebfc | ||
|
|
129d10c7d1 | ||
|
|
9f132eeb29 | ||
|
|
80c798e5e6 | ||
|
|
2690b3230e | ||
|
|
155a379ab4 | ||
|
|
f56cb70b55 | ||
|
|
bb77a4dc9a | ||
|
|
5ac317cbe3 | ||
|
|
0bcc3484dc | ||
|
|
fddf400ddd | ||
|
|
a1b8410bc2 | ||
|
|
cb0da32ce4 | ||
|
|
70e286670e | ||
|
|
c6075a5c39 | ||
|
|
4e1211a1e1 | ||
|
|
c8e54815ef | ||
|
|
31d775c917 | ||
|
|
78d147b002 | ||
|
|
07018c3ce4 | ||
|
|
db08bbf162 | ||
|
|
ac357285c3 | ||
|
|
a4927f9742 | ||
|
|
fac88f3471 | ||
|
|
aeb7542cfe | ||
|
|
9724bbac24 | ||
|
|
deb21dbb9a | ||
|
|
98fe1e3e3d | ||
|
|
48edfa1cbb | ||
|
|
d9b65010cf | ||
|
|
3e1d8bc1cc | ||
|
|
bc441c1255 | ||
|
|
17a4149a73 | ||
|
|
6e786ca796 | ||
|
|
25e251aa79 | ||
|
|
a731c979bb | ||
|
|
90631d22ae | ||
|
|
17a99ff084 | ||
|
|
010f2d5eaa | ||
|
|
29a11f0e0b | ||
|
|
b8f5ebf69a | ||
|
|
385c76510b | ||
|
|
631a0b7a9a | ||
|
|
957e75a853 | ||
|
|
1233f15b91 | ||
|
|
8a06a86eb3 | ||
|
|
c43e18f184 | ||
|
|
f8f826979d | ||
|
|
d912c5c6c3 | ||
|
|
20bdd8b209 | ||
|
|
c4a0469bae | ||
|
|
bf384d7e6a | ||
|
|
957d2210a9 | ||
|
|
a3f461478c | ||
|
|
16fb6b9434 | ||
|
|
7220700bac | ||
|
|
cc62696017 | ||
|
|
3316fc0275 | ||
|
|
d4598e0c6c | ||
|
|
8f58e15f0f | ||
|
|
43656e848a | ||
|
|
9ed210613e | ||
|
|
3a3245b643 | ||
|
|
5050c901bc | ||
|
|
fdaf12a183 | ||
|
|
46c6ddcc80 | ||
|
|
6cdd03a02f | ||
|
|
7071c404f0 | ||
|
|
1d1060d031 | ||
|
|
cb89817d88 | ||
|
|
b0c89362d0 | ||
|
|
6ff016e7ec | ||
|
|
dee9ccf0b1 | ||
|
|
d45a89ab67 | ||
|
|
d9b3e80f7c | ||
|
|
3fda1ac5db | ||
|
|
efc63ca398 | ||
|
|
299d6ff4dc | ||
|
|
8688c752d6 | ||
|
|
9835121fd9 | ||
|
|
937a5595d8 | ||
|
|
4dd0899209 | ||
|
|
58ce606410 | ||
|
|
92891438f6 | ||
|
|
a98bfa4308 | ||
|
|
0261a6093e | ||
|
|
6e56a4c223 | ||
|
|
ccc49df720 | ||
|
|
e38edd24a0 | ||
|
|
c24e809b88 | ||
|
|
87f597c091 | ||
|
|
5a6c951641 | ||
|
|
1ac0018926 | ||
|
|
acf8bc224b | ||
|
|
558ca0dbb4 | ||
|
|
5ad59f6569 | ||
|
|
270929948d | ||
|
|
71a0abf0b2 | ||
|
|
95a86e1a87 | ||
|
|
627885e36b | ||
|
|
5d7e2f74ce | ||
|
|
dee9098379 | ||
|
|
d7a168c9fa | ||
|
|
28a821ae91 | ||
|
|
447ffb366e | ||
|
|
1142cfd94e | ||
|
|
a9bb319eb1 | ||
|
|
45a6c0aca0 | ||
|
|
bbba869cdd | ||
|
|
1b586da2ab | ||
|
|
5ae2622454 | ||
|
|
4735551e06 | ||
|
|
a28caa5c69 | ||
|
|
bd0f03344d | ||
|
|
c3cc21f334 | ||
|
|
9a0371fb08 | ||
|
|
381d5b7e94 | ||
|
|
d3566c5e43 | ||
|
|
0352d154a4 | ||
|
|
8eca793f5f | ||
|
|
343f405982 | ||
|
|
2ee7a49122 | ||
|
|
1ebd83a2f4 | ||
|
|
da2c0f9a34 | ||
|
|
1af860fdc1 | ||
|
|
34ddfc1d73 | ||
|
|
c04ae9a933 | ||
|
|
236f6f0f9d | ||
|
|
0e853811a9 | ||
|
|
c9deba0427 | ||
|
|
84a171d4a4 | ||
|
|
06539236a1 | ||
|
|
3a3d5daefc | ||
|
|
b3957f4af4 | ||
|
|
fe9b9052e9 | ||
|
|
d8a95f3f92 | ||
|
|
d25545ddad | ||
|
|
13712be13b | ||
|
|
7effc32498 | ||
|
|
681ed99fea | ||
|
|
8909bcf5c4 | ||
|
|
5fb67df9ac | ||
|
|
411e603f51 | ||
|
|
8fed46961a | ||
|
|
138ef84973 | ||
|
|
8100518549 | ||
|
|
312721bf51 | ||
|
|
8d069c2da0 | ||
|
|
dd59294911 | ||
|
|
8643b2625b | ||
|
|
28a3a17a15 | ||
|
|
c805a31824 | ||
|
|
4c2b0cd7d5 | ||
|
|
67fa99c64a | ||
|
|
275fd109e6 | ||
|
|
9f6c1e505b | ||
|
|
c4f57b92ef | ||
|
|
7d510d9fbc | ||
|
|
248cc2fa2d | ||
|
|
242a4c6fbb | ||
|
|
761d14beb9 | ||
|
|
891801c73d | ||
|
|
931e1a8698 | ||
|
|
8b68f7b99c | ||
|
|
aff58caeb0 | ||
|
|
93b3416d2c | ||
|
|
1c5080bf04 | ||
|
|
ef13117042 | ||
|
|
1fb157aa0a | ||
|
|
8eae097e8b | ||
|
|
ea71e55e2d | ||
|
|
0adfae1616 | ||
|
|
7037ea72ca | ||
|
|
4856b6e6f4 | ||
|
|
104f01e515 | ||
|
|
0ba47269aa | ||
|
|
0d626fd84a | ||
|
|
9ba48de55b | ||
|
|
5e25fd7a11 | ||
|
|
6107de3e53 | ||
|
|
f8fff04065 | ||
|
|
df167caf55 | ||
|
|
aca5882ad3 | ||
|
|
8afceda1cd | ||
|
|
6b027193d1 | ||
|
|
c1c5641d1e | ||
|
|
81702a4bf8 | ||
|
|
2efd2859c7 | ||
|
|
5eeb1cbfc7 | ||
|
|
dd6996bdb1 | ||
|
|
fb00986f59 | ||
|
|
1df0b97daf | ||
|
|
0338737385 | ||
|
|
e6fdcf3d0d | ||
|
|
82c0936b4c | ||
|
|
36e5d3d7c8 | ||
|
|
5d2a886f6f | ||
|
|
fb27cc8c92 | ||
|
|
dc4047ca6d | ||
|
|
6ab01830f3 | ||
|
|
46fe14d9ef | ||
|
|
6e6419ab51 | ||
|
|
f17368d939 | ||
|
|
ed6e57ffa9 | ||
|
|
7aea3fa8de | ||
|
|
6be74e9a24 | ||
|
|
d43eb17aa3 | ||
|
|
2949d6218b | ||
|
|
01547d8c3f | ||
|
|
15a8d8f449 | ||
|
|
18dd9131cb | ||
|
|
db07ad1a82 | ||
|
|
f82a5cef76 | ||
|
|
11967e3252 | ||
|
|
614730caab | ||
|
|
f3a8f78144 | ||
|
|
9823cd7ea6 | ||
|
|
b2688867f3 | ||
|
|
a7a68fc290 | ||
|
|
0f903a58b5 | ||
|
|
86e2bad4fe | ||
|
|
1efbf9d99f | ||
|
|
24697776d0 | ||
|
|
4b806a3202 | ||
|
|
26df423180 | ||
|
|
4dbc74e488 | ||
|
|
f39eb9d8fe | ||
|
|
593219db8c | ||
|
|
d658497545 | ||
|
|
020bdee36f | ||
|
|
8e0c099867 | ||
|
|
ef134a5b3c | ||
|
|
b947959724 | ||
|
|
717b891554 | ||
|
|
58126e7573 | ||
|
|
91d325ce4e | ||
|
|
2fede443cc | ||
|
|
3cc8514136 | ||
|
|
11311421ad | ||
|
|
f4024d563a | ||
|
|
78b03b6996 | ||
|
|
80f220fcae | ||
|
|
2d0f2b494a | ||
|
|
06e2c3ee6a | ||
|
|
d428f6e17c | ||
|
|
3c0fd8844c | ||
|
|
5829bffaa0 | ||
|
|
b7c135f0fc | ||
|
|
b703b0c64d | ||
|
|
6d554d19a7 | ||
|
|
ec777fabf8 | ||
|
|
db053e3e5f | ||
|
|
41bf9b63bf | ||
|
|
940630f0ed | ||
|
|
d7177e1a72 | ||
|
|
6de3d30a8b | ||
|
|
c67041faea | ||
|
|
831d57a8a0 | ||
|
|
0e8a5e1169 | ||
|
|
2d74f7f1bd | ||
|
|
41fdb067fe | ||
|
|
ebf35d7f12 | ||
|
|
b4b687dfcc | ||
|
|
77307f4bf0 | ||
|
|
f200ccbf60 | ||
|
|
464c21e41d | ||
|
|
27f57150e1 | ||
|
|
364fe65e0d | ||
|
|
79c63107e9 | ||
|
|
b239c402fa | ||
|
|
d945731252 | ||
|
|
f4a1064003 | ||
|
|
c979a4632e | ||
|
|
5f18478675 | ||
|
|
9eed3cff6b | ||
|
|
e71ba99f9c | ||
|
|
6db1a2f367 | ||
|
|
8de761e4e6 | ||
|
|
ca6d04d528 | ||
|
|
06d9952cd7 | ||
|
|
f607eaf152 | ||
|
|
92d481f75d | ||
|
|
b631c6aa4b | ||
|
|
1c36602995 | ||
|
|
cb59635882 | ||
|
|
5ae0c500ac | ||
|
|
1cf019b3b9 | ||
|
|
db04f800e9 | ||
|
|
0a4108d345 | ||
|
|
e7a19611f0 | ||
|
|
afaad43c47 | ||
|
|
5ad24da7cf | ||
|
|
bccdab2ba8 | ||
|
|
156dae40b7 | ||
|
|
5bdd2a5786 | ||
|
|
1b67d9cf8b | ||
|
|
1acdb0ea7b | ||
|
|
1bd05e64e5 | ||
|
|
2047e440d8 | ||
|
|
303dba3c6f | ||
|
|
1771d74b43 | ||
|
|
1dd4f54a1d | ||
|
|
bad91e8090 | ||
|
|
fae1e080c4 | ||
|
|
f9c0c5fe16 | ||
|
|
4de23d27b3 | ||
|
|
43c777dfe2 | ||
|
|
d4bce12dbb | ||
|
|
5dba8d3dd4 | ||
|
|
299b876e1c | ||
|
|
0d78affd3c | ||
|
|
dde1dbe056 | ||
|
|
4bac2f58c2 | ||
|
|
b81e6feb45 | ||
|
|
2a8d72963d | ||
|
|
4138a1941f | ||
|
|
4b658bdfd5 | ||
|
|
17af31d810 | ||
|
|
9945b773db | ||
|
|
b85cf6a1cb | ||
|
|
f98b102a2e | ||
|
|
81ddc11746 | ||
|
|
f29ac65d7c | ||
|
|
5b94672ff5 | ||
|
|
8450db51be | ||
|
|
c535392ef1 | ||
|
|
5c1e18a9a9 | ||
|
|
0607de1802 | ||
|
|
4f1b05bf33 | ||
|
|
4c1e52e452 | ||
|
|
3db6546a2b |
@@ -0,0 +1,21 @@
|
|||||||
|
# Exclude build output and caches
|
||||||
|
**/bin/
|
||||||
|
**/obj/
|
||||||
|
**/out/
|
||||||
|
.vs/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Exclude git internal files
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Exclude local environment/secret files
|
||||||
|
.env
|
||||||
|
*.env
|
||||||
|
*.user
|
||||||
|
*.suo
|
||||||
|
|
||||||
|
# Exclude node modules and temporary logs
|
||||||
|
**/node_modules/
|
||||||
|
**/dist/
|
||||||
|
*.log
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
*.env
|
*.env
|
||||||
*.env.json
|
*.env.json
|
||||||
|
certificates/
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.userprefs
|
*.userprefs
|
||||||
@@ -51,6 +53,8 @@ bld/
|
|||||||
|
|
||||||
# Build results on 'Bin' directories
|
# Build results on 'Bin' directories
|
||||||
**/[Bb]in/*
|
**/[Bb]in/*
|
||||||
|
dist/assets/
|
||||||
|
dist/
|
||||||
# Uncomment if you have tasks that rely on *.refresh files to move binaries
|
# Uncomment if you have tasks that rely on *.refresh files to move binaries
|
||||||
# (https://github.com/github/gitignore/pull/3736)
|
# (https://github.com/github/gitignore/pull/3736)
|
||||||
#!**/[Bb]in/*.refresh
|
#!**/[Bb]in/*.refresh
|
||||||
@@ -58,8 +62,7 @@ bld/
|
|||||||
# Visual Studio 2015/2017 cache/options directory
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
.vscode/
|
.vscode/
|
||||||
bin/
|
|
||||||
certificates/
|
|
||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
#wwwroot/
|
#wwwroot/
|
||||||
|
|
||||||
@@ -347,6 +350,7 @@ _Pvt_Extensions
|
|||||||
|
|
||||||
# Paket dependency manager
|
# Paket dependency manager
|
||||||
**/.paket/paket.exe
|
**/.paket/paket.exe
|
||||||
|
**/.paket/paket.exe
|
||||||
paket-files/
|
paket-files/
|
||||||
|
|
||||||
# FAKE - F# Make
|
# FAKE - F# Make
|
||||||
@@ -432,3 +436,9 @@ FodyWeavers.xsd
|
|||||||
*.msix
|
*.msix
|
||||||
*.msm
|
*.msm
|
||||||
*.msp
|
*.msp
|
||||||
|
/PostFundManagement.ConsoleUI/dist/config.json
|
||||||
|
/PostFundManagement.ConsoleUI/dist/env-config.js
|
||||||
|
/PostFundManagement.ConsoleUI/dist/assets/index-BaOzkVJi.css
|
||||||
|
/PostFundManagement.ConsoleUI/dist/assets/index-BU8qSe7k.js
|
||||||
|
/PostFundManagement.ConsoleUI/dist/favicon.svg
|
||||||
|
/PostFundManagement.ConsoleUI/dist/index.html
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<NoWarn>$(NoWarn);1701;1702;MA0048;MA0051;AV0029;AV0030</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Assembly signing and licensing for non-test projects -->
|
||||||
|
<PropertyGroup Condition="!$(MSBuildProjectName.EndsWith('Tests'))">
|
||||||
|
<SignAssembly>true</SignAssembly>
|
||||||
|
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)PostFundManagement.snk</AssemblyOriginatorKeyFile>
|
||||||
|
<PackageLicenseFile>$(MSBuildThisFileDirectory)LICENSE</PackageLicenseFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Centralized Common Usings across solution -->
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- System & BCL -->
|
||||||
|
<Using Include="System.Collections.ObjectModel" />
|
||||||
|
<Using Include="System.ComponentModel.DataAnnotations" />
|
||||||
|
<Using Include="System.Globalization" />
|
||||||
|
<Using Include="System.Net" />
|
||||||
|
<Using Include="System.Reflection" />
|
||||||
|
<Using Include="System.Security.Cryptography" />
|
||||||
|
<Using Include="System.Security.Cryptography.X509Certificates" />
|
||||||
|
<Using Include="System.Text" />
|
||||||
|
<Using Include="System.Text.Json" />
|
||||||
|
<Using Include="System.Text.Json.Serialization" />
|
||||||
|
<Using Include="System.Text.RegularExpressions" />
|
||||||
|
<Using Include="System.Threading.Channels" />
|
||||||
|
<Using Include="System.Web" />
|
||||||
|
|
||||||
|
<!-- Microsoft Extensions & Web Core -->
|
||||||
|
<Using Include="Microsoft.AspNetCore.Builder" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.DataProtection" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.Http" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.Routing" />
|
||||||
|
<Using Include="Microsoft.Extensions.Configuration" />
|
||||||
|
<Using Include="Microsoft.Extensions.DependencyInjection" />
|
||||||
|
<Using Include="Microsoft.Extensions.DependencyInjection.Extensions" />
|
||||||
|
<Using Include="Microsoft.Extensions.Hosting" />
|
||||||
|
<Using Include="Microsoft.Extensions.Logging" />
|
||||||
|
<Using Include="Microsoft.Extensions.Options" />
|
||||||
|
<Using Include="Microsoft.IdentityModel.Tokens" />
|
||||||
|
<Using Include="Microsoft.FeatureManagement" />
|
||||||
|
|
||||||
|
<!-- Domain Constants -->
|
||||||
|
<Using Static="true" Include="PostFundManagement.Domain.Extensions.Constants" />
|
||||||
|
|
||||||
|
<!-- CQRS, Utilities & Persistence -->
|
||||||
|
<Using Include="FluentResults" />
|
||||||
|
<Using Include="HashidsNet" />
|
||||||
|
<Using Include="Humanizer" />
|
||||||
|
<Using Include="Mediator" />
|
||||||
|
<Using Include="Microsoft.EntityFrameworkCore" />
|
||||||
|
<Using Include="Microsoft.EntityFrameworkCore.Design" />
|
||||||
|
<Using Include="Microsoft.EntityFrameworkCore.Metadata.Builders" />
|
||||||
|
<Using Include="Quartz" />
|
||||||
|
<Using Include="Refit" />
|
||||||
|
|
||||||
|
<!-- API & Documentation -->
|
||||||
|
<Using Include="Asp.Versioning" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.OpenApi" />
|
||||||
|
<Using Include="Microsoft.OpenApi" />
|
||||||
|
<Using Include="Scalar.AspNetCore" />
|
||||||
|
|
||||||
|
<!-- Authentication -->
|
||||||
|
<Using Include="IdentityModel.AspNetCore.OAuth2Introspection" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.Authentication" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.Authentication.Cookies" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" />
|
||||||
|
|
||||||
|
<!-- Diagnostics & Health -->
|
||||||
|
<Using Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" />
|
||||||
|
<Using Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
|
||||||
|
|
||||||
|
<!-- Messaging & Cloud Storage -->
|
||||||
|
<Using Include="Amazon.Runtime" />
|
||||||
|
<Using Include="Amazon.S3" />
|
||||||
|
<Using Include="Amazon.S3.Model" />
|
||||||
|
<Using Include="MailKit.Net.Smtp" />
|
||||||
|
<Using Include="MimeKit" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Domain-specific Usings (Application and downstream projects excluding Infrastructure where Claim/Activity/TokenRequest collide) -->
|
||||||
|
<ItemGroup Condition="'$(MSBuildProjectName)' == 'PostFundManagement.Application'">
|
||||||
|
<Using Include="ModelContextProtocol.Server" />
|
||||||
|
<Using Include="System.ComponentModel" />
|
||||||
|
<Using Include="System.Collections.Concurrent" />
|
||||||
|
<Using Include="System.IO.Pipelines" />
|
||||||
|
<Using Include="ModelContextProtocol.Client" />
|
||||||
|
<Using Include="ModelContextProtocol.Protocol" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Domain-specific Usings (Domain and downstream projects excluding Infrastructure where Claim/Activity/TokenRequest collide) -->
|
||||||
|
<ItemGroup Condition="'$(MSBuildProjectName)' == 'PostFundManagement.Domain'">
|
||||||
|
<Using Include="System.Security.Claims" />
|
||||||
|
<Using Include="AccessTokenClient" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Non-Domain / Non-Infrastructure Usings (Application, Api, Tests) -->
|
||||||
|
<ItemGroup Condition="'$(MSBuildProjectName)' != 'PostFundManagement.Domain' and '$(MSBuildProjectName)' != 'PostFundManagement.Infrastructure'">
|
||||||
|
<Using Include="System.Diagnostics" />
|
||||||
|
<Using Include="System.Security.Claims" />
|
||||||
|
<Using Include="AccessTokenClient" />
|
||||||
|
<Using Static="true" Include="PostFundManagement.Application.Extensions.General" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
|
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- Core & Utilities -->
|
||||||
|
<PackageVersion Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
|
||||||
|
<PackageVersion Include="Hashids.net" Version="1.7.0" />
|
||||||
|
<PackageVersion Include="Humanizer" Version="3.0.10" />
|
||||||
|
<PackageVersion Include="Scrutor" Version="7.0.0" />
|
||||||
|
|
||||||
|
<!-- Microsoft Extensions & Configuration -->
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="10.11.0" />
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.11.0" />
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.Validation" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.FeatureManagement.AspNetCore" Version="4.8.0" />
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.14" />
|
||||||
|
|
||||||
|
<!-- Security, Authentication & Identity -->
|
||||||
|
<PackageVersion Include="AccessTokenClient.Extensions" Version="5.1.0" />
|
||||||
|
<PackageVersion Include="Duende.AccessTokenManagement" Version="4.3.0" />
|
||||||
|
<PackageVersion Include="Duende.AccessTokenManagement.OpenIdConnect" Version="4.3.0" />
|
||||||
|
<PackageVersion Include="IdentityModel" Version="6.2.0" />
|
||||||
|
<PackageVersion Include="IdentityModel.AspNetCore" Version="4.3.0" />
|
||||||
|
<PackageVersion Include="IdentityModel.AspNetCore.OAuth2introspection" Version="6.2.0" />
|
||||||
|
<PackageVersion Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
||||||
|
<PackageVersion Include="KubernetesClient" Version="21.0.8" />
|
||||||
|
<PackageVersion Include="Microsoft.AspNetCore.Authentication.Certificate" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="10.0.14" />
|
||||||
|
|
||||||
|
<!-- Upgraded to latest non-vulnerable version -->
|
||||||
|
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="System.Security.Cryptography.Xml" Version="10.0.14" />
|
||||||
|
|
||||||
|
<!-- Resilience -->
|
||||||
|
<PackageVersion Include="Polly" Version="8.8.1" />
|
||||||
|
<PackageVersion Include="Polly.Extensions" Version="8.8.1" />
|
||||||
|
|
||||||
|
<!-- Model Context Protocol (MCP) -->
|
||||||
|
<PackageVersion Include="ModelContextProtocol" Version="2.3.0" />
|
||||||
|
<PackageVersion Include="ModelContextProtocol.Core" Version="2.3.0" />
|
||||||
|
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="2.3.0" />
|
||||||
|
|
||||||
|
<!-- API, Web & Documentation -->
|
||||||
|
<PackageVersion Include="Asp.Versioning.Abstractions" Version="10.2.1" />
|
||||||
|
<PackageVersion Include="Asp.Versioning.Http" Version="10.2.3" />
|
||||||
|
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="10.2.1" />
|
||||||
|
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.AspNetCore.OutputCaching.StackExchangeRedis" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Refit.HttpClientFactory" Version="16.4.0" />
|
||||||
|
<PackageVersion Include="Scalar.AspNetCore" Version="2.18.3" />
|
||||||
|
|
||||||
|
<!-- Database & Entity Framework Core -->
|
||||||
|
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Abstractions" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Analyzers" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.14" />
|
||||||
|
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.3" />
|
||||||
|
|
||||||
|
<!-- Quartz Scheduler -->
|
||||||
|
<PackageVersion Include="Quartz" Version="4.6.2" />
|
||||||
|
<PackageVersion Include="Quartz.AspNetCore" Version="4.6.2" />
|
||||||
|
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="4.6.2" />
|
||||||
|
<PackageVersion Include="Quartz.Plugins" Version="4.6.2" />
|
||||||
|
<PackageVersion Include="Quartz.Plugins.TimeZoneConverter" Version="4.6.2" />
|
||||||
|
<PackageVersion Include="Quartz.Serialization.SystemTextJson" Version="4.6.2" />
|
||||||
|
|
||||||
|
<!-- Health Checks -->
|
||||||
|
<PackageVersion Include="AspNetCore.HealthChecks.NpgSql" Version="9.0.0" />
|
||||||
|
<PackageVersion Include="AspNetCore.HealthChecks.UI" Version="9.0.0" />
|
||||||
|
<PackageVersion Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
|
||||||
|
<PackageVersion Include="AspNetCore.HealthChecks.UI.Core" Version="9.0.0" />
|
||||||
|
<PackageVersion Include="AspNetCore.HealthChecks.UI.Data" Version="9.0.0" />
|
||||||
|
<PackageVersion Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="9.0.0" />
|
||||||
|
|
||||||
|
<!-- Observability & OpenTelemetry -->
|
||||||
|
<PackageVersion Include="OpenTelemetry" Version="1.20.1" />
|
||||||
|
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.20.1" />
|
||||||
|
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.20.1" />
|
||||||
|
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.20.1" />
|
||||||
|
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.20.1" />
|
||||||
|
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.20.1" />
|
||||||
|
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.20.1" />
|
||||||
|
|
||||||
|
<!-- Email & Messaging -->
|
||||||
|
<PackageVersion Include="MailKit" Version="4.18.2" />
|
||||||
|
<PackageVersion Include="MimeKit" Version="4.18.2" />
|
||||||
|
|
||||||
|
<!-- CQRS & Mediator -->
|
||||||
|
<PackageVersion Include="FluentResults" Version="4.0.0" />
|
||||||
|
<PackageVersion Include="Mediator.Abstractions" Version="3.0.2" />
|
||||||
|
<PackageVersion Include="Mediator.SourceGenerator" Version="3.0.2" />
|
||||||
|
|
||||||
|
<!-- Cloud Storage -->
|
||||||
|
<PackageVersion Include="AWSSDK.Extensions.NetCore.Setup" Version="4.0.102.7" />
|
||||||
|
<PackageVersion Include="AWSSDK.S3" Version="4.0.104.7" />
|
||||||
|
|
||||||
|
<!-- Analyzers -->
|
||||||
|
<PackageVersion Include="Meziantou.Analyzer" Version="3.0.317" />
|
||||||
|
|
||||||
|
<!-- Testing -->
|
||||||
|
<PackageVersion Include="coverlet.collector" Version="10.1.0" />
|
||||||
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.11.0" />
|
||||||
|
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||||
|
<PackageVersion Include="xunit.runner.visualstudio" Version="4.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
|
|
||||||
WORKDIR /app
|
|
||||||
EXPOSE 80
|
|
||||||
EXPOSE 443
|
|
||||||
|
|
||||||
# Optional: Keep only if your API build explicitly requires Node compilation tasks
|
|
||||||
FROM node:22-bookworm-slim AS node
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS build
|
|
||||||
ARG TARGETARCH
|
|
||||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 \
|
|
||||||
NUGET_XMLDOC_MODE=skip
|
|
||||||
WORKDIR /app_source
|
|
||||||
|
|
||||||
# 1. Copy everything over
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# 2. Restore all projects using the solution context (This works perfectly)
|
|
||||||
RUN dotnet restore "PostFundManagement.slnx"
|
|
||||||
|
|
||||||
# 3. Move directly to the publish stage (Skipping the broken dotnet build step)
|
|
||||||
FROM build AS publish
|
|
||||||
WORKDIR "/app_source/PostFundManagement.Api"
|
|
||||||
RUN dotnet publish "PostFundManagement.Api.csproj" -c Release --no-restore -o /app/publish
|
|
||||||
|
|
||||||
# 4. Final runtime assembly
|
|
||||||
FROM base AS final
|
|
||||||
WORKDIR /app
|
|
||||||
COPY --from=publish /app/publish .
|
|
||||||
ENV ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
|
|
||||||
ENTRYPOINT ["dotnet", "PostFundManagement.Api.dll"]
|
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
<mxfile host="Electron">
|
||||||
|
<diagram name="Page-1" id="lj056Mnt25KX5hBf4p9v">
|
||||||
|
<mxGraphModel dx="1063" dy="686" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1600" pageHeight="900" math="0" shadow="0">
|
||||||
|
<root>
|
||||||
|
<mxCell id="0" />
|
||||||
|
<mxCell id="1" parent="0" />
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-42" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" value="" vertex="1">
|
||||||
|
<mxGeometry height="700" width="952" x="340" y="130" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-1" parent="1" style="rounded=0;whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;fillColor=#008a00;fontColor=#ffffff;strokeColor=#005700;" value="<b style="color: rgb(255, 255, 255);">Persistance</b>" vertex="1">
|
||||||
|
<mxGeometry height="70" width="650" x="475" y="730" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-2" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="PostgresSQL" vertex="1">
|
||||||
|
<mxGeometry height="45" width="120" x="500" y="742.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-3" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="Redis" vertex="1">
|
||||||
|
<mxGeometry height="45" width="120" x="740" y="742.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-4" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="Garage S3" vertex="1">
|
||||||
|
<mxGeometry height="45" width="120" x="980" y="742.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-5" parent="1" style="rounded=0;whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;fillColor=#e51400;fontColor=#ffffff;strokeColor=#B20000;" value="<span style="color: rgb(255, 255, 255);"><b>Enterprise Integration Elements</b></span>" vertex="1">
|
||||||
|
<mxGeometry height="70" width="650" x="475" y="580" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-7" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="Skoruba Identity API" vertex="1">
|
||||||
|
<mxGeometry height="45" width="120" x="500" y="592.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-8" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="Skoruba STS" vertex="1">
|
||||||
|
<mxGeometry height="45" width="120" x="658" y="592.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-10" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="Email" vertex="1">
|
||||||
|
<mxGeometry height="45" width="120" x="815" y="592.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-55" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-11" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" target="G6m14uD4gVQkRd8Mz5xj-10">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-56" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-11" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0;entryDx=0;entryDy=0;dashed=1;dashPattern=1 4;" target="G6m14uD4gVQkRd8Mz5xj-3">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-57" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-11" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;dashed=1;dashPattern=1 4;" target="G6m14uD4gVQkRd8Mz5xj-2">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-58" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-11" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;dashPattern=1 4;" target="G6m14uD4gVQkRd8Mz5xj-4">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-11" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="Quartz Scheduler" vertex="1">
|
||||||
|
<mxGeometry height="45" width="120" x="980" y="592.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-12" parent="1" style="rounded=0;whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;fillColor=#fa6800;fontColor=#000000;strokeColor=#C73500;" value="<b style="color: rgb(255, 255, 255);">Integration &amp; Services</b>" vertex="1">
|
||||||
|
<mxGeometry height="70" width="650" x="475" y="440" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-51" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-13" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;dashPattern=1 4;" target="G6m14uD4gVQkRd8Mz5xj-7">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-52" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-13" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;dashPattern=1 4;" target="G6m14uD4gVQkRd8Mz5xj-11">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-53" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-13" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.538;entryY=0.073;entryDx=0;entryDy=0;dashed=1;dashPattern=1 4;" target="G6m14uD4gVQkRd8Mz5xj-8">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-61" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-13" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=1;exitDx=0;exitDy=0;entryX=0.333;entryY=0.167;entryDx=0;entryDy=0;dashed=1;dashPattern=1 4;" target="G6m14uD4gVQkRd8Mz5xj-2">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-62" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-13" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;dashed=1;dashPattern=1 4;" target="G6m14uD4gVQkRd8Mz5xj-3">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-13" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="PFM API" vertex="1">
|
||||||
|
<mxGeometry height="45" width="62" x="500" y="452.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-14" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;dashed=1;fillColor=none;fontColor=#000000;strokeColor=#000000;strokeWidth=2;" value="PFM MCP" vertex="1">
|
||||||
|
<mxGeometry height="45" width="72" x="580" y="452.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-15" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="" vertex="1">
|
||||||
|
<mxGeometry height="45" width="284" x="816" y="452.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-16" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="Governance" vertex="1">
|
||||||
|
<mxGeometry height="20" width="80" x="830" y="465" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-17" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="Evidence" vertex="1">
|
||||||
|
<mxGeometry height="20" width="62" x="918" y="465" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-18" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="Grants" vertex="1">
|
||||||
|
<mxGeometry height="20" width="42" x="988" y="465" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-19" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="Projects" vertex="1">
|
||||||
|
<mxGeometry height="20" width="52" x="1038" y="465" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-20" parent="1" style="rounded=0;whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;fillColor=#0050ef;fontColor=#ffffff;strokeColor=#001DBC;" value="<b>Presentation</b>" vertex="1">
|
||||||
|
<mxGeometry height="70" width="650" x="475" y="300" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-49" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-21" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;dashPattern=1 4;" target="G6m14uD4gVQkRd8Mz5xj-13">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-21" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="PFM Console (Web)" vertex="1">
|
||||||
|
<mxGeometry height="45" width="120" x="500" y="312.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-48" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-22" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;startArrow=classic;startFill=1;dashed=1;dashPattern=1 4;" target="G6m14uD4gVQkRd8Mz5xj-26">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-50" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-22" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;dashed=1;dashPattern=1 4;" target="G6m14uD4gVQkRd8Mz5xj-13">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-22" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="PFM Partner (Web)" vertex="1">
|
||||||
|
<mxGeometry height="45" width="120" x="640" y="312.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-23" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;dashed=1;fillColor=none;fontColor=#000000;strokeColor=#000000;strokeWidth=2;" value="PFM Console Mobile" vertex="1">
|
||||||
|
<mxGeometry height="45" width="130" x="830" y="312.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-24" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;dashed=1;fillColor=none;fontColor=#000000;strokeColor=#000000;strokeWidth=2;" value="PFM Partner Mobile" vertex="1">
|
||||||
|
<mxGeometry height="45" width="120" x="980" y="312.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-26" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="Khongisa CDN" vertex="1">
|
||||||
|
<mxGeometry height="45" width="102" x="668" y="452.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-34" parent="1" style="rounded=0;whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;fillColor=#e3c800;fontColor=#000000;strokeColor=#B09500;" value="<span style="color: rgb(255, 255, 255);"><b>Consumers / Users</b></span>" vertex="1">
|
||||||
|
<mxGeometry height="70" width="650" x="475" y="160" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-25" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="3rd Party AI" vertex="1">
|
||||||
|
<mxGeometry height="45" width="80" x="1020" y="172.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-37" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;dashed=1;fillColor=none;fontColor=#000000;strokeColor=#000000;strokeWidth=2;" value="PFM AI" vertex="1">
|
||||||
|
<mxGeometry height="45" width="62" x="950" y="172.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-46" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-38" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.33;exitY=0.996;exitDx=0;exitDy=0;entryX=0.333;entryY=0.167;entryDx=0;entryDy=0;dashed=1;dashPattern=1 4;" target="G6m14uD4gVQkRd8Mz5xj-21">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-38" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="Private Funding Organisations" vertex="1">
|
||||||
|
<mxGeometry height="45" width="180" x="500" y="172.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-47" edge="1" parent="1" source="G6m14uD4gVQkRd8Mz5xj-39" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;dashed=1;dashPattern=1 4;" target="G6m14uD4gVQkRd8Mz5xj-22">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-39" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;" value="Funding Applicants" vertex="1">
|
||||||
|
<mxGeometry height="45" width="120" x="690" y="172.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-40" parent="1" style="rounded=1;whiteSpace=wrap;html=1;roundedPerimeter=1;dashed=1;fillColor=none;fontColor=#000000;strokeColor=#000000;strokeWidth=2;" value="Data Aggregators" vertex="1">
|
||||||
|
<mxGeometry height="45" width="110" x="820" y="172.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-41" edge="1" parent="1" style="shape=flexArrow;endArrow=classic;startArrow=classic;html=1;rounded=0;fillColor=#1ba1e2;strokeColor=#006EAF;movable=0;resizable=0;rotatable=0;deletable=0;editable=0;locked=1;connectable=0;" value="">
|
||||||
|
<mxGeometry height="100" relative="1" width="100" as="geometry">
|
||||||
|
<mxPoint x="430" y="800" as="sourcePoint" />
|
||||||
|
<mxPoint x="430" y="160" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-44" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;rotation=-90;fontSize=15;movable=0;resizable=0;rotatable=0;deletable=0;editable=0;locked=1;connectable=0;" value="Interaction / Integration / Data Flow" vertex="1">
|
||||||
|
<mxGeometry height="30" width="450" x="170" y="465" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-63" edge="1" parent="1" style="endArrow=none;dashed=1;html=1;strokeWidth=2;rounded=0;" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="340" y="550" as="sourcePoint" />
|
||||||
|
<mxPoint x="1290" y="550" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-64" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Public" vertex="1">
|
||||||
|
<mxGeometry height="30" width="102" x="1190" y="510" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="G6m14uD4gVQkRd8Mz5xj-65" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Private" vertex="1">
|
||||||
|
<mxGeometry height="30" width="102" x="1190" y="570" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
</root>
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
</mxfile>
|
||||||
@@ -0,0 +1,342 @@
|
|||||||
|
# PostFundManagement (PFM) Platform — Architectural Specification
|
||||||
|
|
||||||
|
> **Document Version**: 2.0
|
||||||
|
> **Target Runtimes**: .NET 10.0 (C# 13) | React 19 / TypeScript 5.8 / Vite
|
||||||
|
> **Scope**: System Architecture, Security Topology, RBAC Enforcement, and Maker-Checker Pattern
|
||||||
|
> **Technology Partner & Technical Authorship**: **Lite Charms (PTY) Ltd** is the official technology partner and author of every technical implementation, architecture, and maintenance of the Post-Fund Management (PFM) Platform. **Ifri Technologies** is an initiative and entity within **Lite Charms (PTY) Ltd**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. System Topology & Architectural Layers
|
||||||
|
|
||||||
|
The PFM platform is constructed using **Clean Architecture** and **Domain-Driven Design (DDD)** principles to guarantee strict decoupling of business rules, persistence concerns, and user interfaces.
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ PRESENTATION TIER │
|
||||||
|
│ PostFundManagement.ConsoleUI │ PostFundManagement.PartnerUI │
|
||||||
|
│ (Internal Staff & Governance) │ (External Recipient Portal) │
|
||||||
|
│ React 19 + TypeScript + Vite │ React 19 + WebGL (OGL) + Vite │
|
||||||
|
│ │ │
|
||||||
|
│ PostFundManagement.Mcp (External MCP Server Host - HTTP/SSE & Stdio) │
|
||||||
|
└───────────────────────────────────┬────────────────────────────────────┘
|
||||||
|
│ HTTPS / REST (v1/api/*) + OIDC PKCE
|
||||||
|
┌───────────────────────────────────▼────────────────────────────────────┐
|
||||||
|
│ API GATEWAY │
|
||||||
|
│ PostFundManagement.Api (ASP.NET Core Minimal APIs / Carter) │
|
||||||
|
│ Authentication: JWT Bearer + OpenTelemetry Observability │
|
||||||
|
│ Quartz Client: Scheduler-Only (ZeroSizeThreadPool / Enqueues Jobs) │
|
||||||
|
└───────────────────────────────────┬────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌───────────────────────────────────▼────────────────────────────────────┐
|
||||||
|
│ APPLICATION LAYER │
|
||||||
|
│ PostFundManagement.Application │
|
||||||
|
│ CQRS (Mediator), Business Validators, Domain Services, Event Handlers │
|
||||||
|
└──────────────────┬─────────────────────────────────┬───────────────────┘
|
||||||
|
│ │
|
||||||
|
┌──────────────────▼──────────────────┐ ┌──────────▼───────────────────┐
|
||||||
|
│ DOMAIN LAYER │ │ INFRASTRUCTURE LAYER │
|
||||||
|
│ PostFundManagement.Domain │ │ PostFundManagement.Infrastr.│
|
||||||
|
│ Entities, Value Objects, Enums, │ │ PostgreSQL (EF Core 10), │
|
||||||
|
│ Domain Invariants, Audit Models │ │ Skoruba STS, S3 Garage, │
|
||||||
|
└─────────────────────────────────────┘ │ Redis, Quartz (pfm-scheduler)│
|
||||||
|
└──────────────┬───────────────┘
|
||||||
|
│
|
||||||
|
┌────────────────────────────────────────────────────────▼───────────────┐
|
||||||
|
│ BACKGROUND JOBS WORKER HOST │
|
||||||
|
│ PostFundManagement.Jobs (.NET 10 Background Host / Quartz Runner) │
|
||||||
|
│ Recurring Job Registration (JobsWorker) & Mediated Execution Engine │
|
||||||
|
│ Quartz Runner: Full Execution (DefaultThreadPool MaxConcurrency = 5) │
|
||||||
|
└────────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Security & Identity Architecture
|
||||||
|
|
||||||
|
Authentication and authorization are separated cleanly across system boundaries:
|
||||||
|
|
||||||
|
* **Security Token Service (STS)**: Duende IdentityServer / Skoruba Administration (`https://sts.security.khongisa.co.za`) issues cryptographically signed JWT access tokens using OpenID Connect (OIDC) with Proof Key for Code Exchange (PKCE).
|
||||||
|
* **Audience & Scopes**: Web clients request standard OIDC scopes (`openid`, `profile`, `roles`, `email`, `address`) along with the target API resource scope (`pfm-api-dev`).
|
||||||
|
* **Tenant Isolation**: Every authenticated principal carries a `tenant_id` claim. Multitenant query filters in EF Core enforce cross-tenant partition boundaries at the data access layer.
|
||||||
|
* **Partner Isolation**: Grantees and external recipients possess the `partner` role. The `ConsoleUI` enforces immediate lockout for `partner` roles, restricting external users exclusively to `PartnerUI`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Role-Based Access Control (RBAC) Architecture
|
||||||
|
|
||||||
|
PFM implements a fine-grained, claim-based RBAC model where system permissions are decomposed into **15 operational domains** and **5 progressive action capabilities**.
|
||||||
|
|
||||||
|
### 3.1 Role Claims Inventory
|
||||||
|
|
||||||
|
| Claim Key | Operational Domain | Functional Scope |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `dashboard` | Executive Dashboard | Macro portfolio KPIs, disbursed capital, and ISO 31000 risk heatmap |
|
||||||
|
| `governance` | Governance & Structure | Holding entities, funder portfolios, programmes, rules, and instruments |
|
||||||
|
| `projects_milestones` | Projects & Milestones | Work breakdown structure (WBS), contractual milestones, and activities |
|
||||||
|
| `grant_financials` | Grants & Financials | Statutory funding awards, budget lines, contracts, and disbursement tranches |
|
||||||
|
| `evidence_risks` | M&E, Evidence & Risks | Deliverable evidence verification queue, indicators, and risk registers |
|
||||||
|
| `beneficiaries` | Beneficiaries & Impact | Demographic targets, POPIA compliance items, and outcome deliverables |
|
||||||
|
| `change_management` | Change Management | Project issues, contractual change requests, and budget variations |
|
||||||
|
| `site_visits` | Site Visits | Physical field inspections, checklist observations, and inspector findings |
|
||||||
|
| `settings_invites` | Settings Invites | Onboarding invitations and candidate notifications dispatch |
|
||||||
|
| `settings_users` | Settings Users | User provisioning, role assignments, and account lockouts |
|
||||||
|
| `settings_auditlog` | Settings Audit Log | Immutable audit ledger and system telemetry records |
|
||||||
|
| `settings_notifications`| Settings Notifications | SMTP engines, notification channels, and dispatch telemetry |
|
||||||
|
| `tenant_self_service` | Tenant Self-Service | Tenant profile configuration and delegated sub-user management |
|
||||||
|
| `tenant_management` | Tenant Management | Multi-tenant directory, partition allocation, and solution ownership |
|
||||||
|
| `unrestricted` | System Administration | Root administrative bypass across all system partitions |
|
||||||
|
|
||||||
|
### 3.2 Claim Actions & Capability Hierarchy
|
||||||
|
|
||||||
|
Action capabilities within a claim domain follow a strict subset hierarchy:
|
||||||
|
|
||||||
|
$$\text{view} \subset \text{add} \subset \text{update} \quad \text{and} \quad \text{view} \subset \text{export}$$
|
||||||
|
|
||||||
|
* **`view`**: Read-only access to entity lists, detailed records, metrics, and progress scores.
|
||||||
|
* **`add`**: Can create new records, edit draft records, submit milestone evidence, and change workflow statuses *other than approval* (includes `view`).
|
||||||
|
* **`update`**: Authoritative governance capability. Can execute **approval** status transitions (`Approved`, `Active` contracts) on records (includes `view` and `add`).
|
||||||
|
* **`export`**: Can generate and download structured spreadsheets (`.xlsx`) and formal PDF reports (includes `view`).
|
||||||
|
* **`all`**: Comprehensive authority (`view`, `add`, `update`, `export`) over the claim domain.
|
||||||
|
|
||||||
|
### 3.3 Authoritative Role-to-Claims Matrix
|
||||||
|
|
||||||
|
| Role | Assigned Claim Domains | Standard Capabilities | Record Approval Authority (`update`) |
|
||||||
|
| :--- | :--- | :--- | :---: |
|
||||||
|
| **`Admin`** | `unrestricted` | `all` | **Yes (System-wide)** |
|
||||||
|
| **`Executive`** | `dashboard`, `governance`, `projects_milestones`, `grant_financials`, `evidence_risks`, `tenant_self_service` | `all` | **Yes** |
|
||||||
|
| **`Manager`** | `dashboard`, `projects_milestones`, `grant_financials`, `evidence_risks`, `beneficiaries`, `change_management`, `site_visits`, `settings_invites`, `settings_users`, `settings_auditlog`, `settings_notifications`, `tenant_self_service` | `all` | **Yes** |
|
||||||
|
| **`Supervisor`** | `dashboard`, `projects_milestones`, `grant_financials`, `evidence_risks`, `beneficiaries`, `change_management`, `site_visits`, `settings_invites`, `settings_users` | `view, add, update, export`<br>*(Dashboard: `view, export`)* | **Yes** |
|
||||||
|
| **`Contributor`** | `projects_milestones`, `grant_financials`, `evidence_risks`, `beneficiaries`, `change_management`, `site_visits`, `settings_invites` | **`view, add`** | ❌ **Strictly Prohibited** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Maker-Checker Pattern Architecture
|
||||||
|
|
||||||
|
To eliminate fraud risk, self-dealing, and conflicts of interest, PFM enforces the **Maker-Checker Pattern** (Segregation of Duties / Dual Authorization).
|
||||||
|
|
||||||
|
### 4.1 Core Principle
|
||||||
|
> **The user who creates or drafts a record (the "Maker") is strictly prohibited from verifying or approving that record (the "Checker"), regardless of their administrative role or RBAC claim privileges.**
|
||||||
|
|
||||||
|
Even if an `Executive`, `Manager`, or `Supervisor` possesses the `update` claim action on a domain, they cannot approve a record if they are identified as the record's creator.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A["Maker (User A)<br/>Creates Record"] --> B["Record Persisted<br/>with createdBy = User A"]
|
||||||
|
B --> C{"Checker Evaluation<br/>(Approver Login)"}
|
||||||
|
C -->|"Checker is User A"| D["Approval BLOCKED: Maker-Checker Violation"]
|
||||||
|
C -->|"Checker is User B with update claim"| E["Approval GRANTED: Status Approved"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 Record Attribution Model
|
||||||
|
Every operational domain entity immutably captures the creator identity:
|
||||||
|
* `createdBy`: Cryptographic user identifier (Guid / Subject ID) populated from the verified token claim `context.GetUserId()` upon creation.
|
||||||
|
* `approvedBy`: User identifier of the independent verifier populated upon successful transition to `Approved`.
|
||||||
|
|
||||||
|
### 4.3 Client-Side Enforcement Hooks & Helpers
|
||||||
|
In `PostFundManagement.ConsoleUI`, the Maker-Checker rule is evaluated dynamically via [`useRbac()`](file:///c:/Users/Admin/source/repos/PostFundManagement/PostFundManagement.ConsoleUI/src/auth/rbac.ts):
|
||||||
|
|
||||||
|
* **`isMaker(createdBy)`**: Compares `createdBy` against current user `user.id`, `sub`, and `user.email`.
|
||||||
|
* **`canApproveRecord(claim, createdBy)`**: Evaluates:
|
||||||
|
$$\text{canApproveRecord} = \text{canApprove}(\text{claim}) \land \neg\text{isMaker}(\text{createdBy})$$
|
||||||
|
* **`getApprovalRestrictionReason(claim, createdBy)`**: Surfaces explanatory feedback:
|
||||||
|
- `"Requires update/approval permission"` if the claim lacks `update`.
|
||||||
|
- `"Maker-Checker policy: Record creators cannot approve their own records"` if `isMaker` is true.
|
||||||
|
|
||||||
|
### 4.4 UI Guard Implementations
|
||||||
|
1. **Status Dropdowns**: The `Approved` option in status `<select>` controls is disabled with contextual labels:
|
||||||
|
```tsx
|
||||||
|
<option value={ApprovalStatus.Approved} disabled={!canApproveRecord('projects_milestones', record.createdBy)}>
|
||||||
|
Approved {isMaker(record.createdBy) ? '(Maker-Checker: Cannot approve own record)' : ''}
|
||||||
|
</option>
|
||||||
|
```
|
||||||
|
2. **Action Buttons**: Fast-track "Approve" buttons (e.g. Budget Approval, Disbursement Authorisation, Evidence Review) are hidden or accompanied by a Maker-Checker indicator badge when viewed by the creator.
|
||||||
|
3. **Export Controls**: `<ExportButton>` enforces domain claim checks explicitly (`claim="grant_financials"`), rejecting unauthenticated exports.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Caching Strategy, Mutation Versioning & Performance Optimization
|
||||||
|
|
||||||
|
To achieve sub-50ms page transitions while guaranteeing data freshness across mutations in a distributed architecture, PFM employs a multi-tiered caching topology.
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ REACT QUERY CACHE (In-Memory Browser Tier) │
|
||||||
|
│ staleTime: 2m (dynamic) | 5m (taxonomy) | gcTime: 10m │
|
||||||
|
└───────────────────────────────────┬────────────────────────────────────┘
|
||||||
|
│ Cache Miss / Background Refetch
|
||||||
|
┌───────────────────────────────────▼────────────────────────────────────┐
|
||||||
|
│ AXIOS CLIENT INTERCEPTOR & MUTATION VERSIONING (_v) │
|
||||||
|
│ Reads/Browsing: attaches active `_v` epoch token │
|
||||||
|
│ Mutations (POST/PUT/PATCH/DELETE): auto-bumps epoch -> new `_v` token │
|
||||||
|
└───────────────────────────────────┬────────────────────────────────────┘
|
||||||
|
│ HTTP GET /v1/api/*?_v=<epoch>
|
||||||
|
┌───────────────────────────────────▼────────────────────────────────────┐
|
||||||
|
│ ASP.NET CORE OUTPUTCACHE (Redis / Memory Tier) │
|
||||||
|
│ Base Policy: 2-minute expiration partitioned by URI + QueryString │
|
||||||
|
│ Hits same `_v` -> 2ms response | New `_v` -> DB query & cache refresh │
|
||||||
|
└────────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.1 The OutputCache Stale Data Challenge
|
||||||
|
ASP.NET Core Minimal APIs in `PostFundManagement.Api` attach `.CacheOutput()` to GET endpoints, using a 2-minute base sliding expiration backed by Redis / Memory cache.
|
||||||
|
|
||||||
|
Because OutputCache keys are generated from the HTTP Method, Host, Path, and **Query String**, a naive client refetch after a mutation (e.g. `queryClient.invalidateQueries({ queryKey: ['projects'] })`) calls `GET /v1/api/projects`. OutputCache intercepts this identical URI, returning the pre-mutation response cached earlier. The updated project status or approved milestone does not reflect in the UI until the 2-minute cache expires.
|
||||||
|
|
||||||
|
### 5.2 Client-Side Mutation Epoch Versioning
|
||||||
|
PFM solves this without modifying backend endpoints via **Client-Side Mutation Versioning**:
|
||||||
|
1. **Epoch Tracking (`activeMutationEpoch`)**: `httpClient.ts` tracks an active mutation timestamp initialized from `sessionStorage` or `Date.now()`.
|
||||||
|
2. **Automatic Mutation Interceptor**: Every successful mutating HTTP response (`POST`, `PUT`, `PATCH`, `DELETE` with HTTP 2xx) triggers `bumpMutationEpoch()`.
|
||||||
|
3. **Query Parameter Injection**: Outgoing `GET` requests to internal APIs automatically append `_v: getMutationEpoch()`.
|
||||||
|
4. **Instant Invalidation**: When a mutation succeeds, the epoch is bumped. Subsequent React Query refetches send the new `_v`, producing an immediate **cache miss** in ASP.NET Core OutputCache. The backend queries PostgreSQL, returns fresh data, and caches the new state under the new `_v` key.
|
||||||
|
5. **Cache Hit Snappiness**: Navigations between pages while no mutations have occurred reuse the stable `_v`, yielding sub-5ms responses from Redis/Memory OutputCache and 0ms from React Query in-memory cache.
|
||||||
|
6. **Explicit Freshness (`_t`)**: When `Cache-Control: no-cache` is specified or during explicit refresh operations, a unique timestamp `_t` is attached to force a guaranteed fresh fetch.
|
||||||
|
|
||||||
|
### 5.3 RBAC Startup Pipeline Acceleration
|
||||||
|
Following the introduction of fine-grained RBAC and Maker-Checker checks, startup performance was streamlined to eliminate sequential network waterfalls:
|
||||||
|
* **Cache-First Role Claims**: `fetchAndCacheSystemRoles` and `fetchAndCacheRoleClaims` check in-memory and `localStorage` caches *before* dispatching network calls.
|
||||||
|
* **Parallel User Resolution**: `getUserRolesFromApi` and `getUserClaimsFromApi` are resolved concurrently using `Promise.allSettled`.
|
||||||
|
* **Parallel Claim Resolution**: Claims across all assigned roles are retrieved concurrently rather than in serial loops.
|
||||||
|
* **Immediate Non-Blocking Session Render**: If valid session tokens and user profiles exist in `localStorage`, `AuthProvider` immediately unblocks UI rendering (`isLoading: false`) and runs tenant lockout checks, account synchronization, and claim revalidation asynchronously in the background.
|
||||||
|
* **In-Memory User Sync & Lockout Caching**: `isUserSyncedInBrowser` and `verifyTenantLockout` maintain in-memory references to eliminate synchronous `JSON.parse` operations from the request pipeline.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Distributed Background Job Processing Architecture (PostFundManagement.Jobs)
|
||||||
|
|
||||||
|
The PFM platform decouples synchronous API request handling from asynchronous, long-running, and scheduled workloads using a distributed **Quartz.NET (v4.6+)** clustering topology backed by PostgreSQL (`pfm-scheduler`).
|
||||||
|
|
||||||
|
### 6.1 Split-Node Topology: Scheduler Client vs. Execution Host
|
||||||
|
|
||||||
|
To maintain high API throughput, low memory footprint, and complete process isolation, PFM implements a split-node architecture between `PostFundManagement.Api` and `PostFundManagement.Jobs`:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────┐
|
||||||
|
│ PostFundManagement.Api │
|
||||||
|
│ (API Gateway / Scheduler Client) │
|
||||||
|
│ │
|
||||||
|
│ * AddQuartzSchedulerClient() │
|
||||||
|
│ * ZeroSizeThreadPool (0 execution threads) │
|
||||||
|
│ * SchedulerStatus.Created (Standby mode) │
|
||||||
|
│ * Strictly schedules / enqueues events │
|
||||||
|
│ * Never acquires triggers or executes jobs │
|
||||||
|
└──────────────────────┬───────────────────────┘
|
||||||
|
│ Schedules IEvent jobs via IJobOrchestrator
|
||||||
|
▼
|
||||||
|
┌──────────────────────────────────────────────┐
|
||||||
|
│ PostgreSQL JobStore │
|
||||||
|
│ (pfm-scheduler) │
|
||||||
|
│ │
|
||||||
|
│ * Clustered tables (QRTZ_TRIGGERS, etc.) │
|
||||||
|
│ * System.Text.Json serialized IEvent payload│
|
||||||
|
│ * Distributed locking & check-in heartbeat │
|
||||||
|
└──────────────────────▲───────────────────────┘
|
||||||
|
│
|
||||||
|
│ Acquires triggers & executes (Polling loop)
|
||||||
|
┌──────────────────────┴───────────────────────┐
|
||||||
|
│ PostFundManagement.Jobs │
|
||||||
|
│ (Dedicated Worker Service) │
|
||||||
|
│ │
|
||||||
|
│ * AddQuartzScheduler() │
|
||||||
|
│ * DefaultThreadPool (MaxConcurrency = 5) │
|
||||||
|
│ * SchedulerStatus.Running (Started loop) │
|
||||||
|
│ * JobsWorker background service: │
|
||||||
|
│ - Registers recurring cron jobs on start │
|
||||||
|
│ - Enqueues one-time jobs when needed │
|
||||||
|
│ * Executes MediatorJob<TNotification> │
|
||||||
|
│ * Deserializes and publishes via IMediator │
|
||||||
|
└──────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
| Characteristic | `PostFundManagement.Api` | `PostFundManagement.Jobs` |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| **Architectural Role** | API Gateway & Event Producer | Dedicated Execution Daemon & Consumer |
|
||||||
|
| **Registration Extension** | `AddQuartzSchedulerClient(...)` | `AddQuartzScheduler(...)` |
|
||||||
|
| **Thread Pool** | `ZeroSizeThreadPool` (0 threads) | `DefaultThreadPool` (`MaxConcurrency = 5`) |
|
||||||
|
| **Hosted Service** | None (`AddQuartzHostedService` omitted) | `AddQuartzHostedService(WaitForJobsToComplete = true)` |
|
||||||
|
| **Lifecycle State** | `SchedulerStatus.Created` (Standby) | `SchedulerStatus.Running` (Active execution loop) |
|
||||||
|
| **Readiness Check** | Verifies DB connectivity without requiring `Running` | Verifies active execution engine & DB connectivity |
|
||||||
|
| **Trigger Acquisition** | ❌ **Disabled** (Cannot acquire triggers or run jobs) | ✅ **Active** (Polls DB, acquires triggers, runs jobs) |
|
||||||
|
|
||||||
|
### 6.2 Event-Driven Mediated Orchestration (`IEvent` & `MediatorJob`)
|
||||||
|
|
||||||
|
Background tasks are modeled as strongly-typed domain events implementing `IEvent : INotification` (`PostFundManagement.Domain.Abstractions`).
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
autonumber
|
||||||
|
participant API as PostFundManagement.Api
|
||||||
|
participant Orchestrator as IJobOrchestrator
|
||||||
|
participant DB as PostgreSQL pfm-scheduler
|
||||||
|
participant Host as PostFundManagement.Jobs
|
||||||
|
participant QuartzThread as Quartz Execution Engine
|
||||||
|
participant Mediator as IMediator MediatorJob
|
||||||
|
participant Handler as Domain Event Handlers
|
||||||
|
|
||||||
|
Note over API,Orchestrator: 1. Event Scheduling (API Node)
|
||||||
|
API->>Orchestrator: SendAsync(notification) / ScheduleAsync(notification, cron)
|
||||||
|
Orchestrator->>DB: Stores JobDetail and Trigger with serialized Payload JSON
|
||||||
|
Note over DB,Host: 2. Distributed Acquisition (Jobs Node)
|
||||||
|
QuartzThread->>DB: Polls and acquires due trigger with row-level locks
|
||||||
|
QuartzThread->>Host: Dispatches to local thread pool
|
||||||
|
Host->>Mediator: Instantiates MediatorJob[TNotification]
|
||||||
|
Mediator->>Mediator: Deserializes payload from MergedJobDataMap
|
||||||
|
Mediator->>Handler: Publish(notification, cancellationToken)
|
||||||
|
Handler-->>Host: Execution complete and updates business data in pfm DB
|
||||||
|
Host->>DB: Releases trigger and commits execution state
|
||||||
|
```
|
||||||
|
|
||||||
|
1. **Scheduling via `IJobOrchestrator`**:
|
||||||
|
- `SendAsync<TNotification>(notification)`: Schedules a durable, one-time execution in the `onetime-jobs` group with `StartNow()` and `DisallowConcurrentExecution()`.
|
||||||
|
- `ScheduleAsync<TNotification>(notification, cronExpression)`: Configures a durable, cron-driven recurring job in the `scheduled-jobs` group with automatic misfire handling (`CronTriggerMisfireInstruction.IgnoreMisfires`).
|
||||||
|
- `InterruptAsync(eventName, correlationId)`: Allows runtime cancellation of active job executions across the cluster.
|
||||||
|
2. **Transparent Deserialization & Dispatch (`MediatorJob<TNotification>`)**:
|
||||||
|
`MediatorJob<TNotification>` resolves `IMediator` from DI, deserializes the JSON payload, initiates OpenTelemetry distributed tracing (`MediatorTelemetry.Source`), and publishes the event to all registered in-process handlers.
|
||||||
|
|
||||||
|
### 6.3 Recurring vs. One-Time Job Patterns
|
||||||
|
|
||||||
|
* **Recurring Jobs**: Scheduled by `JobsWorker` (`BackgroundService` in `PostFundManagement.Jobs`) during application startup. These jobs govern periodic health sweeps, SLA tracking, notification batching, and risk evaluations on predictable cron intervals.
|
||||||
|
* **One-Time Jobs**: Primarily scheduled by `PostFundManagement.Api` as immediate or deferred side-effects of HTTP requests and lifecycle mutations (e.g. user invites, award registration triggers). Furthermore, a running recurring job in `PostFundManagement.Jobs` can dispatch a one-time job (`jobOrchestrator.SendAsync`) when handling fan-out tasks or invoking decoupled activities that cannot be triggered directly by the originating job.
|
||||||
|
|
||||||
|
### 6.4 Core Background Processing Capabilities
|
||||||
|
|
||||||
|
The background job runner powers four essential operational workloads:
|
||||||
|
|
||||||
|
1. **Automated Project Monitoring & Reminders**:
|
||||||
|
- Sweeps active projects, contractual deliverables, and milestone deadlines.
|
||||||
|
- Computes planned vs. actual progress variance and escalates overdue milestones (`FR-M04-017`, `FR-M07-009`).
|
||||||
|
- Dispatches automated reminders to Grantees/Partners prior to milestone due dates and submission cut-offs.
|
||||||
|
2. **Risk Monitoring & Automated Notifications**:
|
||||||
|
- Evaluates project risk registers, Key Risk Indicators (KRIs), and issue escalation thresholds (`FR-M09-001` - `011`).
|
||||||
|
- Detects risk score creep (Likelihood $\times$ Impact) and triggers automated alerts when risk tolerance thresholds are breached.
|
||||||
|
- Monitors overdue mitigation action plans and escalates unresolved high-severity risks to Programme Managers.
|
||||||
|
3. **Email Notification Processing**:
|
||||||
|
- Continuously consumes and batches pending notification records (`Platform.Email` / `NotificationPlatform.Email`).
|
||||||
|
- Dispatches transactional emails via MailKit / MimeKit SMTP with exponential backoff and retry policies.
|
||||||
|
- Updates delivery status (`Sent`, `Failed`, retry count) and logs dispatch telemetry for audit compliance (`FR-M25-001`).
|
||||||
|
4. **Platform Extensibility**:
|
||||||
|
- New background jobs and recurring routines can be introduced by creating an `IEvent` model in `PostFundManagement.Application` and scheduling it via `JobsWorker` or `IJobOrchestrator`, with zero impact on the API surface.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Model Context Protocol (MCP) Architectural Specification
|
||||||
|
|
||||||
|
### 11.1 Architectural Shift & Philosophy
|
||||||
|
The PFM platform integrates the **Model Context Protocol (MCP)** standard (v2.3.0) to expose domain capabilities both internally and externally while preventing token exhaustion and eliminating network call overhead:
|
||||||
|
|
||||||
|
1. **Dual Realisation Pattern**:
|
||||||
|
* **In-App MCP (`PostFundManagement.Api` & `PostFundManagement.Jobs`)**: Internal features, background jobs, and workflows obtain in-process access to MCP tooling via `IInAppMcpClient`. Calls are executed directly against scoped services with zero network latency, zero serialization overhead, and no external web requests.
|
||||||
|
* **External MCP Server (`PostFundManagement.Mcp`)**: A dedicated standalone presentation host exposes the exact same tools over standard external transports (HTTP/SSE at `/mcp` and command-line Stdio via `--stdio`) for external LLMs, agentic workbenches, and client IDEs.
|
||||||
|
|
||||||
|
2. **Application.Services Proxying Pattern**:
|
||||||
|
All tools reside in `PostFundManagement.Application/Mcp` and proxy established `Application.Services` interfaces (`IGrantsService`, `IProjectService`, `IExecutionService`, `IGovernanceService`, `IEvidenceService`, `ICommunicationsService`, `IUserService`). This ensures:
|
||||||
|
* **Single Source of Truth (BR-005)**: Business validation, entity mapping, and audit logging happen identically whether called from an HTTP endpoint, an internal in-app MCP consumer, or an external MCP server.
|
||||||
|
* **Domain Groupings**: Tools are partitioned by service domains (`Grants`, `Projects`, `Execution`, `Governance`, `Evidence`, `Communications`, `Users`), providing clean tool namespaces (e.g., `grants_create_award`, `execution_approve_disbursement`).
|
||||||
|
* **Token Usage Optimization**: Responses are wrapped in concise `McpToolResult<T>` records, filtering out internal framework metadata and returning concise JSON structures to minimize LLM context window consumption.
|
||||||
|
|
||||||
|
3. **Current Agentic Boundary**:
|
||||||
|
The platform establishes the full tooling layer and in-app client contracts; no autonomous agents are defined in this release, preserving clear architectural boundaries for scheduled future agent implementations.
|
||||||
|
After Width: | Height: | Size: 2.3 MiB |
|
After Width: | Height: | Size: 2.8 MiB |
|
After Width: | Height: | Size: 2.7 MiB |
|
After Width: | Height: | Size: 2.3 MiB |
|
After Width: | Height: | Size: 2.3 MiB |
|
After Width: | Height: | Size: 2.7 MiB |
@@ -0,0 +1,247 @@
|
|||||||
|
# PostFundManagement (PFM) Platform — Operational Processes & Governance (Processes.md)
|
||||||
|
|
||||||
|
> **Document Context**: Authoritative operational manual and process specification for the PFM Platform. Derived from the PFM SRS v2.0 and governed by **Lite Charms (PTY) Ltd** (official technology partner and author of every technical implementation, architecture, and maintenance of the system, with **Ifri Technologies** operating within Lite Charms (PTY) Ltd).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. PFM End-to-End Operational Lifecycle
|
||||||
|
|
||||||
|
The PFM platform governs the lifecycle of grant, development, innovation, and impact funding portfolios from the moment an award is approved through long-term post-fund monitoring.
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ POST-FUND LIFECYCLE │
|
||||||
|
│ │
|
||||||
|
│ [1. Award Intake] ──> [2. Contracting] ──> [3. Onboarding] │
|
||||||
|
│ External Approval Draft Agreement KYC & Banking Gate │
|
||||||
|
│ Recorded (BR-001) Auto-created (BR-002) Required for Disb. (BR-003) │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ [4. Project & WBS] ──> [5. Budget & Lines] ──> [6. Disbursement Gate] │
|
||||||
|
│ Milestones & Tasks Allocations & Caps Requires Verified Evidence │
|
||||||
|
│ Progress Tracking No Over-commitment & Satisfied Conditions (BR-004) │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ [7. M&E & Evidence] ─> [8. Change & Risk] ──> [9. Close-Out & Impact] │
|
||||||
|
│ Indicator Actuals Variations & Issues Post-Fund Monitoring │
|
||||||
|
│ Field Site Visits Audit Trail (BR-007) Portfolio Roll-Up (BR-010) │
|
||||||
|
└────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Core Operational Processes
|
||||||
|
|
||||||
|
### 2.1 Process 1: Award Intake & Funding Registration (Module M01)
|
||||||
|
* **Pre-condition**: Award has been approved by external sanctioning committee (BR-001).
|
||||||
|
* **Actor**: Funding Administrator (`grant_financials: add`).
|
||||||
|
* **Workflow**:
|
||||||
|
1. Capture external reference number, award amount ($> 0$), funding period, programme, and recipient organisation.
|
||||||
|
2. System enforces duplicate check against external reference numbers.
|
||||||
|
3. Upon successful award creation, the system **automatically initializes** the corresponding legal `Contract` in `Draft` status (BR-002).
|
||||||
|
|
||||||
|
### 2.2 Process 2: Contracting & Agreement Management (Module M02)
|
||||||
|
* **Pre-condition**: Valid award record exists.
|
||||||
|
* **Actors**: Legal / Programme Manager (`grant_financials: add`), Executive / Authorised Signatory (`grant_financials: update`).
|
||||||
|
* **Workflow**:
|
||||||
|
1. Author attaches legal terms, clauses, total value, and effective/expiry dates.
|
||||||
|
2. Upload signed bilateral agreement PDF.
|
||||||
|
3. Transition contract status to `Active`.
|
||||||
|
4. **Maker-Checker Rule**: The user who uploaded or created the contract cannot be the signatory who transitions it to `Active`.
|
||||||
|
|
||||||
|
### 2.3 Process 3: Project WBS & Milestones Management (Module M04 & M07)
|
||||||
|
* **Pre-condition**: Project is in `Active` status.
|
||||||
|
* **Actors**: Contributor / Project Manager (`projects_milestones: add`), Supervisor / Manager (`projects_milestones: update`).
|
||||||
|
* **Workflow**:
|
||||||
|
1. Project Manager defines contractual milestones with due dates and priority levels.
|
||||||
|
2. Contributors log ground activities against milestones.
|
||||||
|
3. Contributor uploads completion evidence.
|
||||||
|
4. Status transitions from `Planned` $\to$ `In Progress` $\to$ `Submitted` $\to$ `Under Review` $\to$ `Verified & Approved`.
|
||||||
|
5. **Verification Gate**: Only users with `projects_milestones: update` who **did not create** the milestone can verify and approve it.
|
||||||
|
|
||||||
|
### 2.4 Process 4: Disbursement Control & Payment Release (Module M05 & M06)
|
||||||
|
* **Pre-condition**: Recipient onboarding complete, banking details verified, and linked milestone deliverable verified.
|
||||||
|
* **Actors**: Finance Officer (`grant_financials: add`), Finance Director / Manager (`grant_financials: update`).
|
||||||
|
* **Workflow**:
|
||||||
|
1. Finance Officer drafts disbursement tranche drawdown against verified milestone.
|
||||||
|
2. System validates conditions precedent and budget caps (commits + expenditures $\le$ approved budget line).
|
||||||
|
3. Authoriser verifies evidence and approves tranche release.
|
||||||
|
4. **Maker-Checker Rule**: The user who captured the payment request cannot approve the disbursement.
|
||||||
|
|
||||||
|
### 2.5 Process 5: M&E Indicators, Risks & Evidence Review (Module M08 & M09)
|
||||||
|
* **Pre-condition**: Approved project indicators and risk registers configured.
|
||||||
|
* **Actors**: M&E Officer (`evidence_risks: add`), Programme Supervisor (`evidence_risks: update`).
|
||||||
|
* **Workflow**:
|
||||||
|
1. Field staff or recipients submit indicator actuals and supporting verification files (`EvidenceType`).
|
||||||
|
2. Evidence enters the `EvidenceReviewQueue`.
|
||||||
|
3. Supervisor reviews document authenticity, geotags, and deliverable match.
|
||||||
|
4. Decision: `Approve & Verify` or `Reject` (with mandatory rework reasoning).
|
||||||
|
5. **Maker-Checker Rule**: The officer who uploaded the evidence document cannot approve their own submission.
|
||||||
|
|
||||||
|
### 2.6 Process 6: Change Management & Variations (Module M15 & M16)
|
||||||
|
* **Pre-condition**: Active project with identified issue or required contractual variance.
|
||||||
|
* **Actors**: Contributor (`change_management: add`), Manager (`change_management: update`).
|
||||||
|
* **Workflow**:
|
||||||
|
1. Contributor logs project issue or drafts contractual change request (budget reallocation, timeline extension, scope shift).
|
||||||
|
2. Impact assessment conducted.
|
||||||
|
3. Governance deliberation.
|
||||||
|
4. Resolution: `Approved & Enacted` or `Rejected`.
|
||||||
|
5. **Maker-Checker Rule**: Change request initiator cannot approve or enact the change.
|
||||||
|
|
||||||
|
### 2.7 Process 7: Automated Background Workflows & Scheduled Jobs (PostFundManagement.Jobs)
|
||||||
|
* **Pre-condition**: Background jobs host (`PostFundManagement.Jobs`) active and connected to `pfm-scheduler`.
|
||||||
|
* **Actors**: System Engine, `JobsWorker` Daemon, Domain Event Handlers.
|
||||||
|
* **Workflow**:
|
||||||
|
1. **Event Triggering**: API operations emit strongly typed events (`IEvent`) and enqueue them into `pfm-scheduler` via `IJobOrchestrator`.
|
||||||
|
2. **Cron Schedule Execution**: `JobsWorker` initializes recurring schedules on daemon startup for periodic monitoring, risk assessments, and communication batches.
|
||||||
|
3. **Job Execution**: Quartz cluster dispatches acquired triggers to `MediatorJob<TNotification>` within `PostFundManagement.Jobs` (concurrency limit = 5).
|
||||||
|
4. **Mediated Processing**: Event payload is deserialized and published via `IMediator` to execute domain logic (project monitoring, risk threshold checks, email dispatch).
|
||||||
|
5. **Cascading One-Time Jobs**: When a recurring job identifies an action item requiring separate asynchronous execution, it triggers a decoupled one-time job (`jobOrchestrator.SendAsync`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. The Maker-Checker Operational Policy
|
||||||
|
|
||||||
|
### 3.1 Policy Definition
|
||||||
|
Under PFM enterprise governance standards, **dual authorization (Maker-Checker)** is mandatory for all material lifecycle decisions:
|
||||||
|
|
||||||
|
$$\text{Approval Granted} \iff (\text{User has 'update' permission}) \land (\text{User ID} \neq \text{Record Creator ID})$$
|
||||||
|
|
||||||
|
### 3.2 Matrix of Segregated Actions
|
||||||
|
|
||||||
|
| Operational Domain | Maker Action (`add`) | Checker Action (`update`) | Maker-Checker Invariant |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| **Projects & Governance** | Register project, update scope | Approve project governance status | Creator cannot approve project |
|
||||||
|
| **Contractual Milestones** | Define milestone, submit evidence | Verify deliverable, approve milestone | Milestone author cannot verify |
|
||||||
|
| **Disbursements** | Capture tranche drawdown request | Authorize payment release | Requestor cannot release payment |
|
||||||
|
| **Budgets** | Define budget line, allocate funds | Approve statutory budget line | Allocator cannot approve budget |
|
||||||
|
| **Evidence Queue** | Upload proof, ingest documentation | Approve & verify evidence artifact | Submitter cannot verify evidence |
|
||||||
|
| **Change Requests** | Draft scope / budget variation | Approve & enact variation | Initiator cannot enact change |
|
||||||
|
| **Site Visits** | Log field checklist & observations | Approve site inspection report | Inspector cannot self-sign report |
|
||||||
|
| **Programmes** | Create programme structure | Approve programme governance | Creator cannot approve programme |
|
||||||
|
|
||||||
|
### 3.3 System Behavior on Maker-Checker Collisions
|
||||||
|
When an authorised approver navigates to a record they personally created:
|
||||||
|
1. **Dropdown Controls**: The `Approved` option is disabled with the label:
|
||||||
|
`Approved (Maker-Checker: Cannot approve own record)`
|
||||||
|
2. **Action Buttons**: Fast-track approve buttons are hidden, and a visual tag `Maker-Checker (Creator)` is displayed.
|
||||||
|
3. **Audit Trail**: Any attempt to bypass client controls logs a security violation in the immutable audit log (`settings_auditlog`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. RBAC Operational Roles & Boundaries
|
||||||
|
|
||||||
|
### 4.1 Role Assignment Rules
|
||||||
|
1. **`Contributor`**: Operational field execution. Strictly restricted to `view` and `add`. Contributors can create activities, submit evidence, and draft change requests, but **never approve records or export datasets**.
|
||||||
|
2. **`Supervisor`**: Operational oversight. Possesses `view, add, update, export`. Verifies milestones, inspects evidence, reviews site visits, and exports reports, subject to the Maker-Checker rule.
|
||||||
|
3. **`Manager`**: Comprehensive operational management. Possesses `all` across operational domains. Manages audit logs, notifications, and tenancy.
|
||||||
|
4. **`Executive`**: Strategic leadership. Possesses `all` across strategic domains (`dashboard`, `governance`, `grant_financials`, `evidence_risks`).
|
||||||
|
5. **`Admin`**: System administrator. Possesses `unrestricted: all`. Governs tenant boundaries, partitions, and system security.
|
||||||
|
6. **`Partner`**: External grantee / recipient. Strictly confined to `PartnerUI`. Any attempt to access `ConsoleUI` triggers an immediate security lockout.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Caching & Mutation Invalidation Workflow
|
||||||
|
|
||||||
|
To ensure that caching never impedes record lifecycle updates or displays stale data while maximizing UI responsiveness, the following operational flow is enforced across ConsoleUI:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
autonumber
|
||||||
|
actor User as User / Maker
|
||||||
|
participant UI as ConsoleUI React Query
|
||||||
|
participant Client as Axios HttpClient
|
||||||
|
participant OutputCache as Backend OutputCache Redis/Memory
|
||||||
|
participant DB as PostgreSQL Database
|
||||||
|
|
||||||
|
User->>UI: Submits Record Mutation Create/Update/Approve
|
||||||
|
UI->>Client: Dispatches POST / PUT / PATCH / DELETE
|
||||||
|
Client->>DB: Executes Mutation in Database
|
||||||
|
DB-->>Client: Returns 200 OK / 201 Created
|
||||||
|
Client->>Client: Interceptor detects mutation: bumps activeMutationEpoch
|
||||||
|
UI->>UI: onSuccess triggers invalidateQueries
|
||||||
|
UI->>Client: Refetches active queries with _v = activeMutationEpoch
|
||||||
|
Client->>OutputCache: GET /v1/api/* with new _v epoch
|
||||||
|
Note over OutputCache: Cache Miss: new _v key never seen before
|
||||||
|
OutputCache->>DB: Queries PostgreSQL for fresh data
|
||||||
|
DB-->>OutputCache: Returns updated entities
|
||||||
|
OutputCache-->>UI: Caches fresh data under _v and returns to UI
|
||||||
|
UI-->>User: UI updates immediately with new status / record
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.1 Operating Rules for Record Modifications
|
||||||
|
1. **Zero Manual Cache Eviction Required**: Frontend developers do not need to manually configure cache busters for standard operations. The Axios response interceptor in `httpClient.ts` automatically increments `activeMutationEpoch` on any successful mutating call (`POST`, `PUT`, `PATCH`, `DELETE`).
|
||||||
|
2. **Deterministic Invalidation**: Calling standard `queryClient.invalidateQueries({ queryKey: [...] })` or `invalidateAndBustCache(queryClient, filters)` guarantees that the subsequent refetch bypasses stale ASP.NET Core OutputCache entries.
|
||||||
|
3. **Optimized Read Cache**: When users navigate across unchanged views or switch tabs, identical `_v` tokens are reused, allowing backend OutputCache and React Query in-memory caches to fulfill requests in < 5ms without unnecessary database hits.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Background Job Lifecycle & Event Execution Workflows
|
||||||
|
|
||||||
|
The dedicated worker host (`PostFundManagement.Jobs`) operates as a decoupled daemon executing recurring maintenance routines and asynchronous domain events dispatched from the API or internally from other jobs.
|
||||||
|
|
||||||
|
### 6.1 Event Ingestion & Job Persistence Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
subgraph Producer ["PostFundManagement.Api (Event Source)"]
|
||||||
|
A["User Action / Lifecycle Transition"] --> B["Domain Command / Mutation"]
|
||||||
|
B --> C["Instantiate IEvent (e.g. SyncUserEvent, ProjectReminderEvent)"]
|
||||||
|
C --> D["IJobOrchestrator.SendAsync() or ScheduleAsync()"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Store ["PostgreSQL Clustered Store (pfm-scheduler)"]
|
||||||
|
D --> E[("QRTZ_JOB_DETAILS and QRTZ_TRIGGERS")]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Consumer ["PostFundManagement.Jobs (Execution Host)"]
|
||||||
|
E -->|"Acquire Trigger Lock"| F["Quartz Scheduler Engine (MaxConcurrency = 5)"]
|
||||||
|
F --> G["MediatorJob[TNotification]"]
|
||||||
|
G --> H["Deserialize JSON Payload from JobDataMap"]
|
||||||
|
H --> I["IMediator.Publish(notification)"]
|
||||||
|
I --> J["Domain Event Handler: Business Logic in pfm DB"]
|
||||||
|
J -.->|"Cascading Sub-Task Needed"| K["IJobOrchestrator.SendAsync: Dispatch One-Time Job"]
|
||||||
|
K -.-> E
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 Standard Background Processes
|
||||||
|
|
||||||
|
#### 1. Automated Project Monitoring & Reminders Process
|
||||||
|
* **Trigger**: Scheduled cron job registered on `JobsWorker` startup.
|
||||||
|
* **Operational Cycle**:
|
||||||
|
1. Job queries active projects and non-completed milestones across tenant partitions.
|
||||||
|
2. Compares milestone contractual due dates against current UTC time:
|
||||||
|
- **T - 14 / T - 7 / T - 1 Days**: Emits reminder notification events to Grantee/Partner project managers.
|
||||||
|
- **T + 0 Days (Overdue)**: Automatically flags the milestone as `Overdue` (`FR-M07-009`) and updates project RAG status to `At Risk` (`FR-M04-017`).
|
||||||
|
3. Escalates overdue notifications to internal Programme Managers for intervention.
|
||||||
|
|
||||||
|
#### 2. Risk Monitoring & Automated Escalation Process
|
||||||
|
* **Trigger**: Periodic recurring schedule.
|
||||||
|
* **Operational Cycle**:
|
||||||
|
1. Queries all active project risk registers and Key Risk Indicators (KRIs) (`FR-M09-001` - `011`).
|
||||||
|
2. Evaluates current severity score ($\text{Risk Score} = \text{Likelihood} \times \text{Impact}$).
|
||||||
|
3. If risk score breaches the configured programme tolerance threshold:
|
||||||
|
- Automatically logs a risk escalation event.
|
||||||
|
- Dispatches alerts to Risk Managers and Portfolio Executives.
|
||||||
|
4. Sweeps assigned risk mitigation actions; identifies overdue mitigation plans and emits reminders to responsible assignees.
|
||||||
|
|
||||||
|
#### 3. Email Notification Processing Workflow (`Platform.Email`)
|
||||||
|
* **Trigger**: High-frequency recurring polling job or deferred one-time dispatch.
|
||||||
|
* **Operational Cycle**:
|
||||||
|
1. Queries pending notifications from the transactional database where `Platform == NotificationPlatform.Email` and `Status == NotificationStatus.Pending` or `Queued`.
|
||||||
|
2. Resolves tenant context using `HeadlessTenantContextService`.
|
||||||
|
3. Formats email templates with recipient credentials and branding.
|
||||||
|
4. Transmits messages via MailKit/MimeKit SMTP engine with rate-limiting and connection pooling.
|
||||||
|
5. On successful delivery: Updates record status to `Sent`, stamps `DeliveredAt` timestamp, and logs dispatch telemetry (`FR-M25-001`).
|
||||||
|
6. On failure: Increments retry counter; if retry limit exceeded, marks as `Failed` with diagnostic error trace.
|
||||||
|
|
||||||
|
### 6.3 Recurring-to-One-Time Job Cascading Pattern
|
||||||
|
|
||||||
|
When a recurring job evaluates a broad batch of records (e.g., scanning thousands of project indicators or verifying compliance expirations), it must not execute long-running operations synchronously in the recurring job thread:
|
||||||
|
1. The recurring job identifies items requiring heavy processing (e.g. generating an executive PDF digest or calling external webhooks).
|
||||||
|
2. For each identified item, it enqueues an isolated one-time job via `IJobOrchestrator.SendAsync(specificEvent)`.
|
||||||
|
3. This releases the recurring job quickly, distributes execution across the 5 available worker threads in `PostFundManagement.Jobs`, and guarantees that a single failed sub-task can be retried independently without stalling the entire recurring cycle.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 281 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 302 KiB |
|
After Width: | Height: | Size: 260 KiB |
|
After Width: | Height: | Size: 269 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 140 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 218 KiB |
|
After Width: | Height: | Size: 637 KiB |
@@ -1,13 +1,17 @@
|
|||||||
PROPRIETARY LICENSE
|
PROPRIETARY LICENSE
|
||||||
|
|
||||||
Copyright (c) 2026 IFRI TECHNOLOGIES (PTY) Ltd. All rights reserved.
|
Copyright (c) 2026 Lite Charms (PTY) Ltd / Ifri Technologies. All rights reserved.
|
||||||
|
|
||||||
This software and its associated documentation (the "Software") are the
|
This software and its associated documentation (the "Software") are the proprietary
|
||||||
proprietary property of IFRI TECHNOLOGIES (PTY) Ltd.
|
property of Lite Charms (PTY) Ltd.
|
||||||
|
|
||||||
The Software is provided for internal use only. Unauthorized copying,
|
Ifri Technologies is an entity within Lite Charms (PTY) Ltd.
|
||||||
distribution, modification, or use of this file via any medium is
|
Lite Charms (PTY) Ltd is the technology partner and author of every technical
|
||||||
strictly prohibited.
|
implementation and maintenance of the system.
|
||||||
|
|
||||||
|
The Software is provided for internal and authorized partner use only.
|
||||||
|
Unauthorized copying, distribution, modification, reverse engineering,
|
||||||
|
or use of this file via any medium is strictly prohibited.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Stage 1: Runtime base
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
|
||||||
|
USER app
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Stage 2: Build & Restore
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
# Copy props and csproj files for layer caching
|
||||||
|
COPY ["Directory.Build.props", "./"]
|
||||||
|
COPY ["Directory.Packages.props", "./"]
|
||||||
|
COPY ["PostFundManagement.snk", "./"]
|
||||||
|
COPY ["LICENSE", "./"]
|
||||||
|
COPY ["PostFundManagement.Api/PostFundManagement.Api.csproj", "PostFundManagement.Api/"]
|
||||||
|
COPY ["PostFundManagement.Application/PostFundManagement.Application.csproj", "PostFundManagement.Application/"]
|
||||||
|
COPY ["PostFundManagement.Domain/PostFundManagement.Domain.csproj", "PostFundManagement.Domain/"]
|
||||||
|
COPY ["PostFundManagement.Infrastructure/PostFundManagement.Infrastructure.csproj", "PostFundManagement.Infrastructure/"]
|
||||||
|
|
||||||
|
# Restore packages
|
||||||
|
RUN dotnet restore "PostFundManagement.Api/PostFundManagement.Api.csproj"
|
||||||
|
|
||||||
|
# Copy full source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build from the project folder using relative filename
|
||||||
|
WORKDIR "/src/PostFundManagement.Api"
|
||||||
|
RUN dotnet build "PostFundManagement.Api.csproj" -c Release -o /app/build
|
||||||
|
|
||||||
|
# Stage 3: Publish
|
||||||
|
FROM build AS publish
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
RUN dotnet publish "PostFundManagement.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
# Stage 4: Final Runtime
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "PostFundManagement.Api.dll"]
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Communications;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Communications;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class CreateNotificationEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/communications/notifications", async (CreateNotificationRequest request,
|
||||||
|
HttpContext context, ICommunicationsService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateNotificationAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Created(),
|
||||||
|
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Creates a new in-app or outbound notification record")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(CreateNotificationEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status201Created)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Communications);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Communications;
|
namespace PostFundManagement.Api.Endpoints.Communications;
|
||||||
|
|
||||||
@@ -14,20 +12,21 @@ public sealed class GetApplicantInviteEndpoint : IEndpoint
|
|||||||
{
|
{
|
||||||
public void Map(IEndpointRouteBuilder builder)
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
{
|
{
|
||||||
builder.MapGet("api/communications/invites/applicant/{applicantUserId:Guid}", async (Guid applicantUserId, ICommunicationsService service,
|
builder.MapGet("api/communications/invites/applicants/{applicantUserId:Guid}", async (Guid applicantUserId, ICommunicationsService service,
|
||||||
HttpContext context, CancellationToken cancellationToken = default) =>
|
HttpContext context, CancellationToken cancellationToken = default) =>
|
||||||
await service.GetApplicantInviteAsync(applicantUserId, cancellationToken) switch
|
await service.GetApplicantInviteAsync(applicantUserId, cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var invite } => Results.Ok(invite),
|
{ IsSuccess: true, Value: var invite } => Results.Ok(invite),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
})
|
})
|
||||||
.WithDescription("Gets an applicant's invite")
|
.WithDescription("Gets an applicant's invite")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetApplicantInviteEndpoint).ToEndpointName())
|
.WithName(typeof(GetApplicantInviteEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Invite>(StatusCodes.Status200OK)
|
.Produces<Invite>(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Communications)
|
.WithTags(EndpointTags.Communications)
|
||||||
.CacheOutput();
|
.CacheOutput();
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Communications;
|
namespace PostFundManagement.Api.Endpoints.Communications;
|
||||||
|
|
||||||
@@ -19,15 +17,16 @@ public sealed class GetApplicantInvitesEndpoint : IEndpoint
|
|||||||
await service.GetApplicantInvitesAsync(applicantUserId, cancellationToken) switch
|
await service.GetApplicantInvitesAsync(applicantUserId, cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var invite } => Results.Ok(invite),
|
{ IsSuccess: true, Value: var invite } => Results.Ok(invite),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
})
|
})
|
||||||
.WithDescription("Gets an applicant's invite list")
|
.WithDescription("Gets an applicant's invite list")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetApplicantInvitesEndpoint).ToEndpointName())
|
.WithName(typeof(GetApplicantInvitesEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Invite[]>(StatusCodes.Status200OK)
|
.Produces<Invite[]>(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Communications)
|
.WithTags(EndpointTags.Communications)
|
||||||
.CacheOutput();
|
.CacheOutput();
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Communications;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetIApplicantDataEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/communications/applicants/{applicantUserId:Guid}/all", async (Guid applicantUserId,
|
||||||
|
ICommunicationsService service, CancellationToken cancellationtToken = default ) =>
|
||||||
|
await service.GetApplicantDataAsync(applicantUserId, cancellationtToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var applicantData } => Results.Ok(applicantData),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
|
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Gets an applicant's data")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(GetIApplicantDataEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Invite>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Communications)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,9 +4,6 @@ using PostFundManagement.Domain;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using static PostFundManagement.Application.Extensions.General;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Communications;
|
namespace PostFundManagement.Api.Endpoints.Communications;
|
||||||
|
|
||||||
@@ -23,6 +20,7 @@ public sealed class GetInvitesEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get a list of sent invitations")
|
.WithDescription("Get a list of sent invitations")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetInvitesEndpoint).ToEndpointName())
|
.WithName(typeof(GetInvitesEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Invite[]>(StatusCodes.Status200OK)
|
.Produces<Invite[]>(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Communications;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetNotificationsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/communications/notifications/{organisationId}", async (string organisationId,
|
||||||
|
ICommunicationsService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetNotificationsAsync(organisationId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var notifications } => Results.Ok(notifications),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Retrieves the notification system report")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(GetNotificationsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Notification[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Notification[]>(StatusCodes.Status404NotFound)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Communications)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Communications;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetUserNotificationsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/communications/notifications/user/{userId:guid}", async (Guid userId,
|
||||||
|
ICommunicationsService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetUserNotificationsAsync(userId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var notifications } => Results.Ok(notifications),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Retrieves all notifications targeted at a specific user")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(GetUserNotificationsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Notification[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Communications)
|
||||||
|
.CacheOutput();
|
||||||
|
|
||||||
|
builder.MapGet("api/communications/notifications/inbox", async (HttpContext context,
|
||||||
|
ICommunicationsService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetUserNotificationsAsync(context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var notifications } => Results.Ok(notifications),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Retrieves all in-app and system notifications targeted at the authenticated caller")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName($"{typeof(GetUserNotificationsEndpoint).ToEndpointName()}Inbox")
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Notification[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Communications)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -5,8 +5,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Communications;
|
namespace PostFundManagement.Api.Endpoints.Communications;
|
||||||
|
|
||||||
@@ -15,19 +13,22 @@ public sealed class SendInviteEndpoint : IEndpoint
|
|||||||
{
|
{
|
||||||
public void Map(IEndpointRouteBuilder builder)
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
{
|
{
|
||||||
builder.MapPost("api/communications/invite", async (InviteRequest request,
|
builder.MapPost("api/communications/invites", async (InviteRequest request,
|
||||||
HttpContext context, ICommunicationsService service, CancellationToken cancellationToken = default) =>
|
HttpContext context, ICommunicationsService service, CancellationToken cancellationToken = default) =>
|
||||||
await service.SendInviteAsync(request, context.GetUserId(), cancellationToken) switch
|
await service.SendInviteAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var invite } => Results.Ok(invite),
|
{ IsSuccess: true, Value: var invite } => Results.Ok(invite),
|
||||||
|
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Trigger an invite to an organization or candidate (specifying NotificationPlatform)")
|
.WithDescription("Trigger an invite to an organization or candidate (specifying NotificationPlatform)")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(SendInviteEndpoint).ToEndpointName())
|
.WithName(typeof(SendInviteEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Invite>(StatusCodes.Status200OK)
|
.Produces<Invite>(StatusCodes.Status200OK)
|
||||||
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.WithTags(EndpointTags.Communications);
|
.WithTags(EndpointTags.Communications);
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Communications;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Communications;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class UpdateNotificationEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/communications/notifications", async (UpdateNotificationRequest request,
|
||||||
|
ICommunicationsService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdateNotificationAsync(request, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.NoContent(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
|
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Updates a notification status, delivery timestamp, or error state")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(UpdateNotificationEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status204NoContent)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Communications);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -3,8 +3,6 @@ using PostFundManagement.Application.Extensions;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Evidence;
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
@@ -34,9 +32,9 @@ public class CreateEvidenceEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.RequireAuthorization()
|
|
||||||
.DisableAntiforgery()
|
|
||||||
.WithDescription("Upload supporting evidence file to S3 and register metadata (M14)")
|
.WithDescription("Upload supporting evidence file to S3 and register metadata (M14)")
|
||||||
|
.DisableAntiforgery()
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(CreateEvidenceEndpoint).ToEndpointName())
|
.WithName(typeof(CreateEvidenceEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Evidence;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Evidence;
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
@@ -19,16 +17,17 @@ public class CreateIndicatorEndpoint : IEndpoint
|
|||||||
await service.CreateIndicatorAsync(request, context.GetUserId(), cancellationToken) switch
|
await service.CreateIndicatorAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true } => Results.Ok(),
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Define indicators and baselines/targets (M08)")
|
.WithDescription("Define indicators and baselines/targets (M08)")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(CreateIndicatorEndpoint).ToEndpointName())
|
.WithName(typeof(CreateIndicatorEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Evidence);
|
.WithTags(EndpointTags.Evidence);
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Evidence;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Evidence;
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
@@ -19,16 +17,18 @@ public class CreateMilestoneEndpoint : IEndpoint
|
|||||||
await service.CreateMilestoneAsync(request, context.GetUserId(), cancellationToken) switch
|
await service.CreateMilestoneAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true } => Results.Ok(),
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Define contractual milestones (M07)")
|
.WithDescription("Define contractual milestones (M07)")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(CreateMilestoneRequest).ToEndpointName())
|
.WithName(typeof(CreateMilestoneRequest).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Evidence);
|
.WithTags(EndpointTags.Evidence);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ using PostFundManagement.Application.Extensions;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Evidence;
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
@@ -19,16 +17,17 @@ public sealed class GetEvidenceEndpoint : IEndpoint
|
|||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var evidence } => Results.Ok(evidence),
|
{ IsSuccess: true, Value: var evidence } => Results.Ok(evidence),
|
||||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get a specific evidence record and assiciated file URLs")
|
.WithDescription("Get a specific evidence record and assiciated file URLs")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetEvidenceEndpoint).ToEndpointName())
|
.WithName(typeof(GetEvidenceEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Domain.Models.Evidence>(StatusCodes.Status200OK)
|
.Produces<Domain.Models.Evidence>(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.Produces(StatusCodes.Status403Forbidden)
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Evidence)
|
.WithTags(EndpointTags.Evidence)
|
||||||
.CacheOutput();
|
.CacheOutput();
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ using PostFundManagement.Application.Extensions;
|
|||||||
using PostFundManagement.Domain;
|
using PostFundManagement.Domain;
|
||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Evidence;
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
@@ -21,6 +19,7 @@ public class GetEvidenceFilesEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get a list of all uploaded evidence files metadata")
|
.WithDescription("Get a list of all uploaded evidence files metadata")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetEvidenceFilesEndpoint).ToEndpointName())
|
.WithName(typeof(GetEvidenceFilesEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK)
|
.Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ using PostFundManagement.Application.Extensions;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Evidence;
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
@@ -19,16 +17,17 @@ public sealed class GetEvidenceUrlEndpoint : IEndpoint
|
|||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var evidenceUrl } => Results.Ok(evidenceUrl),
|
{ IsSuccess: true, Value: var evidenceUrl } => Results.Ok(evidenceUrl),
|
||||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Gets a secure download URL of the evidence file, URL expires (default: 15min)")
|
.WithDescription("Gets a secure download URL of the evidence file, URL expires (default: 15min)")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetEvidenceUrlEndpoint).ToEndpointName())
|
.WithName(typeof(GetEvidenceUrlEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<string>(StatusCodes.Status200OK)
|
|
||||||
.Produces<string>(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status403Forbidden)
|
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<string>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Grants)
|
.WithTags(EndpointTags.Grants)
|
||||||
.CacheOutput();
|
.CacheOutput();
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Evidence;
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
@@ -20,16 +18,17 @@ public sealed class GetIndicatorEndpoint : IEndpoint
|
|||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var indicator } => Results.Ok(indicator),
|
{ IsSuccess: true, Value: var indicator } => Results.Ok(indicator),
|
||||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get a specific indicator.")
|
.WithDescription("Get a specific indicator.")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetIndicatorEndpoint).ToEndpointName())
|
.WithName(typeof(GetIndicatorEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Indicator>(StatusCodes.Status200OK)
|
.Produces<Indicator>(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.Produces(StatusCodes.Status403Forbidden)
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Evidence)
|
.WithTags(EndpointTags.Evidence)
|
||||||
.CacheOutput();
|
.CacheOutput();
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Evidence;
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
@@ -22,6 +20,7 @@ public class GetIndicatorsEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
})
|
})
|
||||||
.WithDescription("Get a list of indicators")
|
.WithDescription("Get a list of indicators")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetIndicatorsEndpoint).ToEndpointName())
|
.WithName(typeof(GetIndicatorsEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Indicator[]>(StatusCodes.Status200OK)
|
.Produces<Indicator[]>(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ using PostFundManagement.Application.Extensions;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Evidence;
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
@@ -22,6 +20,7 @@ public class GetMilestoneEvidenceEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get a list of all uploaded evidence metadata for a specific mliestone")
|
.WithDescription("Get a list of all uploaded evidence metadata for a specific mliestone")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetMilestoneEvidenceEndpoint).ToEndpointName())
|
.WithName(typeof(GetMilestoneEvidenceEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK)
|
.Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ using PostFundManagement.Application.Extensions;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Evidence;
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
@@ -13,7 +11,7 @@ public class GetProjectEvidenceEndpoint : IEndpoint
|
|||||||
{
|
{
|
||||||
public void Map(IEndpointRouteBuilder builder)
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
{
|
{
|
||||||
builder.MapGet("api/evidence/{projectId}", async (string projectId, IEvidenceService service,
|
builder.MapGet("api/evidence/projects/{projectId}", async (string projectId, IEvidenceService service,
|
||||||
CancellationToken cancellationToken = default) =>
|
CancellationToken cancellationToken = default) =>
|
||||||
await service.GetProjectEvidencesAsync(projectId, cancellationToken) switch
|
await service.GetProjectEvidencesAsync(projectId, cancellationToken) switch
|
||||||
{
|
{
|
||||||
@@ -22,6 +20,7 @@ public class GetProjectEvidenceEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
})
|
})
|
||||||
.WithDescription("Get a list of all uploaded evidence metadata for a specific project")
|
.WithDescription("Get a list of all uploaded evidence metadata for a specific project")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetProjectEvidenceEndpoint).ToEndpointName())
|
.WithName(typeof(GetProjectEvidenceEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK)
|
.Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ using PostFundManagement.Application.Extensions;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Evidence;
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
@@ -23,6 +21,7 @@ public class RecordIndicatorAmountEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Record actual values for indicators (M08)")
|
.WithDescription("Record actual values for indicators (M08)")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(RecordIndicatorAmountEndpoint).ToEndpointName())
|
.WithName(typeof(RecordIndicatorAmountEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Evidence;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Evidence;
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
@@ -19,17 +17,19 @@ public sealed class UpdateEvidenceEndpoint : IEndpoint
|
|||||||
await service.UpdateEvidenceAsync(request, context.GetUserId(), cancellationToken) switch
|
await service.UpdateEvidenceAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true } => Results.Ok(),
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Updates a specific evidence record metadata")
|
.WithDescription("Updates a specific evidence record metadata")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.DisableAntiforgery()
|
||||||
.WithName(typeof(UpdateEvidenceEndpoint).ToEndpointName())
|
.WithName(typeof(UpdateEvidenceEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status403Forbidden)
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Evidence);
|
.WithTags(EndpointTags.Evidence);
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ using PostFundManagement.Application.Extensions;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Execution;
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
@@ -23,6 +21,7 @@ public class ApproveDisbursementEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
})
|
})
|
||||||
.WithDescription("Approve disbursement and release payment (M06)")
|
.WithDescription("Approve disbursement and release payment (M06)")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(ApproveDisbursementEndpoint).ToEndpointName())
|
.WithName(typeof(ApproveDisbursementEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Execution;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Execution;
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
@@ -19,17 +17,18 @@ public class CreateDisbursementEndpoint : IEndpoint
|
|||||||
await service.CreateDisbursementAsync(request, context.GetUserId(), cancellationToken) switch
|
await service.CreateDisbursementAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true } => Results.Ok(),
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
})
|
})
|
||||||
.WithDescription("Create a new disbursement schedule (M06)")
|
.WithDescription("Create a new disbursement schedule (M06)")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(CreateDisbursementEndpoint).ToEndpointName())
|
.WithName(typeof(CreateDisbursementEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Execution);
|
.WithTags(EndpointTags.Execution);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Execution;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Execution;
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
@@ -19,17 +17,18 @@ public class CreateRiskEndpoint : IEndpoint
|
|||||||
await service.CreateRiskAsync(request, context.GetUserId(), cancellationToken) switch
|
await service.CreateRiskAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true } => Results.Ok(),
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Add risk to risk register (M09)")
|
.WithDescription("Add risk to risk register (M09)")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(CreateRiskEndpoint).ToEndpointName())
|
.WithName(typeof(CreateRiskEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Execution);
|
.WithTags(EndpointTags.Execution);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Execution;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class CreateRuleEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/execution/rules", async (CreateRuleRequest request, HttpContext context,
|
||||||
|
IExecutionService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateRuleAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Add rule against a programme")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(CreateRuleEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Execution);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Execution;
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
@@ -20,16 +18,17 @@ public sealed class GetDisbursementEndpoint : IEndpoint
|
|||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var disbursement } => Results.Ok(disbursement),
|
{ IsSuccess: true, Value: var disbursement } => Results.Ok(disbursement),
|
||||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get a specific disbursement")
|
.WithDescription("Get a specific disbursement")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetDisbursementEndpoint).ToEndpointName())
|
.WithName(typeof(GetDisbursementEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Disbursement>(StatusCodes.Status200OK)
|
.Produces<Disbursement>(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Execution)
|
.WithTags(EndpointTags.Execution)
|
||||||
.CacheOutput();
|
.CacheOutput();
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Execution;
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
@@ -22,6 +20,7 @@ public class GetDisbursementsEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get a list of disbursements")
|
.WithDescription("Get a list of disbursements")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetDisbursementsEndpoint).ToEndpointName())
|
.WithName(typeof(GetDisbursementsEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Disbursement[]>(StatusCodes.Status200OK)
|
.Produces<Disbursement[]>(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Execution;
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
@@ -20,15 +18,16 @@ public sealed class GetRiskEndpoint : IEndpoint
|
|||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var risk } => Results.Ok(risk),
|
{ IsSuccess: true, Value: var risk } => Results.Ok(risk),
|
||||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get a specific risk")
|
.WithDescription("Get a specific risk")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetRiskEndpoint).ToEndpointName())
|
.WithName(typeof(GetRiskEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Risk>(StatusCodes.Status200OK)
|
.Produces<Risk>(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Execution)
|
.WithTags(EndpointTags.Execution)
|
||||||
.CacheOutput();
|
.CacheOutput();
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ using PostFundManagement.Application.Extensions;
|
|||||||
using PostFundManagement.Domain;
|
using PostFundManagement.Domain;
|
||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
using PostFundManagement.Domain.Models;
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Execution;
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
@@ -21,9 +20,10 @@ public class GetRisksEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get a list of all risks")
|
.WithDescription("Get a list of all risks")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetRisksEndpoint).ToEndpointName())
|
.WithName(typeof(GetRisksEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces<Risk[]>(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Execution)
|
.WithTags(EndpointTags.Execution)
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetRuleEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/execution/rules/{ruleId}", async (string ruleId, IExecutionService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetRuleAsync(ruleId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var rules } => Results.Ok(rules),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a specific programme rule")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(GetRuleEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Rule>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Execution)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetRulesEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/execution/programmes/rules", async (Pagination pagination, IExecutionService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetRulesAsync(pagination, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var rules } => Results.Ok(rules),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of all programme rules")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(GetRulesEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Rule[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Execution)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Execution;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Execution;
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
@@ -23,6 +21,7 @@ public sealed class UpdateDisbursementEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Update the details of a specific disbursement")
|
.WithDescription("Update the details of a specific disbursement")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(UpdateDisbursementEndpoint).ToEndpointName())
|
.WithName(typeof(UpdateDisbursementEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Execution;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Execution;
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
@@ -23,6 +21,7 @@ public sealed class UpdateRiskEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Update the details of a specific risk")
|
.WithDescription("Update the details of a specific risk")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(UpdateRiskEndpoint).ToEndpointName())
|
.WithName(typeof(UpdateRiskEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Execution;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class UpdateRuleEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/execution/rules", async (UpdateRuleRequest request, HttpContext context,
|
||||||
|
IExecutionService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdateRuleAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Update the details of a specific programme rule (supplied version auto-increment)")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(UpdateRuleEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Execution);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Governance;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class CreateInstrumentEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/governance/instruments", async (CreateInstrumentRequest request, HttpContext context,
|
||||||
|
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateInstrumentAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true} => Results.Ok(),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Creates a new programme funding instrument")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(CreateInstrumentEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Governance;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Governance;
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
@@ -19,10 +17,12 @@ public sealed class CreateOrganisationEndpoint : IEndpoint
|
|||||||
await service.CreateOrganisationAsync(request, context.GetUserId(), cancellationToken) switch
|
await service.CreateOrganisationAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true } => Results.Ok(),
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Create a new organization profile")
|
.WithDescription("Create a new organization profile")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(CreateOrganisationEndpoint).ToEndpointName())
|
.WithName(typeof(CreateOrganisationEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Governance;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Governance;
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
@@ -25,6 +23,7 @@ public sealed class CreatePortfolioEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Create a new organization portfolio")
|
.WithDescription("Create a new organization portfolio")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(CreatePortfolioEndpoint).ToEndpointName())
|
.WithName(typeof(CreatePortfolioEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Governance;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class CreateTenantEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/governance/tenants", async (CreateTenantRequest request, IGovernanceService service, HttpContext context,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateTenantAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Create a new tenant")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(CreateTenantEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,6 @@ using PostFundManagement.Application.Extensions;
|
|||||||
using PostFundManagement.Application.Services.Governance;
|
using PostFundManagement.Application.Services.Governance;
|
||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Governance;
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
@@ -21,6 +19,7 @@ public class DashboardEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get executive portfolio dashboard metrics and KPIs")
|
.WithDescription("Get executive portfolio dashboard metrics and KPIs")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(DashboardEndpoint).ToEndpointName())
|
.WithName(typeof(DashboardEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Dashboard>(StatusCodes.Status200OK)
|
.Produces<Dashboard>(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Shared;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetAuditLogsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/governance/auditlog", async (Pagination pagination, AuditService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetAuditLogsAsync(pagination, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var logs } => Results.Ok(logs),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Retrieves audit logs based on pagination parameters")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(GetAuditLogsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<AuditLog[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Governance;
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
@@ -18,11 +16,12 @@ public class GetInstrumentsEndpoint : IEndpoint
|
|||||||
CancellationToken cancellationToken = default) =>
|
CancellationToken cancellationToken = default) =>
|
||||||
await service.GetInstrumentsAsync(programmeId, cancellationToken) switch
|
await service.GetInstrumentsAsync(programmeId, cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var instrumenrs } => Results.Ok(instrumenrs),
|
{ IsSuccess: true, Value: var instruments } => Results.Ok(instruments),
|
||||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
})
|
})
|
||||||
.WithDescription("Get all funding instruments")
|
.WithDescription("Get all funding instruments")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetInstrumentsEndpoint).ToEndpointName())
|
.WithName(typeof(GetInstrumentsEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Instrument[]>(StatusCodes.Status200OK)
|
.Produces<Instrument[]>(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Governance;
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
@@ -20,16 +18,17 @@ public class GetOrganisationEndpoint : IEndpoint
|
|||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var organisation } => Results.Ok(organisation),
|
{ IsSuccess: true, Value: var organisation } => Results.Ok(organisation),
|
||||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get organization profile by ID")
|
.WithDescription("Get organization profile by ID")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetOrganisationEndpoint).ToEndpointName())
|
.WithName(typeof(GetOrganisationEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Organisation>(StatusCodes.Status200OK)
|
.Produces<Organisation>(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.Produces(StatusCodes.Status403Forbidden)
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Governance)
|
.WithTags(EndpointTags.Governance)
|
||||||
.CacheOutput();
|
.CacheOutput();
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetOrganisationPortfoliosEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/organisations/{organisationId}/portfolio", async (string organisationId,
|
||||||
|
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetOrganisationPorfoliosAsync(organisationId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var portfolios } => Results.Ok(portfolios),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get all portfolios belonging to a specific organisation")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(GetOrganisationPortfoliosEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Portfolio[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Governance;
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
@@ -22,6 +20,7 @@ public class GetOrganisationsEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get a paginated directory of organizations")
|
.WithDescription("Get a paginated directory of organizations")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetOrganisationsEndpoint).ToEndpointName())
|
.WithName(typeof(GetOrganisationsEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Organisation[]>(StatusCodes.Status200OK)
|
.Produces<Organisation[]>(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetPorfolioValueEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/portfolios/{portfolioId}/value", async (string portfolioId, IGovernanceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetPorfolioValueAsync(portfolioId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var amount } => Results.Ok(amount),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Calculates and returns a portfolio's total project's budget value")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(GetPorfolioValueEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<decimal>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Governance;
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
@@ -19,16 +17,17 @@ public sealed class GetPortfolioEndpoint : IEndpoint
|
|||||||
await service.GetPortfolioAsync(portfolioIdHash: portfolioId, cancellationToken) switch
|
await service.GetPortfolioAsync(portfolioIdHash: portfolioId, cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var portfolio } => Results.Ok(portfolio),
|
{ IsSuccess: true, Value: var portfolio } => Results.Ok(portfolio),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get a specific portfolios")
|
.WithDescription("Get a specific portfolios")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetPortfolioEndpoint).ToEndpointName())
|
.WithName(typeof(GetPortfolioEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Portfolio[]>(StatusCodes.Status200OK)
|
.Produces<Portfolio[]>(StatusCodes.Status200OK)
|
||||||
.Produces<Portfolio[]>(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status403Forbidden)
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Governance)
|
.WithTags(EndpointTags.Governance)
|
||||||
.CacheOutput();
|
.CacheOutput();
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Governance;
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
@@ -19,17 +17,18 @@ public sealed class GetPortfolioProgrammesEndpoint : IEndpoint
|
|||||||
await service.GetPortfolioProgrammesAsync(portfolioId, cancellationToken) switch
|
await service.GetPortfolioProgrammesAsync(portfolioId, cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var portfolios } => Results.Ok(portfolios),
|
{ IsSuccess: true, Value: var portfolios } => Results.Ok(portfolios),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get a programme's portfolios")
|
.WithDescription("Get a programme's portfolios")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetPortfolioProgrammesEndpoint).ToEndpointName())
|
.WithName(typeof(GetPortfolioProgrammesEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Programme[]>(StatusCodes.Status200OK)
|
.Produces<Programme[]>(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status403Forbidden)
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Governance)
|
.WithTags(EndpointTags.Governance)
|
||||||
.CacheOutput();
|
.CacheOutput();
|
||||||
|
|||||||
@@ -1,28 +1,26 @@
|
|||||||
using PostFundManagement.Application.Abstractions.Services;
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
using PostFundManagement.Application.Extensions;
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Governance;
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
[ApiVersionTarget(1)]
|
[ApiVersionTarget(1)]
|
||||||
public class GetPortfoliosEndpoint : IEndpoint
|
public sealed class GetPortfoliosEndpoint : IEndpoint
|
||||||
{
|
{
|
||||||
public void Map(IEndpointRouteBuilder builder)
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
{
|
{
|
||||||
builder.MapGet("api/governance/organisations/{organisationId}/portfolio", async (string organisationId,
|
builder.MapPost("api/governance/organisations/portfolios", async (Pagination pagination, IGovernanceService service,
|
||||||
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
CancellationToken cancellationToken = default) =>
|
||||||
await service.GetPorfoliosAsync(organisationId, cancellationToken) switch
|
await service.GetPorfoliosAsync(pagination, cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var portfolios } => Results.Ok(portfolios),
|
{ IsSuccess: true, Value: var portfolios } => Results.Ok(portfolios),
|
||||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get all portfolios")
|
.WithDescription("Get all portfolios")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetPortfoliosEndpoint).ToEndpointName())
|
.WithName(typeof(GetPortfoliosEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Portfolio[]>(StatusCodes.Status200OK)
|
.Produces<Portfolio[]>(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetProgrammeValueEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/programmes/{programmeId}/value", async (string programmeId, IGovernanceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetProgrammeValueAsync(programmeId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var amount } => Results.Ok(amount),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Calculates and returns a programme's total project budget value")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(GetProgrammeValueEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<decimal>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Governance;
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
@@ -23,6 +21,7 @@ public class GetProgrammesEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get all programmes")
|
.WithDescription("Get all programmes")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetProgrammesEndpoint).ToEndpointName())
|
.WithName(typeof(GetProgrammesEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Programme[]>(StatusCodes.Status200OK)
|
.Produces<Programme[]>(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetTenantEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/tenants/{tenantId}", async (string tenantId, IGovernanceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetTenantAsync(tenantId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var tenant } => Results.Ok(tenant),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Gets a single tenant")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(GetTenantsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Tenant>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetTenantOrganisationsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/tenants/{tenantIdHash}/organisations", async (IGovernanceService service, string tenantIdHash,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetTenantOrganisationsAsync(tenantIdHash, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var tenantOrganisations } => Results.Ok(tenantOrganisations),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Gets organisations created and managed by one tenant")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(GetTenantOrganisationsEndpoint).ToEndpointName("Retrieval"))
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<TenantOrganisation[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetTenantUsersEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/tenants/{tenantIdHash}/users", async (IGovernanceService service, string tenantIdHash,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetTenantUsersAsync(tenantIdHash, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var tenantUsers } => Results.Ok(tenantUsers),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Gets users created and managed by one tenant")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(GetTenantUsersEndpoint).ToEndpointName("Retrieval"))
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<TenantUser[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Entities;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetTenantsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/tenants", async (IGovernanceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetTenantsAsync(cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var tenants } => Results.Ok(tenants),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Gets system tenants")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(GetTenantsEndpoint).ToEndpointName("Retrieval"))
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Tenant[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Governance;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class UpdateInstrumentEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/governance/instruments", async (UpdateInstrumentRequest request, HttpContext context,
|
||||||
|
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdateInstrumentAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Updates a programme funding instrument")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(UpdateInstrumentEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Governance;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Governance;
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
@@ -15,21 +13,24 @@ public sealed class UpdateOrganisationEndpoint : IEndpoint
|
|||||||
public void Map(IEndpointRouteBuilder builder)
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
{
|
{
|
||||||
builder.MapPut("api/governance/organisations", async (UpdateOrganisationRequest request,
|
builder.MapPut("api/governance/organisations", async (UpdateOrganisationRequest request,
|
||||||
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
IGovernanceService service, HttpContext context, CancellationToken cancellationToken = default) =>
|
||||||
await service.UpdateOrganisationAsync(request, cancellationToken) switch
|
await service.UpdateOrganisationAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true } => Results.Ok(),
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Update an existing organization profile")
|
.WithDescription("Update an existing organization profile")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(UpdateOrganisationEndpoint).ToEndpointName())
|
.WithName(typeof(UpdateOrganisationEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
|
||||||
.WithTags(EndpointTags.Governance);
|
.WithTags(EndpointTags.Governance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Governance;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Governance;
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
@@ -25,6 +23,7 @@ public sealed class UpdatePortfolioEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Update an existing organization portfolio")
|
.WithDescription("Update an existing organization portfolio")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(UpdatePortfolioEndpoint).ToEndpointName())
|
.WithName(typeof(UpdatePortfolioEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Governance;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class UpdateTenantEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/governance/tenants", async (UpdateTenantRequest request, HttpContext context,
|
||||||
|
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdateTenantAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Updates a specific tenant")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
|
.WithName(typeof(UpdateTenantEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Grants;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Grants;
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
@@ -19,17 +17,18 @@ public class CreateAwardEndpoint : IEndpoint
|
|||||||
await service.CreateAwardAsync(request, context.GetUserId(), cancellationToken) switch
|
await service.CreateAwardAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true } => Results.Ok(),
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Register an approved funding award (M01)")
|
.WithDescription("Register an approved funding award (M01)")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(CreateAwardEndpoint).ToEndpointName())
|
.WithName(typeof(CreateAwardEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Grants);
|
.WithTags(EndpointTags.Grants);
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Grants;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Grants;
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
@@ -20,16 +18,17 @@ public class CreateBudgetEndpoint : IEndpoint
|
|||||||
{
|
{
|
||||||
{ IsSuccess: true } => Results.Ok(),
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r when r.HasError<CreateError>() => Results.BadRequest(),
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Create a new budget line (M05)")
|
.WithDescription("Create a new budget line (M05)")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(CreateBudgetEndpoint).ToEndpointName())
|
.WithName(typeof(CreateBudgetEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
|
||||||
.Produces(StatusCodes.Status400BadRequest)
|
.Produces(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Grants;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Grants;
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
@@ -19,15 +17,17 @@ public class CreateContractEndpoint : IEndpoint
|
|||||||
await service.CreateContractAsync(request, context.GetUserId(), cancellationToken) switch
|
await service.CreateContractAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
{
|
{
|
||||||
{ IsSuccess: true } => Results.Ok(),
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Create a contract record from an approved award (M02)")
|
.WithDescription("Create a contract record from an approved award (M02)")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(CreateContractEndpoint).ToEndpointName())
|
.WithName(typeof(CreateContractEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.WithTags(EndpointTags.Grants);
|
.WithTags(EndpointTags.Grants);
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Application.Services.Grants;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Grants;
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
@@ -23,6 +21,7 @@ public class ExecuteContractEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Execute the contract and lock the contractual version")
|
.WithDescription("Execute the contract and lock the contractual version")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(ExecuteContractEndpoint).ToEndpointName())
|
.WithName(typeof(ExecuteContractEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces(StatusCodes.Status200OK)
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Grants;
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
@@ -20,16 +18,17 @@ public class GetAwardEndpoint : IEndpoint
|
|||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var award } => Results.Ok(award),
|
{ IsSuccess: true, Value: var award } => Results.Ok(award),
|
||||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get award by ID")
|
.WithDescription("Get award by ID")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetAwardEndpoint).ToEndpointName())
|
.WithName(typeof(GetAwardEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Award>(StatusCodes.Status200OK)
|
.Produces<Award>(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status403Forbidden)
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
.WithTags(EndpointTags.Grants)
|
.WithTags(EndpointTags.Grants)
|
||||||
.CacheOutput();
|
.CacheOutput();
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Grants;
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
@@ -22,6 +20,7 @@ public class GetAwardsEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get a list of awards")
|
.WithDescription("Get a list of awards")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetAwardsEndpoint).ToEndpointName())
|
.WithName(typeof(GetAwardsEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Award[]>(StatusCodes.Status200OK)
|
.Produces<Award[]>(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Grants;
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
@@ -20,14 +18,15 @@ public class GetBudgetEndpoint : IEndpoint
|
|||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var budget } => Results.Ok(budget),
|
{ IsSuccess: true, Value: var budget } => Results.Ok(budget),
|
||||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get budget by ID")
|
.WithDescription("Get budget by ID")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetBudgetEndpoint).ToEndpointName())
|
.WithName(typeof(GetBudgetEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Budget>(StatusCodes.Status200OK)
|
.Produces<Budget>(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces(StatusCodes.Status403Forbidden)
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain;
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Grants;
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
@@ -22,6 +20,7 @@ public class GetBudgetsEndpoint : IEndpoint
|
|||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get all budgets")
|
.WithDescription("Get all budgets")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetBudgetsEndpoint).ToEndpointName())
|
.WithName(typeof(GetBudgetsEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Budget[]>(StatusCodes.Status200OK)
|
.Produces<Budget[]>(StatusCodes.Status200OK)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using PostFundManagement.Domain.Abstractions;
|
|||||||
using PostFundManagement.Domain.Errors;
|
using PostFundManagement.Domain.Errors;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Extensions;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
using PostFundManagement.Domain.Security;
|
|
||||||
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Endpoints.Grants;
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
@@ -20,14 +18,15 @@ public class GetContractEndpoint : IEndpoint
|
|||||||
{
|
{
|
||||||
{ IsSuccess: true, Value: var contract } => Results.Ok(contract),
|
{ IsSuccess: true, Value: var contract } => Results.Ok(contract),
|
||||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
})
|
})
|
||||||
.WithDescription("Get contract by ID")
|
.WithDescription("Get contract by ID")
|
||||||
|
.RequireAuthorization(Constants.MainScope)
|
||||||
.WithName(typeof(GetContractEndpoint).ToEndpointName())
|
.WithName(typeof(GetContractEndpoint).ToEndpointName())
|
||||||
.MapToApiVersion(new ApiVersion(1))
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
.Produces<Contract>(StatusCodes.Status200OK)
|
.Produces<Contract>(StatusCodes.Status200OK)
|
||||||
.Produces(StatusCodes.Status404NotFound)
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
.Produces(StatusCodes.Status403Forbidden)
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
.Produces(StatusCodes.Status401Unauthorized)
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
|||||||