Added stubs for user management endpoints

This commit is contained in:
2026-10-04 15:17:22 +02:00
parent 06539236a1
commit 84a171d4a4
13 changed files with 160 additions and 12 deletions
@@ -0,0 +1,5 @@
namespace PostFundManagement.Api.Endpoints.Users;
public class AddUserClaimEndpoint
{
}
@@ -0,0 +1,5 @@
namespace PostFundManagement.Api.Endpoints.Users;
public class AddUserRoleEndpoint
{
}
@@ -0,0 +1,5 @@
namespace PostFundManagement.Api.Endpoints.Users;
public class CreateUserEndpoint
{
}
@@ -0,0 +1,6 @@
namespace PostFundManagement.Api.Endpoints.Users;
public class DeleteUserClaimEndpoint
{
}
@@ -0,0 +1,5 @@
namespace PostFundManagement.Api.Endpoints.Users;
public class DeleteUserRoleEndpoint
{
}
@@ -0,0 +1,5 @@
namespace PostFundManagement.Api.Endpoints.Users;
public class GetSystemRolesEndpoint
{
}
@@ -0,0 +1,6 @@
namespace PostFundManagement.Api.Endpoints.Users;
public class GetUserClaimsEndpoint
{
}
@@ -0,0 +1,5 @@
namespace PostFundManagement.Api.Endpoints.Users;
public class GetUserRolesEndpoint
{
}
@@ -0,0 +1,5 @@
namespace PostFundManagement.Api.Endpoints.Users;
public class LogUserOutEndpoint
{
}
@@ -0,0 +1,5 @@
namespace PostFundManagement.Api.Endpoints.Users;
public class SetUserLockoutEndpoint
{
}
@@ -10,7 +10,7 @@ public interface ISkorubaUserService
Task<Result<string>> CreateUserAsync(CreateUserRequest request, CancellationToken cancellationToken = default);
[Log]
Task<Result<UserResponse>> GetUserAsync(string userId, CancellationToken cancellationToken = default);
Task<Result<UserResponse>> GetUserAsync(Guid userId, CancellationToken cancellationToken = default);
[Log]
[Audit]
@@ -18,13 +18,30 @@ public interface ISkorubaUserService
[Log]
[Audit]
Task<Result> SetUserLockoutAsync(string userId, bool disabled, CancellationToken cancellationToken = default);
Task<Result> SetUserLockoutAsync(Guid userId, bool disabled, CancellationToken cancellationToken = default);
[Log]
[Audit]
Task<Result> AddClaimAsync(string userId, string claimType, string claimValue, CancellationToken cancellationToken = default);
Task<Result> AddClaimAsync(Guid userId, string claimType, string claimValue, CancellationToken cancellationToken = default);
[Log]
[Audit]
Task<Result> RemoveClaimAsync(string userId, int claimId, CancellationToken cancellationToken = default);
Task<Result> RemoveClaimAsync(Guid userId, int claimId, CancellationToken cancellationToken = default);
[Log]
[Audit]
Task<Result> AddRoleAsync(Guid userId, string roleId, CancellationToken cancellationToken = default);
[Log]
[Audit]
Task<Result> RemoveRoleAsync(Guid userId, string roleId, CancellationToken cancellationToken = default);
[Log]
Task<Result<Role[]>> GetAvailableRolesAsync(string? searchText = null, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default);
[Log]
Task<Result<Role[]>> GetUserRolesAsync(string userId, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default);
[Log]
Task<Result<Models.Security.Claim[]>> GetUserClaimsAsync(string userId, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default);
}
@@ -4,6 +4,12 @@ public sealed record CreateUserRequest(string UserName, string Email, string Pas
public sealed record UpdateUserRequest(string Id, string UserName, string Email, bool EmailConfirmed, string? PhoneNumber, bool PhoneNumberConfirmed, bool LockoutEnabled, DateTimeOffset? LockoutEnd, int AccessFailedCount, bool TwoFactorEnabled);
public sealed record UserClaim(string ClaimType, string ClaimValue, int? ClaimId = null);
public sealed record Claim(string ClaimType, string ClaimValue, int? ClaimId = null);
public sealed record UserResponse(string Id, string UserName, string Email, bool IsLockedOut, bool LockoutEnabled);
public record ClaimsResponse(int TotalCount, int PageSize, ICollection<Claim> Claims);
public sealed record UserResponse(string Id, string UserName, string Email, bool IsLockedOut, bool LockoutEnabled);
public record Role(string Id, string Name);
public record RolesResponse(int TotalCount, int PageSize, ICollection<Role> Roles);
@@ -13,7 +13,8 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie
var httpClient = httpClientFactory.CreateClient();
var response = await httpClient.PostAsJsonAsync("api/users", request, cancellationToken);
response.EnsureSuccessStatusCode();
if (!response.IsSuccessStatusCode)
return Result.Fail(CreateError.Create("Failed to create user."));
var created = await response.Content.ReadFromJsonAsync<UserResponse>(cancellationToken: cancellationToken);
@@ -21,7 +22,7 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie
.ToResult(created?.Id!);
}));
public async Task<Result<UserResponse>> GetUserAsync(string userId, CancellationToken cancellationToken = default) =>
public async Task<Result<UserResponse>> GetUserAsync(Guid userId, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<UserResponse>>>)(async () =>
{
var httpClient = httpClientFactory.CreateClient();
@@ -40,13 +41,13 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie
return Result.OkIf(response.IsSuccessStatusCode, UpdateError.Create("Failed to update user."));
}));
public async Task<Result> SetUserLockoutAsync(string userId, bool disabled, CancellationToken cancellationToken = default) =>
public async Task<Result> SetUserLockoutAsync(Guid userId, bool disabled, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result>>)(async () =>
{
var httpClient = httpClientFactory.CreateClient();
var user = await httpClient.GetFromJsonAsync<UpdateUserRequest>($"api/users/{userId}", cancellationToken);
if(user is null)
if (user is null)
return Result.Fail(NotFoundError.Create($"User {userId} not found."));
var updated = user with
@@ -60,7 +61,7 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie
return Result.OkIf(response.IsSuccessStatusCode, UpdateError.Create("Failed to update user lockout status."));
}));
public async Task<Result> AddClaimAsync(string userId, string claimType, string claimValue, CancellationToken cancellationToken = default) =>
public async Task<Result> AddClaimAsync(Guid userId, string claimType, string claimValue, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result>>)(async () =>
{
var httpClient = httpClientFactory.CreateClient();
@@ -71,7 +72,7 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie
return Result.OkIf(response.IsSuccessStatusCode, CreateError.Create("Failed to add user claim."));
}));
public async Task<Result> RemoveClaimAsync(string userId, int claimId, CancellationToken cancellationToken = default) =>
public async Task<Result> RemoveClaimAsync(Guid userId, int claimId, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result>>)(async () =>
{
var httpClient = httpClientFactory.CreateClient();
@@ -79,4 +80,76 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie
return Result.OkIf(response.IsSuccessStatusCode, CreateError.Create("Failed to remove user claim."));
}));
public async Task<Result<Claim[]>> GetUserClaimsAsync(string userId, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Claim[]>>>)(async () =>
{
var httpClient = httpClientFactory.CreateClient();
var result = await httpClient.GetFromJsonAsync<ClaimsResponse>($"api/users/{userId}/claims?page={page}&pageSize={pageSize}", cancellationToken);
return Result.FailIf(result is null, NotFoundError.Create($"Claims for user {userId} could not be retrieved."))
.ToResult(result?.Claims.ToArray() ?? []);
}));
public async Task<Result> AddRoleAsync(Guid userId, string roleId, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result>>)(async () =>
{
var httpClient = httpClientFactory.CreateClient();
var payload = new { UserId = userId, RoleId = roleId };
var response = await httpClient.PostAsJsonAsync($"api/users/{userId}/roles", payload, cancellationToken);
return Result.OkIf(response.IsSuccessStatusCode, CreateError.Create("Failed to add user role."));
}));
public async Task<Result> RemoveRoleAsync(Guid userId, string roleId, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result>>)(async () =>
{
var httpClient = httpClientFactory.CreateClient();
var response = await httpClient.DeleteAsync($"api/users/{userId}/roles/{roleId}", cancellationToken);
return Result.OkIf(response.IsSuccessStatusCode, CreateError.Create("Failed to remove user role."));
}));
public async Task<Result<Role[]>> GetUserRolesAsync(string userId, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Role[]>>>)(async () =>
{
var httpClient = httpClientFactory.CreateClient();
var result = await httpClient.GetFromJsonAsync<RolesResponse>($"api/users/{userId}/roles?page={page}&pageSize={pageSize}", cancellationToken);
return Result.FailIf(result is null, NotFoundError.Create($"Roles for user {userId} could not be retrieved."))
.ToResult(result?.Roles?.ToArray() ?? []);
}));
public async Task<Result> InvalidateUserSessionAsync(Guid userId, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result>>)(async () =>
{
var httpClient = httpClientFactory.CreateClient();
var user = await httpClient.GetFromJsonAsync<UserResponse>($"api/users/{userId}", cancellationToken);
if (user is null)
return Result.Fail(NotFoundError.Create($"User {userId} not found."));
var response = await httpClient.PutAsJsonAsync("api/users", user, cancellationToken);
return Result.OkIf(response.IsSuccessStatusCode, UpdateError.Create("Failed to invalidate user session."));
}));
public async Task<Result<Role[]>> GetAvailableRolesAsync(string? searchText = null, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Role[]>>>)(async () =>
{
var httpClient = httpClientFactory.CreateClient();
var query = $"api/roles?page={page}&pageSize={pageSize}";
if (!string.IsNullOrWhiteSpace(searchText))
query += $"&searchText={Uri.EscapeDataString(searchText)}";
var result = await httpClient.GetFromJsonAsync<RolesResponse>(query, cancellationToken);
return Result.FailIf(result is null, NotFoundError.Create("Available roles could not be retrieved."))
.ToResult(result?.Roles?.ToArray() ?? []);
}));
}