From c6d1d862ff91c0678abd176b6577738f7e38ccca Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Sun, 30 Aug 2026 15:19:56 +0200 Subject: [PATCH 1/3] Consolidate SyncUserEvent endpoints --- .../SyncIdentityUserEndpoint.cs | 30 ------------------- .../Endpoints/Users/SyncUserEndpoint.cs | 13 ++++---- .../Communications/Events/SyncUserEvent.cs | 2 ++ 3 files changed, 10 insertions(+), 35 deletions(-) delete mode 100644 PostFundManagement.Api/Endpoints/Communications/SyncIdentityUserEndpoint.cs diff --git a/PostFundManagement.Api/Endpoints/Communications/SyncIdentityUserEndpoint.cs b/PostFundManagement.Api/Endpoints/Communications/SyncIdentityUserEndpoint.cs deleted file mode 100644 index 3ce7bdb..0000000 --- a/PostFundManagement.Api/Endpoints/Communications/SyncIdentityUserEndpoint.cs +++ /dev/null @@ -1,30 +0,0 @@ -using PostFundManagement.Application.Extensions; -using PostFundManagement.Application.Services.Communications.Events; -using PostFundManagement.Domain.Abstractions; -using PostFundManagement.Domain.Extensions; -using PostFundManagement.Domain.Security; - -namespace PostFundManagement.Api.Endpoints.Communications; - -[ApiVersionTarget(1)] -public class SyncIdentityUserEndpoint : IEndpoint -{ - public void Map(IEndpointRouteBuilder builder) - { - builder.MapPost("api/integration/sync", async (IJobOrchestrator jobOrchestrator, - CancellationToken cancellationToken = default) => - { - var syncEvent = new SyncUserEvent(); - - await jobOrchestrator.SendAsync(syncEvent, cancellationToken); - - return Results.Ok(); - }) - .WithDescription("Trigger integration and data synchronization in the background") - .WithName(typeof(SyncIdentityUserEndpoint).ToEndpointName()) - .MapToApiVersion(new ApiVersion(1)) - .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status401Unauthorized) - .WithTags(EndpointTags.Communications); - } -} diff --git a/PostFundManagement.Api/Endpoints/Users/SyncUserEndpoint.cs b/PostFundManagement.Api/Endpoints/Users/SyncUserEndpoint.cs index 3ac6553..6d30e80 100644 --- a/PostFundManagement.Api/Endpoints/Users/SyncUserEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Users/SyncUserEndpoint.cs @@ -1,4 +1,5 @@ using PostFundManagement.Application.Extensions; +using PostFundManagement.Application.Services.Communications.Events; using PostFundManagement.Application.Services.Users; using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Extensions; @@ -11,21 +12,23 @@ public class SyncUserEndpoint : IEndpoint { public void Map(IEndpointRouteBuilder builder) { - builder.MapPost("api/users/sync", async (HttpContext context, - UserService service, CancellationToken cancellationToken = default) => + builder.MapPost("api/integration/sync", async (IJobOrchestrator jobOrchestrator, + HttpContext context, UserService 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.IsSuccess ? Results.Ok() : Results.BadRequest(result.Errors.ToProblems()); }) - .WithDescription("Synchronise caller user profile using id_token's SID, inserting/updating user details in database") + .WithDescription("Trigger integration and data synchronization in the background") .WithName(typeof(SyncUserEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status401Unauthorized) - .WithTags(EndpointTags.Users); + .WithTags(EndpointTags.Communications); } } diff --git a/PostFundManagement.Application/Services/Communications/Events/SyncUserEvent.cs b/PostFundManagement.Application/Services/Communications/Events/SyncUserEvent.cs index 86e93a4..3aa9e86 100644 --- a/PostFundManagement.Application/Services/Communications/Events/SyncUserEvent.cs +++ b/PostFundManagement.Application/Services/Communications/Events/SyncUserEvent.cs @@ -6,6 +6,8 @@ namespace PostFundManagement.Application.Services.Communications.Events; public class SyncUserEvent : EventBase, IEvent { public string Name { get; set; } = nameof(SyncUserEvent); + + public static SyncUserEvent Create() => new(); } public class SyncUserEventHandler(ILogger logger) : INotificationHandler -- 2.47.3 From 6c7db78e17efa010659a3d4704fcfee946400a4e Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Sun, 30 Aug 2026 15:38:34 +0200 Subject: [PATCH 2/3] Implemented UpdateUserStatusEndpoint --- .../Endpoints/Users/SyncUserEndpoint.cs | 2 +- .../Users/UpdateUserStatusEndpoint.cs | 32 +++++++++++++++++ .../Services/Users/UserService.cs | 36 ++++++++++++++----- 3 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 PostFundManagement.Api/Endpoints/Users/UpdateUserStatusEndpoint.cs diff --git a/PostFundManagement.Api/Endpoints/Users/SyncUserEndpoint.cs b/PostFundManagement.Api/Endpoints/Users/SyncUserEndpoint.cs index 6d30e80..cc0efa1 100644 --- a/PostFundManagement.Api/Endpoints/Users/SyncUserEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Users/SyncUserEndpoint.cs @@ -29,6 +29,6 @@ public class SyncUserEndpoint : IEndpoint .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status401Unauthorized) - .WithTags(EndpointTags.Communications); + .WithTags(EndpointTags.Users); } } diff --git a/PostFundManagement.Api/Endpoints/Users/UpdateUserStatusEndpoint.cs b/PostFundManagement.Api/Endpoints/Users/UpdateUserStatusEndpoint.cs new file mode 100644 index 0000000..4eee9d1 --- /dev/null +++ b/PostFundManagement.Api/Endpoints/Users/UpdateUserStatusEndpoint.cs @@ -0,0 +1,32 @@ +using PostFundManagement.Application.Extensions; +using PostFundManagement.Application.Services.Users; +using PostFundManagement.Domain; +using PostFundManagement.Domain.Abstractions; +using PostFundManagement.Domain.Extensions; +using PostFundManagement.Domain.Security; + +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, + UserService service, CancellationToken cancellationToken = default) => + { + var result = await service.UpdateUserStatusAsync(userId, status, cancellationToken); + + return result.IsSuccess + ? Results.Ok() + : Results.NotFound(); + }) + .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) + .WithTags(EndpointTags.Users); + } +} diff --git a/PostFundManagement.Application/Services/Users/UserService.cs b/PostFundManagement.Application/Services/Users/UserService.cs index 8f3a65e..2eac0cb 100644 --- a/PostFundManagement.Application/Services/Users/UserService.cs +++ b/PostFundManagement.Application/Services/Users/UserService.cs @@ -8,7 +8,7 @@ namespace PostFundManagement.Application.Services.Users; public sealed class UserService(IDbContextFactory contextFactory) : IService { - public async ValueTask SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default) + public async ValueTask SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default) { try { @@ -28,7 +28,7 @@ public sealed class UserService(IDbContextFactory contextF { user!.Email = email; user.LastLoginAt = DateTime.UtcNow; - + context.Users.Update(user); } @@ -44,8 +44,8 @@ public sealed class UserService(IDbContextFactory contextF public async ValueTask> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default) { - try - { + try + { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var query = context.Users.AsNoTracking(); @@ -74,9 +74,29 @@ public sealed class UserService(IDbContextFactory contextF return Result.Ok(users?.Select(u => u.Map()).ToArray() ?? []); } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var changedTotal = await context.Users + .Where(f => f.Id == userId) + .ExecuteUpdateAsync(f => f.SetProperty(u => u.Status, status), cancellationToken); + + return changedTotal > 0 + ? Result.Ok() + : Result.Fail("Failed to update user activity status."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } } } -- 2.47.3 From 841763441da5cbe0096d836e780d4b32cb6acedc Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Sun, 30 Aug 2026 15:46:46 +0200 Subject: [PATCH 3/3] Implemented GetUserEndpoint --- .../Endpoints/Users/GetUserEndpoint.cs | 32 +++++++++++++++++++ .../Services/Users/UserService.cs | 18 +++++++++++ 2 files changed, 50 insertions(+) create mode 100644 PostFundManagement.Api/Endpoints/Users/GetUserEndpoint.cs diff --git a/PostFundManagement.Api/Endpoints/Users/GetUserEndpoint.cs b/PostFundManagement.Api/Endpoints/Users/GetUserEndpoint.cs new file mode 100644 index 0000000..c2ec515 --- /dev/null +++ b/PostFundManagement.Api/Endpoints/Users/GetUserEndpoint.cs @@ -0,0 +1,32 @@ +using PostFundManagement.Application.Extensions; +using PostFundManagement.Application.Services.Users; +using PostFundManagement.Domain.Abstractions; +using PostFundManagement.Domain.Extensions; +using PostFundManagement.Domain.Models; +using PostFundManagement.Domain.Security; + +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, UserService service, + HttpContext context, CancellationToken cancellationToken = default) => + { + var result = await service.GetUserAsync(userId, cancellationToken); + + return result.IsSuccess + ? Results.Ok(result.Value) + : Results.NotFound(); + }) + .WithDescription("Gets the metadata of a single user.") + .WithName(typeof(GetUserEndpoint).ToEndpointName()) + .MapToApiVersion(new ApiVersion(1)) + .Produces(StatusCodes.Status200OK) + .Produces(StatusCodes.Status404NotFound) + .Produces(StatusCodes.Status401Unauthorized) + .WithTags(EndpointTags.Users); + } +} diff --git a/PostFundManagement.Application/Services/Users/UserService.cs b/PostFundManagement.Application/Services/Users/UserService.cs index 2eac0cb..1292eb9 100644 --- a/PostFundManagement.Application/Services/Users/UserService.cs +++ b/PostFundManagement.Application/Services/Users/UserService.cs @@ -99,4 +99,22 @@ public sealed class UserService(IDbContextFactory contextF return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } + + public async Task> GetUserAsync(Guid userId, CancellationToken cancellationToken) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var user = await context.Users.AsNoTracking().FirstOrDefaultAsync(f => f.Id == userId, cancellationToken); + + return user is not null + ? Result.Ok(user.Map()) + : Result.Fail("Failed to find specific user."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } } -- 2.47.3