Compare commits
63
Commits
1931899d53
...
endpoints
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea0e4eb018 | ||
|
|
9ebbafb840 | ||
|
|
f3863c785d | ||
|
|
9874eac65d | ||
|
|
2b8edd05e3 | ||
|
|
edbae6defb | ||
|
|
b3e884336d | ||
|
|
1b0ceb8ea9 | ||
|
|
d779e4152e | ||
|
|
9e611cb79c | ||
|
|
b99ebf657c | ||
|
|
99ab5b2ce6 | ||
|
|
a15b25248d | ||
|
|
66f720a20b | ||
|
|
81c111c3c3 | ||
|
|
415aa30cf7 | ||
|
|
72ea20a2de | ||
|
|
15661ce8cb | ||
|
|
e274c33592 | ||
|
|
a218b19875 | ||
|
|
0a1378fe25 | ||
|
|
6719699689 | ||
|
|
8ea014e4fb | ||
|
|
9728459223 | ||
|
|
a73f53a0d9 | ||
|
|
9f76b203e3 | ||
|
|
da4499ce41 | ||
|
|
d55fb59d8d | ||
|
|
a04e6f6f50 | ||
|
|
669945ad13 | ||
|
|
cba63e2609 | ||
|
|
14b1f3a397 | ||
|
|
adc413d12b | ||
|
|
b8aa5651fa | ||
|
|
2df5b5c2ed | ||
|
|
346cb8da5f | ||
|
|
9d90f9120b | ||
|
|
5caa806600 | ||
|
|
2aed75fd4e | ||
|
|
db8d336c18 | ||
|
|
1d406a9184 | ||
|
|
841763441d | ||
|
|
6c7db78e17 | ||
|
|
c6d1d862ff | ||
|
|
4827cb5795 | ||
|
|
c204cbb1c2 | ||
|
|
0f7f875abe | ||
|
|
f51b1feb4b | ||
|
|
b288450cbb | ||
|
|
e5205f8443 | ||
|
|
5a4c621950 | ||
|
|
be5fbdbb5f | ||
|
|
a3bf7b375b | ||
|
|
7535c60ca6 | ||
|
|
de20ace27d | ||
|
|
9f1146130e | ||
|
|
5a325b0a75 | ||
|
|
ab2ef9da31 | ||
|
|
77517579e3 | ||
|
|
8f7af90344 | ||
|
|
b48c663756 | ||
|
|
e3058e0439 | ||
|
|
25cba4a3d5 |
+290
@@ -0,0 +1,290 @@
|
|||||||
|
# Remove the line below if you want to inherit .editorconfig settings from higher directories
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# C# files
|
||||||
|
[*.cs]
|
||||||
|
# IDE0250: Prefer make struct 'readonly'
|
||||||
|
dotnet_diagnostic.IDE0250.severity = warning
|
||||||
|
|
||||||
|
# IDE0060: Remove unused parameters (Good cleanup pairing)
|
||||||
|
dotnet_diagnostic.IDE0060.severity = warning
|
||||||
|
|
||||||
|
# CA1852: Seal internal types (Available in modern .NET)
|
||||||
|
dotnet_diagnostic.CA1852.severity = warning
|
||||||
|
|
||||||
|
# MA0018: Add sealed modifier to types that are never inherited
|
||||||
|
dotnet_diagnostic.MA0018.severity = warning
|
||||||
|
|
||||||
|
# Enforce that classes should be sealed
|
||||||
|
dotnet_diagnostic.MA0053.severity = warning
|
||||||
|
|
||||||
|
# CRITICAL: Force the analyzer to also flag PUBLIC classes, not just internal ones
|
||||||
|
meziantou_analyzer.MA0053.public_class_should_be_sealed = true
|
||||||
|
MA0053.public_class_should_be_sealed = true
|
||||||
|
|
||||||
|
# Keep the rule active as a warning by default
|
||||||
|
dotnet_diagnostic.MA0048.severity = warning
|
||||||
|
|
||||||
|
# Specific exclusions for Meziantou.Analyzer MA0048
|
||||||
|
# Disable the rule for enums
|
||||||
|
meziantou_analyzer.MA0048.exclude_enums = true
|
||||||
|
|
||||||
|
# Disable the rule for records
|
||||||
|
meziantou_analyzer.MA0048.exclude_records = true
|
||||||
|
|
||||||
|
#EXCLUDE specific files that are meant to hold grouped enums/records
|
||||||
|
dotnet_diagnostic.MA0048.severity = warning
|
||||||
|
|
||||||
|
# Disable the requirement to specify ConfigureAwait(false)
|
||||||
|
dotnet_diagnostic.MA0004.severity = none
|
||||||
|
|
||||||
|
# ALTERNATIVE: Exclude any file ending with 'Enums.cs' or 'Records.cs'
|
||||||
|
# (e.g., BillingEnums.cs, CustomerRecords.cs)
|
||||||
|
[**/*{Enums,Records}.cs]
|
||||||
|
dotnet_diagnostic.MA0048.severity = none
|
||||||
|
|
||||||
|
[*.{cs,vb}]
|
||||||
|
dotnet_diagnostic.IDE0039.severity = none
|
||||||
|
|
||||||
|
#### Core EditorConfig Options ####
|
||||||
|
|
||||||
|
# Indentation and spacing
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
tab_width = 4
|
||||||
|
|
||||||
|
# New line preferences
|
||||||
|
end_of_line = crlf
|
||||||
|
insert_final_newline = false
|
||||||
|
|
||||||
|
#### .NET Code Actions ####
|
||||||
|
|
||||||
|
# Type members
|
||||||
|
dotnet_hide_advanced_members = false
|
||||||
|
dotnet_member_insertion_location = with_other_members_of_the_same_kind
|
||||||
|
dotnet_property_generation_behavior = prefer_throwing_properties
|
||||||
|
|
||||||
|
# Symbol search
|
||||||
|
dotnet_search_reference_assemblies = true
|
||||||
|
|
||||||
|
#### .NET Coding Conventions ####
|
||||||
|
|
||||||
|
# Organize usings
|
||||||
|
dotnet_separate_import_directive_groups = false
|
||||||
|
dotnet_sort_system_directives_first = false
|
||||||
|
file_header_template = unset
|
||||||
|
|
||||||
|
# this. and Me. preferences
|
||||||
|
dotnet_style_qualification_for_event = false
|
||||||
|
dotnet_style_qualification_for_field = false
|
||||||
|
dotnet_style_qualification_for_method = false
|
||||||
|
dotnet_style_qualification_for_property = false
|
||||||
|
|
||||||
|
# Language keywords vs BCL types preferences
|
||||||
|
dotnet_style_predefined_type_for_locals_parameters_members = true
|
||||||
|
dotnet_style_predefined_type_for_member_access = true
|
||||||
|
|
||||||
|
# Parentheses preferences
|
||||||
|
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
|
||||||
|
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
|
||||||
|
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
|
||||||
|
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
|
||||||
|
|
||||||
|
# Modifier preferences
|
||||||
|
dotnet_style_require_accessibility_modifiers = for_non_interface_members
|
||||||
|
|
||||||
|
# Expression-level preferences
|
||||||
|
dotnet_prefer_system_hash_code = true
|
||||||
|
dotnet_style_coalesce_expression = true
|
||||||
|
dotnet_style_collection_initializer = true
|
||||||
|
dotnet_style_explicit_tuple_names = true
|
||||||
|
dotnet_style_namespace_match_folder = true
|
||||||
|
dotnet_style_null_propagation = true
|
||||||
|
dotnet_style_object_initializer = true
|
||||||
|
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||||
|
dotnet_style_prefer_auto_properties = true
|
||||||
|
dotnet_style_prefer_collection_expression = when_types_loosely_match
|
||||||
|
dotnet_style_prefer_compound_assignment = true
|
||||||
|
dotnet_style_prefer_conditional_expression_over_assignment = true
|
||||||
|
dotnet_style_prefer_conditional_expression_over_return = true
|
||||||
|
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
|
||||||
|
dotnet_style_prefer_inferred_anonymous_type_member_names = true
|
||||||
|
dotnet_style_prefer_inferred_tuple_names = true
|
||||||
|
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
|
||||||
|
dotnet_style_prefer_non_hidden_explicit_cast_in_source = true
|
||||||
|
dotnet_style_prefer_simplified_boolean_expressions = true
|
||||||
|
dotnet_style_prefer_simplified_interpolation = true
|
||||||
|
|
||||||
|
# Field preferences
|
||||||
|
dotnet_style_readonly_field = true
|
||||||
|
|
||||||
|
# Parameter preferences
|
||||||
|
dotnet_code_quality_unused_parameters = all
|
||||||
|
|
||||||
|
# Suppression preferences
|
||||||
|
dotnet_remove_unnecessary_suppression_exclusions = none
|
||||||
|
|
||||||
|
# New line preferences
|
||||||
|
dotnet_style_allow_multiple_blank_lines_experimental = true
|
||||||
|
dotnet_style_allow_statement_immediately_after_block_experimental = true
|
||||||
|
|
||||||
|
#### C# Coding Conventions ####
|
||||||
|
|
||||||
|
# var preferences
|
||||||
|
csharp_style_var_elsewhere = false
|
||||||
|
csharp_style_var_for_built_in_types = false
|
||||||
|
csharp_style_var_when_type_is_apparent = false
|
||||||
|
|
||||||
|
# Expression-bodied members
|
||||||
|
csharp_style_expression_bodied_accessors = true
|
||||||
|
csharp_style_expression_bodied_constructors = false
|
||||||
|
csharp_style_expression_bodied_indexers = true
|
||||||
|
csharp_style_expression_bodied_lambdas = true
|
||||||
|
csharp_style_expression_bodied_local_functions = true
|
||||||
|
csharp_style_expression_bodied_methods = false
|
||||||
|
csharp_style_expression_bodied_operators = false
|
||||||
|
csharp_style_expression_bodied_properties = true
|
||||||
|
|
||||||
|
# Pattern matching preferences
|
||||||
|
csharp_style_pattern_matching_over_as_with_null_check = true
|
||||||
|
csharp_style_pattern_matching_over_is_with_cast_check = true
|
||||||
|
csharp_style_prefer_extended_property_pattern = true
|
||||||
|
csharp_style_prefer_not_pattern = true
|
||||||
|
csharp_style_prefer_pattern_matching = true
|
||||||
|
csharp_style_prefer_switch_expression = true
|
||||||
|
|
||||||
|
# Null-checking preferences
|
||||||
|
csharp_style_conditional_delegate_call = true
|
||||||
|
|
||||||
|
# Modifier preferences
|
||||||
|
csharp_prefer_static_anonymous_function = true
|
||||||
|
csharp_prefer_static_local_function = true
|
||||||
|
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
|
||||||
|
csharp_style_prefer_readonly_struct = true
|
||||||
|
csharp_style_prefer_readonly_struct_member = true
|
||||||
|
|
||||||
|
# Code-block preferences
|
||||||
|
csharp_prefer_braces = true
|
||||||
|
csharp_prefer_simple_using_statement = true
|
||||||
|
csharp_prefer_system_threading_lock = true
|
||||||
|
csharp_style_namespace_declarations = block_scoped
|
||||||
|
csharp_style_prefer_method_group_conversion = true
|
||||||
|
csharp_style_prefer_primary_constructors = true
|
||||||
|
csharp_style_prefer_simple_property_accessors = true
|
||||||
|
csharp_style_prefer_top_level_statements = true
|
||||||
|
|
||||||
|
# Expression-level preferences
|
||||||
|
csharp_prefer_simple_default_expression = true
|
||||||
|
csharp_style_deconstructed_variable_declaration = true
|
||||||
|
csharp_style_implicit_object_creation_when_type_is_apparent = true
|
||||||
|
csharp_style_inlined_variable_declaration = true
|
||||||
|
csharp_style_prefer_implicitly_typed_lambda_expression = true
|
||||||
|
csharp_style_prefer_index_operator = true
|
||||||
|
csharp_style_prefer_local_over_anonymous_function = true
|
||||||
|
csharp_style_prefer_null_check_over_type_check = true
|
||||||
|
csharp_style_prefer_range_operator = true
|
||||||
|
csharp_style_prefer_tuple_swap = true
|
||||||
|
csharp_style_prefer_unbound_generic_type_in_nameof = true
|
||||||
|
csharp_style_prefer_utf8_string_literals = true
|
||||||
|
csharp_style_throw_expression = true
|
||||||
|
csharp_style_unused_value_assignment_preference = discard_variable
|
||||||
|
csharp_style_unused_value_expression_statement_preference = discard_variable
|
||||||
|
|
||||||
|
# 'using' directive preferences
|
||||||
|
csharp_using_directive_placement = outside_namespace
|
||||||
|
|
||||||
|
# New line preferences
|
||||||
|
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
|
||||||
|
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
|
||||||
|
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
|
||||||
|
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
|
||||||
|
csharp_style_allow_embedded_statements_on_same_line_experimental = true
|
||||||
|
|
||||||
|
#### C# Formatting Rules ####
|
||||||
|
|
||||||
|
# New line preferences
|
||||||
|
csharp_new_line_before_catch = true
|
||||||
|
csharp_new_line_before_else = true
|
||||||
|
csharp_new_line_before_finally = true
|
||||||
|
csharp_new_line_before_members_in_anonymous_types = true
|
||||||
|
csharp_new_line_before_members_in_object_initializers = true
|
||||||
|
csharp_new_line_before_open_brace = all
|
||||||
|
csharp_new_line_between_query_expression_clauses = true
|
||||||
|
|
||||||
|
# Indentation preferences
|
||||||
|
csharp_indent_block_contents = true
|
||||||
|
csharp_indent_braces = false
|
||||||
|
csharp_indent_case_contents = true
|
||||||
|
csharp_indent_case_contents_when_block = true
|
||||||
|
csharp_indent_labels = one_less_than_current
|
||||||
|
csharp_indent_switch_labels = true
|
||||||
|
|
||||||
|
# Space preferences
|
||||||
|
csharp_space_after_cast = false
|
||||||
|
csharp_space_after_colon_in_inheritance_clause = true
|
||||||
|
csharp_space_after_comma = true
|
||||||
|
csharp_space_after_dot = false
|
||||||
|
csharp_space_after_keywords_in_control_flow_statements = true
|
||||||
|
csharp_space_after_semicolon_in_for_statement = true
|
||||||
|
csharp_space_around_binary_operators = before_and_after
|
||||||
|
csharp_space_around_declaration_statements = false
|
||||||
|
csharp_space_before_colon_in_inheritance_clause = true
|
||||||
|
csharp_space_before_comma = false
|
||||||
|
csharp_space_before_dot = false
|
||||||
|
csharp_space_before_open_square_brackets = false
|
||||||
|
csharp_space_before_semicolon_in_for_statement = false
|
||||||
|
csharp_space_between_empty_square_brackets = false
|
||||||
|
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||||
|
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||||
|
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_parentheses = false
|
||||||
|
csharp_space_between_square_brackets = false
|
||||||
|
|
||||||
|
# Wrapping preferences
|
||||||
|
csharp_preserve_single_line_blocks = true
|
||||||
|
csharp_preserve_single_line_statements = true
|
||||||
|
|
||||||
|
#### Naming styles ####
|
||||||
|
|
||||||
|
# Naming rules
|
||||||
|
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||||
|
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||||
|
|
||||||
|
# Symbol specifications
|
||||||
|
|
||||||
|
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||||
|
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.interface.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||||
|
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.types.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||||
|
|
||||||
|
# Naming styles
|
||||||
|
|
||||||
|
dotnet_naming_style.pascal_case.required_prefix =
|
||||||
|
dotnet_naming_style.pascal_case.required_suffix =
|
||||||
|
dotnet_naming_style.pascal_case.word_separator =
|
||||||
|
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||||
|
dotnet_naming_style.begins_with_i.required_suffix =
|
||||||
|
dotnet_naming_style.begins_with_i.word_separator =
|
||||||
|
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||||
@@ -58,6 +58,8 @@ 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/
|
||||||
|
|
||||||
|
|||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 80
|
||||||
|
EXPOSE 443
|
||||||
|
|
||||||
|
FROM node:22-bookworm-slim AS node
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||||
|
ARG TARGETARCH
|
||||||
|
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 \
|
||||||
|
NUGET_XMLDOC_MODE=skip
|
||||||
|
WORKDIR /app_source
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN dotnet restore "PostFundManagement.slnx"
|
||||||
|
|
||||||
|
WORKDIR "/app_source/PostFundManagement.Api"
|
||||||
|
RUN dotnet build "PostFundManagement.Api.csproj" -c Release -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
RUN dotnet publish "PostFundManagement.Api.csproj" -c Release --no-restore -o /app/publish
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENV ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
|
||||||
|
ENTRYPOINT ["dotnet", "PostFundManagement.Api.dll"]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
PROPRIETARY LICENSE
|
||||||
|
|
||||||
|
Copyright (c) 2026 IFRI TECHNOLOGIES (PTY) Ltd. All rights reserved.
|
||||||
|
|
||||||
|
This software and its associated documentation (the "Software") are the
|
||||||
|
proprietary property of IFRI TECHNOLOGIES (PTY) Ltd.
|
||||||
|
|
||||||
|
The Software is provided for internal use only. Unauthorized copying,
|
||||||
|
distribution, modification, or use of this file via any medium is
|
||||||
|
strictly prohibited.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
@@ -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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Communications;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetApplicantInviteEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/communications/invites/applicant/{applicantUserId:Guid}", async (Guid applicantUserId, ICommunicationsService service,
|
||||||
|
HttpContext context, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetApplicantInviteAsync(applicantUserId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var invite } => Results.Ok(invite),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Gets an applicant's invite")
|
||||||
|
.WithName(typeof(GetApplicantInviteEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Invite>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Communications)
|
||||||
|
.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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Communications;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetApplicantInvitesEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/communications/invites/{applicantUserId:Guid}", async (Guid applicantUserId, ICommunicationsService service,
|
||||||
|
HttpContext context, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetApplicantInvitesAsync(applicantUserId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var invite } => Results.Ok(invite),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Gets an applicant's invite list")
|
||||||
|
.WithName(typeof(GetApplicantInvitesEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Invite[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Communications)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
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;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetInvitesEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/communications/invites/{page:int}/{pageSize:int}", async (ICommunicationsService service,
|
||||||
|
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetInvitesAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var invites } => Results.Ok(invites),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of sent invitations")
|
||||||
|
.WithName(typeof(GetInvitesEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Invite[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Communications)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Communications;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class SendInviteEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/communications/invite", async (InviteRequest request,
|
||||||
|
HttpContext context, ICommunicationsService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.SendInviteAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var invite } => Results.Ok(invite),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Trigger an invite to an organization or candidate (specifying NotificationPlatform)")
|
||||||
|
.WithName(typeof(SendInviteEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Invite>(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Communications);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class CreateEvidenceEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/evidence/file", async (HttpRequest request, IEvidenceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
{
|
||||||
|
var userId = request.HttpContext.GetUserId();
|
||||||
|
|
||||||
|
if (!request.HasFormContentType)
|
||||||
|
return Results.BadRequest("Request must be a multipart form.");
|
||||||
|
|
||||||
|
var form = await request.ReadFormAsync(cancellationToken);
|
||||||
|
var file = form.Files.GetFile("file");
|
||||||
|
|
||||||
|
if (file == null || file.Length == 0)
|
||||||
|
return Results.BadRequest("No file uploaded or file is empty.");
|
||||||
|
|
||||||
|
return await service.CreateEvidenceAsync(userId, form, file, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.RequireAuthorization()
|
||||||
|
.DisableAntiforgery()
|
||||||
|
.WithDescription("Upload supporting evidence file to S3 and register metadata (M14)")
|
||||||
|
.WithName(typeof(CreateEvidenceEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Evidence);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Evidence;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class CreateIndicatorEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/evidence/indicators", async (CreateIndicatorRequest request, HttpContext context,
|
||||||
|
IEvidenceService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateIndicatorAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Define indicators and baselines/targets (M08)")
|
||||||
|
.WithName(typeof(CreateIndicatorEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Evidence);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Evidence;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class CreateMilestoneEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/evidence/milestones", async (CreateMilestoneRequest request, HttpContext context,
|
||||||
|
IEvidenceService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateMilestoneAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Define contractual milestones (M07)")
|
||||||
|
.WithName(typeof(CreateMilestoneRequest).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Evidence);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetEvidenceEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/evidence/{evidenceId}", async (string evidenceId, IEvidenceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetEvidenceAsync(evidenceId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var evidence } => Results.Ok(evidence),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a specific evidence record and assiciated file URLs")
|
||||||
|
.WithName(typeof(GetEvidenceEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Domain.Models.Evidence>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Evidence)
|
||||||
|
.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.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetEvidenceFilesEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/evidence/files/{page:int}/{pageSize:int}", async (IEvidenceService service,
|
||||||
|
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetEvidenceFilesAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var evidences} => Results.Ok(evidences),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of all uploaded evidence files metadata")
|
||||||
|
.WithName(typeof(GetEvidenceFilesEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Evidence)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetIndicatorEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/evidence/indicators/{indicatorId}", async (string indicatorId, IEvidenceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetIndicatorAsync(indicatorId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var indicator } => Results.Ok(indicator),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a specific indicator.")
|
||||||
|
.WithName(typeof(GetIndicatorEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Indicator>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Evidence)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetIndicatorsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/evidence/indicators/{page:int}/{pageSize:int}", async (IEvidenceService service,
|
||||||
|
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetIndicatorsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var indicators } => Results.Ok(indicators),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of indicators")
|
||||||
|
.WithName(typeof(GetIndicatorsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Indicator[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Evidence)
|
||||||
|
.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.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetMilestoneEvidenceEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/evidence/milestone/{milestoneId}", async (string milestoneId, IEvidenceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetMilestoneEvidencesAsync(milestoneId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var evidences } => Results.Ok(evidences),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of all uploaded evidence metadata for a specific mliestone")
|
||||||
|
.WithName(typeof(GetMilestoneEvidenceEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Evidence)
|
||||||
|
.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.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetProjectEvidenceEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/evidence/{projectId}", async (string projectId, IEvidenceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetProjectEvidencesAsync(projectId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var evidence } => Results.Ok(evidence),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of all uploaded evidence metadata for a specific project")
|
||||||
|
.WithName(typeof(GetProjectEvidenceEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Evidence)
|
||||||
|
.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.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class RecordIndicatorAmountEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/evidence/indicators/{indicatorId}/actuals/{actualAmount:decimal}", async (string indicatorId, decimal actualAmount,
|
||||||
|
HttpContext context, IEvidenceService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.RecordIndicatorActualAmountAsync(indicatorId, actualAmount, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ 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 => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Record actual values for indicators (M08)")
|
||||||
|
.WithName(typeof(RecordIndicatorAmountEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Evidence);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Evidence;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Evidence;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class UpdateEvidenceEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/evidence", async (UpdateEvidenceRequest request, HttpContext context, IEvidenceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdateEvidenceAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Updates a specific evidence record metadata")
|
||||||
|
.WithName(typeof(UpdateEvidenceEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Evidence);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class ApproveDisbursementEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/execution/disbursements/{disbursementId}/approve", async (string disbursementId,
|
||||||
|
IExecutionService service, HttpContext context, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.ApproveDisbursementAsync(disbursementId, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ 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 => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Approve disbursement and release payment (M06)")
|
||||||
|
.WithName(typeof(ApproveDisbursementEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Execution);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class CreateDisbursementEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/execution/disbursements", async (CreateDisbursementRequest request,
|
||||||
|
IExecutionService service, HttpContext context, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateDisbursementAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Create a new disbursement schedule (M06)")
|
||||||
|
.WithName(typeof(CreateDisbursementEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Execution);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class CreateRiskEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/execution/risks", async (CreateRiskRequest request, IExecutionService service,
|
||||||
|
HttpContext context, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateRiskAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Add risk to risk register (M09)")
|
||||||
|
.WithName(typeof(CreateRiskEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Execution);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetDisbursementEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/execution/disbursements/{disbursementId}", async (string disbursementId, IExecutionService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetDisbursementAsync(disbursementIdHash: disbursementId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var disbursement } => Results.Ok(disbursement),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a specific disbursement")
|
||||||
|
.WithName(typeof(GetDisbursementEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Disbursement>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Execution)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetDisbursementsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/execution/disbursements", async (IExecutionService service,
|
||||||
|
int page = 1, int pageSize = 10, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetDisbursementsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var disbursements } => Results.Ok(disbursements),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of disbursements")
|
||||||
|
.WithName(typeof(GetDisbursementsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Disbursement[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Execution)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetRiskEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/execution/risks/{riskId}", async (string riskId, IExecutionService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetRiskAsync(riskIdHash: riskId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var risk } => Results.Ok(risk),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a specific risk")
|
||||||
|
.WithName(typeof(GetRiskEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Risk>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.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.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetRisksEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/execution/risks", async (IExecutionService service,
|
||||||
|
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetRisksAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var risks } => Results.Ok(risks),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of all risks")
|
||||||
|
.WithName(typeof(GetRisksEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Execution)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class UpdateDisbursementEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/execution/disbursements", async (UpdateDisbursementRequest request, HttpContext context,
|
||||||
|
IExecutionService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdateDisbursementAsync(request, userId: 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 disbursement")
|
||||||
|
.WithName(typeof(UpdateDisbursementEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Execution;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class UpdateRiskEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/execution/risks", async (UpdateRiskRequest request,
|
||||||
|
IExecutionService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdateRiskAsync(request, 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 risk")
|
||||||
|
.WithName(typeof(UpdateRiskEndpoint).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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class CreateOrganisationEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/governance/organisations", async (CreateOrganisationRequest request, HttpContext context,
|
||||||
|
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateOrganisationAsync(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("Create a new organization profile")
|
||||||
|
.WithName(typeof(CreateOrganisationEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Governance);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class CreatePortfolioEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/governance/organisations/portfolio", async (CreatePortfolioRequest request, HttpContext context,
|
||||||
|
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreatePortfolioAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
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("Create a new organization portfolio")
|
||||||
|
.WithName(typeof(CreatePortfolioEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces<Problems>(StatusCodes.Status404NotFound)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Governance);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Governance;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class DashboardEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/dashboard", async (IGovernanceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetDashboardAsync(cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var dashboard } => Results.Ok(dashboard),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get executive portfolio dashboard metrics and KPIs")
|
||||||
|
.WithName(typeof(DashboardEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Dashboard>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetInstrumentsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/instruments/{programmeId}", async (string programmeId, IGovernanceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetInstrumentsAsync(programmeId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var instrumenrs } => Results.Ok(instrumenrs),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Get all funding instruments")
|
||||||
|
.WithName(typeof(GetInstrumentsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Instrument[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetOrganisationEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/organisations/{organisationId}", async (string organisationId,
|
||||||
|
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetOrganisationAsync(organisationId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var organisation } => Results.Ok(organisation),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get organization profile by ID")
|
||||||
|
.WithName(typeof(GetOrganisationEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Organisation>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetOrganisationsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/organisations", async (IGovernanceService service,
|
||||||
|
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetOrganisationsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var orgnanisations } => Results.Ok(orgnanisations),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a paginated directory of organizations")
|
||||||
|
.WithName(typeof(GetOrganisationsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Organisation[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetPortfolioEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/portfolios/{portfolioId}", async (string portfolioId, IGovernanceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetPortfolioAsync(portfolioIdHash: portfolioId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var portfolio } => Results.Ok(portfolio),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a specific portfolios")
|
||||||
|
.WithName(typeof(GetPortfolioEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Portfolio[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces<Portfolio[]>(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetPortfolioProgrammesEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/portfolios/{portfolioId}/programmes", async (string portfolioId, IGovernanceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetPortfolioProgrammesAsync(portfolioId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var portfolios } => Results.Ok(portfolios),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a programme's portfolios")
|
||||||
|
.WithName(typeof(GetPortfolioProgrammesEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Programme[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetPortfoliosEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/organisations/{organisationId}/portfolio", async (string organisationId,
|
||||||
|
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetPorfoliosAsync(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")
|
||||||
|
.WithName(typeof(GetPortfoliosEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Portfolio[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetProgrammesEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/governance/programmes/{portfolioId}", async (string portfolioId, IGovernanceService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetProgrammesAsync(portfolioId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var programmes } => Results.Ok(programmes),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get all programmes")
|
||||||
|
.WithName(typeof(GetProgrammesEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Programme[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class UpdateOrganisationEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/governance/organisations", async (UpdateOrganisationRequest request,
|
||||||
|
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdateOrganisationAsync(request, 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 an existing organization profile")
|
||||||
|
.WithName(typeof(UpdateOrganisationEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Governance);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Governance;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class UpdatePortfolioEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/governance/organisations/portfolio", async (UpdatePortfolioRequest request, HttpContext context,
|
||||||
|
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdatePortfolioAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Update an existing organization portfolio")
|
||||||
|
.WithName(typeof(UpdatePortfolioEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Governance);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Grants;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class CreateAwardEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/grants/awards", async (CreateAwardRequest request, HttpContext context,
|
||||||
|
IGrantsService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateAwardAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
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("Register an approved funding award (M01)")
|
||||||
|
.WithName(typeof(CreateAwardEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Grants);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Grants;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class CreateBudgetEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/grants/budgets", async (CreateBudgetRequest request, HttpContext context,
|
||||||
|
IGrantsService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateBudgetAsync(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<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Create a new budget line (M05)")
|
||||||
|
.WithName(typeof(CreateBudgetEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Grants);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Grants;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class CreateContractEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/grants/contracts", async (CreateContractRequest request, HttpContext context,
|
||||||
|
IGrantsService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateContractAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Create a contract record from an approved award (M02)")
|
||||||
|
.WithName(typeof(CreateContractEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Grants);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Grants;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class ExecuteContractEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/grants/contracts/execute", async (ExecuteContractRequest request, HttpContext context,
|
||||||
|
IGrantsService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.ExecuteContractAsync(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("Execute the contract and lock the contractual version")
|
||||||
|
.WithName(typeof(ExecuteContractEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Grants);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetAwardEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/grants/awards/{awardId}", async (string awardId, IGrantsService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetAwardAsync(awardId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var award } => Results.Ok(award),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get award by ID")
|
||||||
|
.WithName(typeof(GetAwardEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Award>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Grants)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetAwardsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/grants/awards", async (IGrantsService service,
|
||||||
|
int page = 1, int pageSize = 10, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetAwardsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var awards } => Results.Ok(awards),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of awards")
|
||||||
|
.WithName(typeof(GetAwardsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Award[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Grants)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetBudgetEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/grants/budgets/{budgetId}", async (string budgetId, IGrantsService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetBudgetAsync(budgetId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var budget } => Results.Ok(budget),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get budget by ID")
|
||||||
|
.WithName(typeof(GetBudgetEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Budget>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Grants)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetBudgetsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/grants/budgets", async (IGrantsService service,
|
||||||
|
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetBudgetsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var budgets } => Results.Ok(budgets),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get all budgets")
|
||||||
|
.WithName(typeof(GetBudgetsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Budget[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Grants)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetContractEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/grants/contracts/{contractId}", async (string contractId, IGrantsService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetContractAsync(contractId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var contract } => Results.Ok(contract),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get contract by ID")
|
||||||
|
.WithName(typeof(GetContractEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Contract>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Grants)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Grants;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetContractsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/grants/contracts", async (IGrantsService service,
|
||||||
|
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetContractsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var contracts } => Results.Ok(contracts),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get all contracts")
|
||||||
|
.WithName(typeof(GetContractsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Contract[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Grants)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Projects;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class CreateActivityEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/projects/activities", async (CreateActivityRequest request,
|
||||||
|
HttpContext context, IProjectService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateActivityAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } result => Results.Ok(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Add a new activity under project (M04)")
|
||||||
|
.WithName(typeof(CreateActivityEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Projects);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Projects;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class CreateProgrammeEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/projects/programme", async (CreateProgrammeRequest request, HttpContext context,
|
||||||
|
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateProgrammeAsync(request, userId: context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Create programme")
|
||||||
|
.WithName(typeof(CreateProgrammeEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Projects);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Projects;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class CreateProjectEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/projects", async (CreateProjectRequest request, HttpContext context,
|
||||||
|
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.CreateProjectAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } result => Results.Ok(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
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 project (M04)")
|
||||||
|
.WithName(typeof(CreateProjectEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Projects);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetActivitiesEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/projects/{projectId}/activities", async (string projectId,
|
||||||
|
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetActivitiesAsync(projectId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var activities } => Results.Ok(activities),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of activities for a project")
|
||||||
|
.WithName(typeof(GetActivitiesEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Domain.Models.Activity[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Projects)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetActivityEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/projects/activities/{activityId}", async (string activityId,
|
||||||
|
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetActivityAsync(activityId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var activity } => Results.Ok(activity),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Gets a specific activity by it's id")
|
||||||
|
.WithName(typeof(GetActivityEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Domain.Models.Activity>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Projects)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetMilestoneEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/projects/milestones/{milestoneId}", async (string milestoneId, IProjectService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetMilestoneAsync(milestoneId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var milestone } => Results.Ok(milestone),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Gets a specific milestone")
|
||||||
|
.WithName(typeof(GetMilestoneEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Milestone>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Projects)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetMilestonesEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/projects/milestones/{page:int}/{pageSize:int}", async (IProjectService service,
|
||||||
|
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetMilestonesAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var milestones } => Results.Ok(milestones),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of all milestones")
|
||||||
|
.WithName(typeof(GetMilestonesEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Milestone[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Projects)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetProgrammeEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/projects/programme/{programmeId}", async (string programmeId, IProjectService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetProgrammeAsync(programmeId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var programme } => Results.Ok(programme),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get specific programme")
|
||||||
|
.WithName(typeof(GetProgrammeEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Programme>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Projects)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetProgrammeProjectsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/projects/{programmeId}/programme", async (string programmeId,
|
||||||
|
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetProgrammeProjectsAsync(programmeId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var projects } => Results.Ok(projects),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of all projects under a programme")
|
||||||
|
.WithName(typeof(GetProgrammeProjectsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Project[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Projects)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetProgrammesEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/projects/programme/{page:int}/{pageSize:int}", async (IProjectService service,
|
||||||
|
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetProgrammesAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var programmes } => Results.Ok(programmes),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of all programmes")
|
||||||
|
.WithName(typeof(GetProgrammesEndpoint).ToEndpointName("Projects"))
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Programme[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Projects)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetProjectEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/projects/{projectId}", async (string projectId, IProjectService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetProjectAsync(projectId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var project } => Results.Ok(project),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get project by ID")
|
||||||
|
.WithName(typeof(GetProjectEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Project[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Projects)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetProjectIndicatorsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/projects/indicators/project/{projectId}", async (string projectId, IProjectService service,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetProjectIndicatorsAsync(projectIdHash: projectId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var indicators } => Results.Ok(indicators),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get get a detailed list of project indicators")
|
||||||
|
.WithName(typeof(GetProjectIndicatorsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Indicator[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Projects)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetProjectMilestonesEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/projects/{projectId}/milestones", async (string projectId,
|
||||||
|
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetProjectMilestonesAsync(projectId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var milestones } => Results.Ok(milestones),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of milestones for a project")
|
||||||
|
.WithName(typeof(GetProjectMilestonesEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Milestone[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Projects)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetProjectRisksEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/projects/{projectId}/risks", async (string projectId,
|
||||||
|
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetProjectRisksAsync(projectId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var risks } => Results.Ok(risks),
|
||||||
|
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of risks for a project")
|
||||||
|
.WithName(typeof(GetProjectRisksEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Risk[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Projects)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetProjectsEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/projects", async (IProjectService service,
|
||||||
|
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetProjectsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var projects } => Results.Ok(projects),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Get a list of projects")
|
||||||
|
.WithName(typeof(GetProjectsEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<Project[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Projects)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Projects;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class UpdateMilestoneEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/projects/milestone", async (UpdateMilestoneRequest request, IProjectService service,
|
||||||
|
HttpContext context, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdateMilestoneAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Update project milestone details")
|
||||||
|
.WithName(typeof(UpdateMilestoneEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Projects);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Projects;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class UpdateProgrammeEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/projects/programme", async (UpdateProgrammeRequest request, HttpContext context,
|
||||||
|
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdateProgrammeAsync(request, userId: context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Update existing programme")
|
||||||
|
.WithName(typeof(UpdateProgrammeEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Projects);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Projects;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class UpdateProjectEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/projects", async (UpdateProjectRequest request,
|
||||||
|
IProjectService service, HttpContext context, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdateProjectAsync(request, userId: 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 project details")
|
||||||
|
.WithName(typeof(UpdateProjectEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.WithTags(EndpointTags.Projects);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Projects;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Projects;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class UpdateProjectStatusEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPut("api/projects/status", async (UpdateProjectStatusRequest request, HttpContext context,
|
||||||
|
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdateProjectStatusAsync(request, context.GetUserId(), cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Update project status (M04)")
|
||||||
|
.WithName(typeof(UpdateProjectStatusEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Projects);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Tools;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class EncodeIdEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/tools/hashid/{id64:long}", async (long id64, IToolService service,
|
||||||
|
CancellationToken cancellationToken) =>
|
||||||
|
await service.EncodeLong(id64, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var hash } => Results.Ok(hash),
|
||||||
|
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Hashes an int64 identifier")
|
||||||
|
.WithName(typeof(EncodeIdEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<string>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces(StatusCodes.Status403Forbidden)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Tools)
|
||||||
|
.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;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Users;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class GetUserEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/users/{userId:Guid}", async (Guid userId, IUserService service,
|
||||||
|
HttpContext context, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetUserAsync(userId, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var user } => Results.Ok(user),
|
||||||
|
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Gets the metadata of a single user.")
|
||||||
|
.WithName(typeof(GetUserEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<User>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Users)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Users;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Users;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class GetUsersEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/users/{page:int}/{pageSize:int}", async (UserFilterRequest request, IUserService service,
|
||||||
|
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.GetUsersAsync(Pagination.Create(page, pageSize), request, cancellationToken) switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true, Value: var users } => Results.Ok(users),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
})
|
||||||
|
.WithDescription("Paginated, searchable directory of system users. Supports filtering by ActivityStatus, searching by Email, and sorting")
|
||||||
|
.WithName(typeof(GetUsersEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces<User[]>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Users)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Services.Communications.Events;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Users;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public class SyncUserEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapPost("api/integration/sync", async (IJobOrchestrator jobOrchestrator,
|
||||||
|
HttpContext context, IUserService service, CancellationToken cancellationToken = default) =>
|
||||||
|
{
|
||||||
|
var result = await service.SyncUserAsync(context.GetUserId(), context.GetUserEmail(), cancellationToken);
|
||||||
|
|
||||||
|
if (result.IsSuccess)
|
||||||
|
await jobOrchestrator.SendAsync(SyncUserEvent.Create(), cancellationToken);
|
||||||
|
|
||||||
|
return result switch
|
||||||
|
{
|
||||||
|
{ IsSuccess: true } => Results.Ok(),
|
||||||
|
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||||||
|
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.WithDescription("Trigger integration and data synchronization in the background")
|
||||||
|
.WithName(typeof(SyncUserEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Users)
|
||||||
|
.CacheOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Security;
|
||||||
|
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Api.Endpoints.Users;
|
||||||
|
|
||||||
|
[ApiVersionTarget(1)]
|
||||||
|
public sealed class UpdateUserStatusEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public void Map(IEndpointRouteBuilder builder)
|
||||||
|
{
|
||||||
|
builder.MapGet("api/users/{userId:Guid}/status/{status:int}", async (Guid userId, ActivityStatus status,
|
||||||
|
IUserService service, CancellationToken cancellationToken = default) =>
|
||||||
|
await service.UpdateUserStatusAsync(userId, status, 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("Changes the Activity status of the user in the system")
|
||||||
|
.WithName(typeof(UpdateUserStatusEndpoint).ToEndpointName())
|
||||||
|
.MapToApiVersion(new ApiVersion(1))
|
||||||
|
.Produces(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status401Unauthorized)
|
||||||
|
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||||
|
.WithTags(EndpointTags.Users);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,27 +5,103 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<UserSecretsId>59a05094-4ac2-472d-af04-3407e909432d</UserSecretsId>
|
<UserSecretsId>59a05094-4ac2-472d-af04-3407e909432d</UserSecretsId>
|
||||||
|
<AssemblyOriginatorKeyFile>..\PostFundManagement.snk</AssemblyOriginatorKeyFile>
|
||||||
|
<SignAssembly>True</SignAssembly>
|
||||||
|
<PackageLicenseFile>..\LICENSE</PackageLicenseFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.11" />
|
<NoWarn>1701;1702;AV0029;AV0030;</NoWarn>
|
||||||
<PackageReference Include="Microsoft.OpenApi" Version="2.12.0" />
|
</PropertyGroup>
|
||||||
<PackageReference Include="Scalar.AspNetCore" Version="2.16.20" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Global Usings -->
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702;AV0029;AV0030;</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Security (IODC)-->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Using Include="System.Security.Cryptography.X509Certificates" />
|
<PackageReference Include="IdentityModel.AspNetCore" Version="4.3.0" />
|
||||||
|
<PackageReference Include="IdentityModel.AspNetCore.OAuth2introspection" Version="6.2.0" />
|
||||||
|
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
||||||
|
<PackageReference Include="IdentityModel" Version="6.2.0" />
|
||||||
|
<PackageReference Include="KubernetesClient" Version="19.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Certificate" Version="10.0.11" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.11" />
|
||||||
|
|
||||||
<Using Include="Microsoft.AspNetCore.Authentication" />
|
<Using Include="Microsoft.AspNetCore.Authentication" />
|
||||||
<Using Include="Microsoft.AspNetCore.Authentication.Cookies" />
|
<Using Include="Microsoft.AspNetCore.Authentication.Cookies" />
|
||||||
<Using Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" />
|
<Using Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
|
||||||
|
<Using Include="Microsoft.IdentityModel.Tokens" />
|
||||||
|
<Using Include="System.Security.Claims" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Health Checks -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="9.0.0" />
|
||||||
|
|
||||||
|
<Using Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- API Versioning -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AccessTokenClient.Extensions" Version="5.1.0" />
|
||||||
|
<PackageReference Include="Asp.Versioning.Abstractions" Version="10.2.1" />
|
||||||
|
<PackageReference Include="Asp.Versioning.Http" Version="10.2.3" />
|
||||||
|
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="10.2.1" />
|
||||||
|
|
||||||
|
<Using Include="Asp.Versioning" />
|
||||||
|
<Using Include="Asp.Versioning.Builder" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- API Documentation -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.11" />
|
||||||
|
<PackageReference Include="Scalar.AspNetCore" Version="2.17.2" />
|
||||||
|
|
||||||
|
<Using Include="Scalar.AspNetCore" />
|
||||||
|
<Using Include="Microsoft.OpenApi" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.OpenApi" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- file nesting -->
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectCapability Include="ConfigurableFileNesting" />
|
||||||
|
<ProjectCapability Include="ConfigurableFileNestingFeatureEnabled" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- CQRS -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Mediator.SourceGenerator" Version="3.0.2">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
|
||||||
|
<Using Include="FluentResults" />
|
||||||
|
<Using Include="Mediator" />
|
||||||
|
<Using Include="Quartz" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Shared Global Usings -->
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="System.Web" />
|
||||||
|
<Using Include="System.Diagnostics" />
|
||||||
|
<Using Include="System.Reflection" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.Mvc" />
|
||||||
|
<Using Include="Microsoft.EntityFrameworkCore" />
|
||||||
|
<Using Include="System.ComponentModel.DataAnnotations" />
|
||||||
|
<Using Include="Microsoft.Extensions.DependencyInjection.Extensions" />
|
||||||
|
<Using Include="System.Security.Cryptography.X509Certificates" />
|
||||||
<Using Include="Microsoft.AspNetCore.DataProtection" />
|
<Using Include="Microsoft.AspNetCore.DataProtection" />
|
||||||
<Using Include="Refit" />
|
<Using Include="Refit" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\PostFundManagement.Domain\PostFundManagement.Domain.csproj" />
|
<ProjectReference Include="..\PostFundManagement.Application\PostFundManagement.Application.csproj" />
|
||||||
<ProjectReference Include="..\PostFundManagement.Infrastructure\PostFundManagement.Infrastructure.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,22 +1,177 @@
|
|||||||
using Scalar.AspNetCore;
|
using PostFundManagement.Application.Extensions;
|
||||||
|
using PostFundManagement.Application.Filters;
|
||||||
|
using PostFundManagement.Domain.Extensions;
|
||||||
|
using PostFundManagement.Domain.Health;
|
||||||
|
using PostFundManagement.Domain.Mediator;
|
||||||
|
using static PostFundManagement.Domain.Extensions.Constants;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddOpenApi();
|
builder.Services.AddEndpointsApiExplorer()
|
||||||
|
.AddApiVersioning(options => options.ApiVersionReader = new HeaderApiVersionReader());
|
||||||
|
|
||||||
|
builder.Services.AddApiVersioning().AddApiExplorer();
|
||||||
|
builder.Services.AddScoped<ActiveUserFilter>();
|
||||||
|
|
||||||
|
builder.Services.AddEndpoints(Assembly.GetExecutingAssembly());
|
||||||
|
builder.Services.AddApiServices(builder.Configuration);
|
||||||
|
builder.Services.AddSharedServices();
|
||||||
|
builder.Services.AddRegisterServicesWithCaching(builder.Configuration);
|
||||||
|
builder.Services.AddEndpointAndServiceCaching(builder.Configuration);
|
||||||
|
|
||||||
|
builder.Services.AddMediator();
|
||||||
|
builder.Services.AddSecurityApiSdk(builder.Configuration);
|
||||||
|
builder.Services.AddApiSecurity(builder.Configuration);
|
||||||
|
|
||||||
|
builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(TelemetryPipelineBehavior<,>));
|
||||||
|
builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingPipelineBehavior<,>));
|
||||||
|
|
||||||
|
builder.Services.AddQuartzScheduler(DefaultSchedulerName, builder.Configuration);
|
||||||
|
builder.Services.AddEmailServices(builder.Configuration);
|
||||||
|
builder.Services.AddHashServices(builder.Configuration);
|
||||||
|
builder.Services.AddGarageS3(builder.Configuration);
|
||||||
|
|
||||||
|
builder.Services.AddHttpClient();
|
||||||
|
builder.Services.AddDataProtectionDatabase(builder.Configuration);
|
||||||
|
builder.Services.AddApplicationDbContext(builder.Configuration);
|
||||||
|
|
||||||
|
builder.Services.AddHealthChecks()
|
||||||
|
.AddCheck<QuartzHealthCheck>("QuartzScheduler")
|
||||||
|
.AddCheck<PostgresHealthCheck>("PostgresDatabase")
|
||||||
|
.AddCheck("Self", () => HealthCheckResult.Healthy());
|
||||||
|
|
||||||
|
IEnumerable<string> identityScopes = ["openid", "profile", "roles", "email", "address"];
|
||||||
|
|
||||||
|
builder.Services.AddValidation()
|
||||||
|
.AddEndpointValidation();
|
||||||
|
|
||||||
|
builder.Services.AddOpenApi(options =>
|
||||||
|
{
|
||||||
|
options.AddDocumentTransformer((document, context, cancellationToken) =>
|
||||||
|
{
|
||||||
|
document.Components ??= new OpenApiComponents();
|
||||||
|
document.Components?.SecuritySchemes!["OAuth2"] = new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Type = SecuritySchemeType.OAuth2,
|
||||||
|
Description = "Interactive OAuth2 flow for authentication and authorization",
|
||||||
|
Flows = new OpenApiOAuthFlows
|
||||||
|
{
|
||||||
|
AuthorizationCode = new OpenApiOAuthFlow
|
||||||
|
{
|
||||||
|
AuthorizationUrl = new Uri($"{builder.Configuration["SecuritySettings:Authority"]}/connect/authorize"),
|
||||||
|
TokenUrl = new Uri($"{builder.Configuration["SecuritySettings:Authority"]}/connect/token"),
|
||||||
|
Scopes = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "openid", "OpenID Connect" },
|
||||||
|
{ "profile", "User Profile" },
|
||||||
|
{ "roles", "User Roles" },
|
||||||
|
{ "email", "User Email" },
|
||||||
|
{ "address", "User Address" },
|
||||||
|
{ builder.Configuration["SecuritySettings:Audience"]!, "PFM API Scope" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
});
|
||||||
|
|
||||||
|
options.AddOperationTransformer((operation, context, cancellationToken) =>
|
||||||
|
{
|
||||||
|
operation.Security ??= [];
|
||||||
|
|
||||||
|
var schemeReference = new OpenApiSecuritySchemeReference("OAuth2");
|
||||||
|
|
||||||
|
operation.Security.Add(new OpenApiSecurityRequirement
|
||||||
|
{
|
||||||
|
[schemeReference] = [.. identityScopes, builder.Configuration["SecuritySettings:Audience"]!]
|
||||||
|
});
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddCors(options => options.AddPolicy("AllowFrontend", policy =>
|
||||||
|
policy.WithOrigins("https://localhost:3000", "https://localhost:8076", "https://console.ifri.khongisa.co.za")
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowCredentials()));
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
if (app.Environment.IsDevelopment())
|
var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>();
|
||||||
{
|
var scheduler = await schedulerFactory.GetScheduler(DefaultSchedulerName);
|
||||||
app.MapScalarApiReference(options =>
|
|
||||||
{
|
|
||||||
options.WithTitle("Post-fund Management API")
|
|
||||||
.WithTheme(ScalarTheme.BluePlanet);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.MapOpenApi();
|
if (!scheduler!.IsStarted)
|
||||||
}
|
await scheduler.Start();
|
||||||
|
|
||||||
|
app.UseHsts();
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseRouting();
|
||||||
|
app.UseCors("AllowFrontend");
|
||||||
|
app.UseOutputCache();
|
||||||
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
ApiVersionSet versionSet = app.NewApiVersionSet("v1")
|
||||||
|
.HasApiVersion(new ApiVersion(1))
|
||||||
|
.ReportApiVersions()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var versionGroups = new Dictionary<int, RouteGroupBuilder>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
1, app.MapGroup("v{version:apiVersion}").WithApiVersionSet(versionSet)
|
||||||
|
.RequireAuthorization("ApiScope").AddEndpointFilter<ActiveUserFilter>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
app.MapEndpoints(versionGroups);
|
||||||
|
|
||||||
|
app.UseHealthChecks("/health", new HealthCheckOptions
|
||||||
|
{
|
||||||
|
Predicate = _ => true,
|
||||||
|
AllowCachingResponses = true,
|
||||||
|
ResponseWriter = HealthChecks.UI.Client.UIResponseWriter.WriteHealthCheckUIResponse
|
||||||
|
});
|
||||||
|
|
||||||
|
app.MapHealthChecksUI(options => { options.UIPath = "/healthui"; });
|
||||||
|
app.UseHealthChecks("/ready");
|
||||||
|
|
||||||
|
app.MapOpenApi();
|
||||||
|
|
||||||
|
var apiVersions = app.DescribeApiVersions()
|
||||||
|
.OrderByDescending(o => o.ApiVersion.MajorVersion)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var description in app.DescribeApiVersions().OrderByDescending(o => o.ApiVersion.MajorVersion))
|
||||||
|
{
|
||||||
|
app.MapScalarApiReference($"/openapi/{description.GroupName}", (options, context) =>
|
||||||
|
{
|
||||||
|
options.AddServer(new ScalarServer($"https://{context.Request.Host}"));
|
||||||
|
options.WithOpenApiRoutePattern($"/openapi/{description.GroupName}.json");
|
||||||
|
options.WithTheme(ScalarTheme.DeepSpace);
|
||||||
|
options.Agent = new ScalarAgentOptions { Disabled = true };
|
||||||
|
options.AddPreferredSecuritySchemes("OAuth2")
|
||||||
|
.AddOAuth2Flows("OAuth2", flow =>
|
||||||
|
{
|
||||||
|
flow.AuthorizationCode = new AuthorizationCodeFlow
|
||||||
|
{
|
||||||
|
ClientId = builder.Configuration["SecuritySettings:ClientId"]!,
|
||||||
|
Pkce = Pkce.Sha256,
|
||||||
|
SelectedScopes = [.. identityScopes, builder.Configuration["SecuritySettings:Audience"]!]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var latestVersionGroup = apiVersions.FirstOrDefault()?.GroupName ?? "v1";
|
||||||
|
|
||||||
|
app.MapGet("/", () => Results.Redirect($"/openapi/{latestVersionGroup}"))
|
||||||
|
.ExcludeFromDescription();
|
||||||
|
|
||||||
|
if (!app.Environment.IsDevelopment())
|
||||||
|
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
@@ -1,5 +1,27 @@
|
|||||||
{
|
{
|
||||||
"MachineIdentity": {
|
"GeneralS3Settings": {
|
||||||
|
"ServiceUrl": "http://192.168.1.177:30900",
|
||||||
|
"Region": "garage",
|
||||||
|
"BucketName": "pfm.general",
|
||||||
|
"CdnBaseUrl": "https://pfm.general.cdn.khongisa.co.za"
|
||||||
|
},
|
||||||
|
"ContractS3Settings": {
|
||||||
|
"ServiceUrl": "http://192.168.1.177:30900",
|
||||||
|
"Region": "garage",
|
||||||
|
"BucketName": "pfm.general",
|
||||||
|
"CdnBaseUrl": "https://pfm.general.cdn.khongisa.co.za"
|
||||||
|
},
|
||||||
|
"EvidenceS3Settings": {
|
||||||
|
"ServiceUrl": "http://192.168.1.177:30900",
|
||||||
|
"Region": "garage",
|
||||||
|
"BucketName": "pfm.general",
|
||||||
|
"CdnBaseUrl": "https://pfm.general.cdn.khongisa.co.za"
|
||||||
|
},
|
||||||
|
"Monitoring": {
|
||||||
|
"Address": "http://aspire-dashboard-service.aspire.svc.cluster.local:18889",
|
||||||
|
"ServiceName": "Pfm.DEV"
|
||||||
|
},
|
||||||
|
"SecuritySettings": {
|
||||||
"Authority": "https://sts.security.khongisa.co.za",
|
"Authority": "https://sts.security.khongisa.co.za",
|
||||||
"Audience": "pfm-api-dev"
|
"Audience": "pfm-api-dev"
|
||||||
},
|
},
|
||||||
@@ -9,5 +31,15 @@
|
|||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"EmailProcessorCron": "0 */5 * * * ?",
|
||||||
|
"Email": {
|
||||||
|
"SmtpServer": "smtp.example.com",
|
||||||
|
"SmtpPort": 587,
|
||||||
|
"SenderEmail": "noreply@example.com",
|
||||||
|
"SenderName": "Example"
|
||||||
|
},
|
||||||
|
"HasherSettings": {
|
||||||
|
"MinHashLength": 11
|
||||||
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*"
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Timezones;
|
using static PostFundManagement.Domain.Extensions.Timezones;
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Abstractions;
|
namespace PostFundManagement.Application.Abstractions;
|
||||||
|
|
||||||
public abstract class EventBase
|
public abstract class EventBase
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace PostFundManagement.Application.Abstractions;
|
||||||
|
|
||||||
|
public interface IApplication;
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
using PostFundManagement.Application.Services.Shared;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Abstractions;
|
||||||
|
|
||||||
|
public abstract class S3ServiceBase(IAmazonS3 amazonS3)
|
||||||
|
{
|
||||||
|
protected readonly IAmazonS3 Client = amazonS3;
|
||||||
|
|
||||||
|
protected abstract string BucketName { get; }
|
||||||
|
protected abstract string CdnBaseUrl { get; }
|
||||||
|
|
||||||
|
public virtual async ValueTask<Result<string>> UploadFileAsync(string fileName, Stream fileStream, string contentType, CancellationToken cancellationToken = default) =>
|
||||||
|
await Result.Try((Func<ValueTask<Result<string>>>)(async () =>
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(BucketName))
|
||||||
|
return Result.Fail<string>(Domain.Errors.ValidationError.Create("Bucket name is not configured."));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(CdnBaseUrl))
|
||||||
|
return Result.Fail<string>(Domain.Errors.ValidationError.Create("CDN base URL is not configured."));
|
||||||
|
|
||||||
|
await using var stream = new MemoryStream();
|
||||||
|
|
||||||
|
await fileStream.CopyToAsync(stream, cancellationToken);
|
||||||
|
await fileStream.DisposeAsync();
|
||||||
|
|
||||||
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var fileHash = HashService.StreamToSha256Hash(stream);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(fileHash))
|
||||||
|
return Result.Fail<string>(CreateError.Create("Failed to compute file hash."));
|
||||||
|
|
||||||
|
var fileKey = $"{fileHash.ToLower(CultureInfo.InvariantCulture)}{Path.GetExtension(fileName)}";
|
||||||
|
|
||||||
|
var putRequest = new PutObjectRequest
|
||||||
|
{
|
||||||
|
BucketName = BucketName,
|
||||||
|
Key = fileKey,
|
||||||
|
InputStream = stream,
|
||||||
|
ContentType = contentType,
|
||||||
|
UseChunkEncoding = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var response = await Client.PutObjectAsync(putRequest, cancellationToken);
|
||||||
|
|
||||||
|
return response.HttpStatusCode != HttpStatusCode.OK
|
||||||
|
? Result.Fail<string>(CreateError.Create($"Failed to upload {fileName} to S3."))
|
||||||
|
: Result.Ok($"{CdnBaseUrl}/{fileKey}");
|
||||||
|
}));
|
||||||
|
|
||||||
|
public virtual async ValueTask<Result> DeleteFileAsync(string fileKey, CancellationToken cancellationToken = default) =>
|
||||||
|
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(BucketName))
|
||||||
|
return Result.Fail(NotFoundError.Create("Bucket name is not configured."));
|
||||||
|
|
||||||
|
var response = await Client.DeleteObjectAsync(new DeleteObjectRequest
|
||||||
|
{
|
||||||
|
BucketName = BucketName,
|
||||||
|
Key = fileKey
|
||||||
|
}, cancellationToken);
|
||||||
|
|
||||||
|
return Result.FailIf(response.HttpStatusCode == HttpStatusCode.NoContent,
|
||||||
|
UpdateError.Create($"Failed to delete {fileKey} from S3."));
|
||||||
|
}));
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace PostFundManagement.Application.Abstractions;
|
||||||
|
|
||||||
|
public abstract class ServiceCacheBase
|
||||||
|
{
|
||||||
|
protected readonly DistributedCacheEntryOptions cacheOptions = new()
|
||||||
|
{
|
||||||
|
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(2)
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using PostFundManagement.Application.Services.Communications;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
public interface ICommunicationsService
|
||||||
|
{
|
||||||
|
ValueTask<Result<Invite>> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Invite[]>> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Invite>> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Invite[]>> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using PostFundManagement.Application.Services.Evidence;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
public interface IEvidenceService
|
||||||
|
{
|
||||||
|
ValueTask<Result> CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Indicator[]>> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Evidence[]>> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Indicator>> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
ValueTask<Result<Evidence>> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Evidence[]>> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Evidence[]>> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using PostFundManagement.Application.Services.Execution;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
public interface IExecutionService
|
||||||
|
{
|
||||||
|
ValueTask<Result<Risk[]>> GetRisksAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Disbursement[]>> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Disbursement>> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Risk>> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using PostFundManagement.Application.Services.Governance;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
public interface IGovernanceService
|
||||||
|
{
|
||||||
|
ValueTask<Result<Programme[]>> GetProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Portfolio[]>> GetPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Organisation[]>> GetOrganisationsAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Organisation>> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Instrument[]>> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Dashboard>> GetDashboardAsync(CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Organisation>> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Programme[]>> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Portfolio>> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using PostFundManagement.Application.Services.Grants;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
public interface IGrantsService
|
||||||
|
{
|
||||||
|
ValueTask<Result<Contract[]>> GetContractsAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Contract>> GetContractAsync(string contractIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Budget[]>> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Budget>> GetBudgetAsync(string budgetIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Award[]>> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Award>> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
public interface IIdentityService
|
||||||
|
{
|
||||||
|
ValueTask<Result> UserIsAuthorisedAsync(Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using PostFundManagement.Application.Services.Projects;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
public interface IProjectService
|
||||||
|
{
|
||||||
|
ValueTask<Result> UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Project[]>> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Project>> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Domain.Models.Activity[]>> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Milestone[]>> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Indicator[]>> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Milestone>> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Milestone[]>> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Domain.Models.Activity>> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Risk[]>> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Programme>> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Programme[]>> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<Project[]>> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
public interface IToolService
|
||||||
|
{
|
||||||
|
ValueTask<Result<string>> EncodeLong(long id, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using PostFundManagement.Application.Services.Users;
|
||||||
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
public interface IUserService
|
||||||
|
{
|
||||||
|
ValueTask<Result> SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<User[]>> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result> UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
ValueTask<Result<User>> GetUserAsync(Guid userId, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
+20
-3
@@ -1,11 +1,28 @@
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Api;
|
using PostFundManagement.Domain.Security;
|
||||||
using PostFundManagement.Domain.Api.Configuration;
|
using PostFundManagement.Domain.Security.Configuration;
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Extensions;
|
namespace PostFundManagement.Application.Extensions;
|
||||||
|
|
||||||
public static class Api
|
public static class Api
|
||||||
{
|
{
|
||||||
|
public static IServiceCollection AddEndpointValidation(this IServiceCollection services) => services.AddValidation();
|
||||||
|
|
||||||
|
public static IServiceCollection AddEndpointAndServiceCaching(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddStackExchangeRedisOutputCache(options =>
|
||||||
|
{
|
||||||
|
options.Configuration = configuration.GetConnectionString("Redis");
|
||||||
|
options.ConfigurationOptions?.ClientName = "PFM-API";
|
||||||
|
options.InstanceName = "PFM-API_";
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddOutputCache(options =>
|
||||||
|
options.AddBasePolicy(builder => builder.Expire(TimeSpan.FromMinutes(2))));
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
public static void ConfigureCookieOidcSameSiteSupport(this IServiceCollection services) =>
|
public static void ConfigureCookieOidcSameSiteSupport(this IServiceCollection services) =>
|
||||||
services.Configure<CookiePolicyOptions>(options =>
|
services.Configure<CookiePolicyOptions>(options =>
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using PostFundManagement.Application.Services.Shared;
|
||||||
|
using PostFundManagement.Domain.Configuration.Email;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Extensions;
|
||||||
|
|
||||||
|
public static class Email
|
||||||
|
{
|
||||||
|
public const string EmailFromName = "PFM Team";
|
||||||
|
public const string EmailFromAddress = "info@pfm.co.za"; // TODO: replace with real address
|
||||||
|
|
||||||
|
public static IServiceCollection AddEmailServices(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.Configure<SmtpSettings>(configuration.GetSection("Email"));
|
||||||
|
|
||||||
|
services.AddSingleton<EmailService>();
|
||||||
|
|
||||||
|
services.AddOpenTelemetry()
|
||||||
|
.WithTracing(tracing => tracing.AddSource("Pfm.EmailService"))
|
||||||
|
.WithMetrics(metrics => metrics.AddMeter("Pfm.EmailService"));
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
namespace PostFundManagement.Application.Extensions;
|
||||||
|
|
||||||
|
public static class General
|
||||||
|
{
|
||||||
|
public static string Clean(this string input) => Patterns.SpaceRegex().Replace(input, string.Empty);
|
||||||
|
|
||||||
|
public static string ToJson<TValue>(this TValue obj) where TValue : class => JsonSerializer.Serialize(obj);
|
||||||
|
|
||||||
|
public static TValue FromJson<TValue>(this string json) where TValue : class => JsonSerializer.Deserialize<TValue>(json)!;
|
||||||
|
|
||||||
|
public static Guid GetUserId(this HttpContext context) => (Guid)context.Items[Domain.Extensions.Constants.UserIdItemKey]!;
|
||||||
|
|
||||||
|
public static string GetUserEmail(this HttpContext context) => (string)context.Items[Domain.Extensions.Constants.EmailtemKey]!;
|
||||||
|
|
||||||
|
public static (bool Decoded, Guid userId) GetUserId(this ClaimsPrincipal principal)
|
||||||
|
{
|
||||||
|
var subClaim = principal.FindFirst(JwtRegisteredClaimNames.Sub)?.Value ?? principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||||
|
|
||||||
|
var decoded = Guid.TryParse(subClaim, out var userId);
|
||||||
|
|
||||||
|
return (decoded, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (bool Decoded, string email) GetUserEmail(this ClaimsPrincipal principal)
|
||||||
|
{
|
||||||
|
var emailClaim = principal.FindFirst("email")?.Value ?? principal.FindFirst(ClaimTypes.Email)?.Value;
|
||||||
|
|
||||||
|
var decoded = !string.IsNullOrWhiteSpace(emailClaim);
|
||||||
|
|
||||||
|
return (decoded, emailClaim!);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Microsoft.AspNetCore.Mvc.ProblemDetails ToProblems(this IReadOnlyList<IError> errors) => new()
|
||||||
|
{
|
||||||
|
Detail = string.Join(',', errors.Select(e => e.Message)),
|
||||||
|
Status = StatusCodes.Status400BadRequest,
|
||||||
|
Title = "Error",
|
||||||
|
};
|
||||||
|
}
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
|
using PostFundManagement.Application.Services.Shared;
|
||||||
using PostFundManagement.Domain.Configuration.Hash;
|
using PostFundManagement.Domain.Configuration.Hash;
|
||||||
using PostFundManagement.Domain.Services;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Extensions;
|
namespace PostFundManagement.Application.Extensions;
|
||||||
|
|
||||||
public static class Hash
|
public static class Hash
|
||||||
{
|
{
|
||||||
+7
-1
@@ -1,4 +1,10 @@
|
|||||||
namespace PostFundManagement.Domain.Extensions;
|
using OpenTelemetry.Exporter;
|
||||||
|
using OpenTelemetry.Logs;
|
||||||
|
using OpenTelemetry.Resources;
|
||||||
|
using OpenTelemetry.Trace;
|
||||||
|
using OpenTelemetry.Metrics;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Extensions;
|
||||||
|
|
||||||
public static class Otel
|
public static class Otel
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace PostFundManagement.Application.Extensions;
|
||||||
|
|
||||||
|
public partial class Patterns
|
||||||
|
{
|
||||||
|
[GeneratedRegex(@"\s+")]
|
||||||
|
public static partial Regex SpaceRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"\A\b[0-9a-fA-F]+\b\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
|
||||||
|
public static partial Regex HexHashRegex { get; }
|
||||||
|
|
||||||
|
[GeneratedRegex(@"\A[0-9a-fA-F]{32}\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
|
||||||
|
public static partial Regex Md5Regex { get; }
|
||||||
|
|
||||||
|
[GeneratedRegex(@"\A[0-9a-fA-F]{64}\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
|
||||||
|
public static partial Regex Sha256Regex { get; }
|
||||||
|
}
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
using PostFundManagement.Infrastructure.Database;
|
using PostFundManagement.Infrastructure.Database;
|
||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
using static PostFundManagement.Domain.Extensions.Constants;
|
||||||
|
|
||||||
namespace PostFundManagement.Infrastructure.Extensions;
|
namespace PostFundManagement.Application.Extensions;
|
||||||
|
|
||||||
public static class Postgres
|
public static class Postgres
|
||||||
{
|
{
|
||||||
+10
-6
@@ -6,8 +6,6 @@ namespace PostFundManagement.Domain.Extensions;
|
|||||||
|
|
||||||
public static class Quartz
|
public static class Quartz
|
||||||
{
|
{
|
||||||
public const string DefaultSchedulerName = "tech-shop";
|
|
||||||
|
|
||||||
public static IServiceCollection AddQuartzSchedulerClient(this IServiceCollection services, string schedulerName, IConfiguration configuration)
|
public static IServiceCollection AddQuartzSchedulerClient(this IServiceCollection services, string schedulerName, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
var connectionString = configuration.GetConnectionString(DatabaseConfigName);
|
var connectionString = configuration.GetConnectionString(DatabaseConfigName);
|
||||||
@@ -29,9 +27,12 @@ public static class Quartz
|
|||||||
|
|
||||||
storage.UseSystemTextJsonSerializer();
|
storage.UseSystemTextJsonSerializer();
|
||||||
storage.SetProperty("quartz.jobStore.clustered", "true");
|
storage.SetProperty("quartz.jobStore.clustered", "true");
|
||||||
storage.SetProperty("quartz.jobStore.tablePrefix", "qrtz_");
|
|
||||||
|
|
||||||
storage.UsePostgres(connectionString!);
|
storage.UsePostgres(options =>
|
||||||
|
{
|
||||||
|
options.ConnectionString = connectionString!;
|
||||||
|
options.TablePrefix = "quartz.qrtz_";
|
||||||
|
});
|
||||||
storage.UseClustering(cluster =>
|
storage.UseClustering(cluster =>
|
||||||
{
|
{
|
||||||
cluster.CheckinInterval = TimeSpan.FromSeconds(30);
|
cluster.CheckinInterval = TimeSpan.FromSeconds(30);
|
||||||
@@ -71,9 +72,12 @@ public static class Quartz
|
|||||||
|
|
||||||
storage.UseSystemTextJsonSerializer();
|
storage.UseSystemTextJsonSerializer();
|
||||||
storage.SetProperty("quartz.jobStore.clustered", "true");
|
storage.SetProperty("quartz.jobStore.clustered", "true");
|
||||||
storage.SetProperty("quartz.jobStore.tablePrefix", "qrtz_");
|
|
||||||
|
|
||||||
storage.UsePostgres(connectionString!);
|
storage.UsePostgres(options =>
|
||||||
|
{
|
||||||
|
options.ConnectionString = connectionString!;
|
||||||
|
options.TablePrefix = "quartz.qrtz_";
|
||||||
|
});
|
||||||
storage.UseClustering(cluster =>
|
storage.UseClustering(cluster =>
|
||||||
{
|
{
|
||||||
cluster.CheckinInterval = TimeSpan.FromSeconds(30);
|
cluster.CheckinInterval = TimeSpan.FromSeconds(30);
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
using PostFundManagement.Application.Services.Shared;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using static PostFundManagement.Domain.Extensions.Constants;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Extensions;
|
||||||
|
|
||||||
|
public static class S3
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddGarageS3(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(configuration.GetSection($"{GeneralS3SettingsSection}:ServiceUrl").Value))
|
||||||
|
{
|
||||||
|
services.AddKeyedSingleton<IAmazonS3, AmazonS3Client>(GeneralBucketName, (provider, client) =>
|
||||||
|
new AmazonS3Client(new BasicAWSCredentials(configuration.GetSection($"{GeneralS3SettingsSection}:AccessKey").Value,
|
||||||
|
configuration.GetSection($"{GeneralS3SettingsSection}:SecretKey").Value),
|
||||||
|
new AmazonS3Config
|
||||||
|
{
|
||||||
|
ServiceURL = configuration.GetSection($"{GeneralS3SettingsSection}:ServiceUrl").Value,
|
||||||
|
AuthenticationRegion = configuration.GetSection($"{GeneralS3SettingsSection}:Region").Value,
|
||||||
|
ForcePathStyle = true,
|
||||||
|
EndpointDiscoveryEnabled = true,
|
||||||
|
UseHttp = configuration.GetSection($"{GeneralS3SettingsSection}:ServiceUrl").Value!.Contains("http://", StringComparison.InvariantCultureIgnoreCase),
|
||||||
|
}));
|
||||||
|
|
||||||
|
services.AddKeyedScoped<IS3Service, GeneralS3Service>(GeneralBucketName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(configuration.GetSection($"{EvidenceS3SettingsSection}:ServiceUrl").Value))
|
||||||
|
{
|
||||||
|
services.AddKeyedSingleton<IAmazonS3, AmazonS3Client>(EvidenceBucketName, (provider, client) =>
|
||||||
|
new AmazonS3Client(new BasicAWSCredentials(configuration.GetSection($"{EvidenceS3SettingsSection}:AccessKey").Value,
|
||||||
|
configuration.GetSection($"{EvidenceS3SettingsSection}:SecretKey").Value),
|
||||||
|
new AmazonS3Config
|
||||||
|
{
|
||||||
|
ServiceURL = configuration.GetSection($"{EvidenceS3SettingsSection}:ServiceUrl").Value,
|
||||||
|
AuthenticationRegion = configuration.GetSection($"{EvidenceS3SettingsSection}:Region").Value,
|
||||||
|
ForcePathStyle = true,
|
||||||
|
EndpointDiscoveryEnabled = true,
|
||||||
|
UseHttp = configuration.GetSection($"{EvidenceS3SettingsSection}:ServiceUrl").Value!.Contains("http://", StringComparison.InvariantCultureIgnoreCase),
|
||||||
|
}));
|
||||||
|
|
||||||
|
services.AddKeyedScoped<IS3Service, EvidenceS3Service>(EvidenceBucketName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(configuration.GetSection($"{ContractS3SettingsSection}:ServiceUrl").Value))
|
||||||
|
{
|
||||||
|
services.AddKeyedSingleton<IAmazonS3, AmazonS3Client>(ContractBucketName, (provider, client) =>
|
||||||
|
new AmazonS3Client(new BasicAWSCredentials(configuration.GetSection($"{ContractS3SettingsSection}:AccessKey").Value,
|
||||||
|
configuration.GetSection($"{ContractS3SettingsSection}:SecretKey").Value),
|
||||||
|
new AmazonS3Config
|
||||||
|
{
|
||||||
|
ServiceURL = configuration.GetSection($"{ContractS3SettingsSection}:ServiceUrl").Value,
|
||||||
|
AuthenticationRegion = configuration.GetSection($"{ContractS3SettingsSection}:Region").Value,
|
||||||
|
ForcePathStyle = true,
|
||||||
|
EndpointDiscoveryEnabled = true,
|
||||||
|
UseHttp = configuration.GetSection($"{ContractS3SettingsSection}:ServiceUrl").Value!.Contains("http://", StringComparison.InvariantCultureIgnoreCase),
|
||||||
|
}));
|
||||||
|
|
||||||
|
services.AddKeyedScoped<IS3Service, ContractS3Service>(ContractBucketName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
-12
@@ -1,12 +1,11 @@
|
|||||||
using PostFundManagement.Domain.Api.Configuration;
|
using PostFundManagement.Application.Services.Shared;
|
||||||
using PostFundManagement.Domain.Extensions;
|
using PostFundManagement.Domain.Security.Configuration;
|
||||||
using PostFundManagement.Domain.Sdk;
|
using PostFundManagement.Domain.Security.Sdk;
|
||||||
using PostFundManagement.Domain.Services;
|
|
||||||
using PostFundManagement.Infrastructure.Database;
|
using PostFundManagement.Infrastructure.Database;
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Extensions;
|
namespace PostFundManagement.Application.Extensions;
|
||||||
|
|
||||||
public static class Api
|
public static class Security
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddSecurityApiSdk(this IServiceCollection services, IConfiguration configuration)
|
public static IServiceCollection AddSecurityApiSdk(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
@@ -42,6 +41,8 @@ public static class Api
|
|||||||
|
|
||||||
public static IServiceCollection AddWebSecurity(this IServiceCollection services, IConfiguration configuration)
|
public static IServiceCollection AddWebSecurity(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
|
services.AddAuthorization();
|
||||||
|
|
||||||
var certString = configuration["DataProtection:Certificate"] ?? configuration["DataProtection__Certificate"];
|
var certString = configuration["DataProtection:Certificate"] ?? configuration["DataProtection__Certificate"];
|
||||||
var certPassword = configuration["DataProtection:Password"] ?? configuration["DataProtection__Password"];
|
var certPassword = configuration["DataProtection:Password"] ?? configuration["DataProtection__Password"];
|
||||||
|
|
||||||
@@ -52,18 +53,18 @@ public static class Api
|
|||||||
|
|
||||||
services.AddDataProtection().PersistKeysToDbContext<DataProtectionDbContext>()
|
services.AddDataProtection().PersistKeysToDbContext<DataProtectionDbContext>()
|
||||||
.ProtectKeysWithCertificate(certificate)
|
.ProtectKeysWithCertificate(certificate)
|
||||||
.SetApplicationName("LiteCharmsApp");
|
.SetApplicationName("PfmApp");
|
||||||
|
|
||||||
services.Configure<DataProtectionOptions>(options => options.ApplicationDiscriminator = "LiteCharmsApp");
|
services.Configure<DataProtectionOptions>(options => options.ApplicationDiscriminator = "PfmApp");
|
||||||
|
|
||||||
services.ConfigureCookieOidcSameSiteSupport();
|
services.ConfigureCookieOidcSameSiteSupport();
|
||||||
|
|
||||||
var configSection = configuration.GetSection(nameof(SecuritySettings));
|
var configSection = configuration.GetSection(nameof(SecurityClientSettings));
|
||||||
|
|
||||||
var authOptions = new SecuritySettings();
|
var authOptions = new SecurityClientSettings();
|
||||||
configSection.Bind(authOptions);
|
configSection.Bind(authOptions);
|
||||||
|
|
||||||
services.Configure<SecuritySettings>(configSection);
|
services.Configure<SecurityClientSettings>(configSection);
|
||||||
|
|
||||||
services.AddAuthentication(options =>
|
services.AddAuthentication(options =>
|
||||||
{
|
{
|
||||||
@@ -74,7 +75,7 @@ public static class Api
|
|||||||
{
|
{
|
||||||
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
|
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
|
||||||
options.Cookie.SameSite = SameSiteMode.Lax;
|
options.Cookie.SameSite = SameSiteMode.Lax;
|
||||||
options.Cookie.Name = "LiteCharmsApp.Session";
|
options.Cookie.Name = "PfmApp.Session";
|
||||||
})
|
})
|
||||||
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
|
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
|
||||||
{
|
{
|
||||||
@@ -121,4 +122,39 @@ public static class Api
|
|||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddApiSecurity(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
var configSection = configuration.GetSection(nameof(SecuritySettings));
|
||||||
|
|
||||||
|
var authOptions = new SecuritySettings();
|
||||||
|
configSection.Bind(authOptions);
|
||||||
|
|
||||||
|
services.Configure<SecuritySettings>(configSection);
|
||||||
|
|
||||||
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
|
.AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.MapInboundClaims = false;
|
||||||
|
options.Authority = authOptions.Authority;
|
||||||
|
options.Audience = authOptions.Audience;
|
||||||
|
|
||||||
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidIssuer = authOptions.Authority,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddAuthorizationBuilder()
|
||||||
|
.AddPolicy("ApiScope", policy =>
|
||||||
|
{
|
||||||
|
policy.RequireAuthenticatedUser();
|
||||||
|
policy.RequireClaim("scope", authOptions.Audience!);
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user