Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/Web/Features/BlogPosts/Create/CreateBlogPostHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,23 @@ public async Task<Result<Guid>> Handle(CreateBlogPostCommand request, Cancellati
try
{
var post = BlogPost.Create(request.Title, request.Content, request.Author);
await repo.AddAsync(post, cancellationToken);
await cache.InvalidateAllAsync(cancellationToken);
await repo.AddAsync(post, cancellationToken).ConfigureAwait(false);
await cache.InvalidateAllAsync(cancellationToken).ConfigureAwait(false);
return Result.Ok<Guid>(post.Id);
}
catch (Exception ex)
catch (OperationCanceledException)
{
throw;
}
catch (InvalidOperationException ex)
{
return Result.Fail<Guid>(ex.Message);
}
#pragma warning disable CA1031 // Intentional: top-level handler converts unexpected failures to Result to keep UI stable
catch (Exception)
{
return Result.Fail<Guid>("An unexpected error occurred.");
}
#pragma warning restore CA1031
}
}
18 changes: 14 additions & 4 deletions src/Web/Features/BlogPosts/Delete/DeleteBlogPostHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public async Task<Result> Handle(DeleteBlogPostCommand request, CancellationToke
{
try
{
await repo.DeleteAsync(request.Id, cancellationToken);
await cache.InvalidateAllAsync(cancellationToken);
await cache.InvalidateByIdAsync(request.Id, cancellationToken);
await repo.DeleteAsync(request.Id, cancellationToken).ConfigureAwait(false);
await cache.InvalidateAllAsync(cancellationToken).ConfigureAwait(false);
await cache.InvalidateByIdAsync(request.Id, cancellationToken).ConfigureAwait(false);
return Result.Ok();
}
catch (DbUpdateConcurrencyException)
Expand All @@ -31,9 +31,19 @@ public async Task<Result> Handle(DeleteBlogPostCommand request, CancellationToke
"This post was modified by another user. Please reload and try again.",
ResultErrorCode.Concurrency);
}
catch (Exception ex)
catch (OperationCanceledException)
{
throw;
}
catch (InvalidOperationException ex)
{
return Result.Fail(ex.Message);
}
#pragma warning disable CA1031 // Intentional: top-level handler converts unexpected failures to Result to keep UI stable
catch (Exception)
{
return Result.Fail("An unexpected error occurred.");
}
#pragma warning restore CA1031
}
}
36 changes: 28 additions & 8 deletions src/Web/Features/BlogPosts/Edit/EditBlogPostHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ public async Task<Result> Handle(EditBlogPostCommand request, CancellationToken
{
try
{
var post = await repo.GetByIdAsync(request.Id, cancellationToken);
var post = await repo.GetByIdAsync(request.Id, cancellationToken).ConfigureAwait(false);
if (post is null)
return Result.Fail($"BlogPost {request.Id} not found.");
post.Update(request.Title, request.Content);
await repo.UpdateAsync(post, cancellationToken);
await cache.InvalidateAllAsync(cancellationToken);
await cache.InvalidateByIdAsync(request.Id, cancellationToken);
await repo.UpdateAsync(post, cancellationToken).ConfigureAwait(false);
await cache.InvalidateAllAsync(cancellationToken).ConfigureAwait(false);
await cache.InvalidateByIdAsync(request.Id, cancellationToken).ConfigureAwait(false);
return Result.Ok();
}
catch (DbUpdateConcurrencyException)
Expand All @@ -37,10 +37,20 @@ public async Task<Result> Handle(EditBlogPostCommand request, CancellationToken
"This post was modified by another user. Please reload and try again.",
ResultErrorCode.Concurrency);
}
catch (Exception ex)
catch (OperationCanceledException)
{
throw;
}
catch (InvalidOperationException ex)
{
return Result.Fail(ex.Message);
}
#pragma warning disable CA1031 // Intentional: top-level handler converts unexpected failures to Result to keep UI stable
catch (Exception)
{
return Result.Fail("An unexpected error occurred.");
}
#pragma warning restore CA1031
}

public async Task<Result<BlogPostDto?>> Handle(GetBlogPostByIdQuery request, CancellationToken cancellationToken)
Expand All @@ -51,14 +61,24 @@ public async Task<Result> Handle(EditBlogPostCommand request, CancellationToken
request.Id,
async () =>
{
var post = await repo.GetByIdAsync(request.Id, cancellationToken);
var post = await repo.GetByIdAsync(request.Id, cancellationToken).ConfigureAwait(false);
return post?.ToDto();
}, cancellationToken);
}, cancellationToken).ConfigureAwait(false);
return Result.Ok<BlogPostDto?>(dto);
}
catch (Exception ex)
catch (OperationCanceledException)
{
throw;
}
catch (InvalidOperationException ex)
{
return Result.Fail<BlogPostDto?>(ex.Message);
}
#pragma warning disable CA1031 // Intentional: top-level handler converts unexpected failures to Result to keep UI stable
catch (Exception)
{
return Result.Fail<BlogPostDto?>("An unexpected error occurred.");
}
#pragma warning restore CA1031
}
}
16 changes: 13 additions & 3 deletions src/Web/Features/BlogPosts/List/GetBlogPostsHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,24 @@ public async Task<Result<IReadOnlyList<BlogPostDto>>> Handle(
var result = await cache.GetOrFetchAllAsync(
async () =>
{
var all = await repo.GetAllAsync(cancellationToken);
var all = await repo.GetAllAsync(cancellationToken).ConfigureAwait(false);
return (IReadOnlyList<BlogPostDto>)all.Select(p => p.ToDto()).ToList();
}, cancellationToken);
}, cancellationToken).ConfigureAwait(false);
return Result.Ok<IReadOnlyList<BlogPostDto>>(result);
}
catch (Exception ex)
catch (OperationCanceledException)
{
throw;
}
catch (InvalidOperationException ex)
{
return Result.Fail<IReadOnlyList<BlogPostDto>>(ex.Message);
}
#pragma warning disable CA1031 // Intentional: top-level handler converts unexpected failures to Result to keep UI stable
catch (Exception)
{
return Result.Fail<IReadOnlyList<BlogPostDto>>("An unexpected error occurred.");
}
#pragma warning restore CA1031
}
}
86 changes: 71 additions & 15 deletions src/Web/Features/UserManagement/UserManagementHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ public async Task<Result<IReadOnlyList<UserWithRolesDto>>> Handle(
{
try
{
var client = await GetManagementClientAsync(cancellationToken);
var usersPager = await client.Users.ListAsync(new ListUsersRequestParameters(), cancellationToken: cancellationToken);
var client = await GetManagementClientAsync(cancellationToken).ConfigureAwait(false);
var usersPager = await client.Users.ListAsync(new ListUsersRequestParameters(), cancellationToken: cancellationToken).ConfigureAwait(false);
var result = new List<UserWithRolesDto>();
await foreach (var user in usersPager)
{
var rolesPager = await client.Users.Roles.ListAsync(
user.UserId ?? string.Empty, new ListUserRolesRequestParameters(), cancellationToken: cancellationToken);
user.UserId ?? string.Empty, new ListUserRolesRequestParameters(), cancellationToken: cancellationToken).ConfigureAwait(false);
var roles = new List<string>();
await foreach (var role in rolesPager)
{
Expand All @@ -47,63 +47,119 @@ public async Task<Result<IReadOnlyList<UserWithRolesDto>>> Handle(
}
return Result.Ok<IReadOnlyList<UserWithRolesDto>>(result);
}
catch (Exception ex)
catch (OperationCanceledException)
{
throw;
}
catch (InvalidOperationException ex)
{
return Result.Fail<IReadOnlyList<UserWithRolesDto>>(ex.Message);
}
catch (HttpRequestException ex)
{
return Result.Fail<IReadOnlyList<UserWithRolesDto>>(ex.Message);
}
#pragma warning disable CA1031 // Intentional: top-level handler converts unexpected failures to Result to keep UI stable
catch (Exception)
{
return Result.Fail<IReadOnlyList<UserWithRolesDto>>("An unexpected error occurred.");
}
#pragma warning restore CA1031
}

public async Task<Result> Handle(AssignRoleCommand request, CancellationToken cancellationToken)
{
try
{
var client = await GetManagementClientAsync(cancellationToken);
var client = await GetManagementClientAsync(cancellationToken).ConfigureAwait(false);
await client.Users.Roles.AssignAsync(
request.UserId,
new AssignUserRolesRequestContent { Roles = [request.RoleId] },
cancellationToken: cancellationToken);
cancellationToken: cancellationToken).ConfigureAwait(false);
return Result.Ok();
}
catch (Exception ex)
catch (OperationCanceledException)
{
throw;
}
catch (InvalidOperationException ex)
{
return Result.Fail(ex.Message);
}
catch (HttpRequestException ex)
{
return Result.Fail(ex.Message);
}
#pragma warning disable CA1031 // Intentional: top-level handler converts unexpected failures to Result to keep UI stable
catch (Exception)
{
return Result.Fail("An unexpected error occurred.");
}
#pragma warning restore CA1031
}

public async Task<Result> Handle(RemoveRoleCommand request, CancellationToken cancellationToken)
{
try
{
var client = await GetManagementClientAsync(cancellationToken);
var client = await GetManagementClientAsync(cancellationToken).ConfigureAwait(false);
await client.Users.Roles.DeleteAsync(
request.UserId,
new DeleteUserRolesRequestContent { Roles = [request.RoleId] },
cancellationToken: cancellationToken);
cancellationToken: cancellationToken).ConfigureAwait(false);
return Result.Ok();
}
catch (Exception ex)
catch (OperationCanceledException)
{
throw;
}
catch (InvalidOperationException ex)
{
return Result.Fail(ex.Message);
}
catch (HttpRequestException ex)
{
return Result.Fail(ex.Message);
}
#pragma warning disable CA1031 // Intentional: top-level handler converts unexpected failures to Result to keep UI stable
catch (Exception)
{
return Result.Fail("An unexpected error occurred.");
}
#pragma warning restore CA1031
}

public async Task<Result<IReadOnlyList<RoleDto>>> Handle(GetAvailableRolesQuery request, CancellationToken cancellationToken)
{
try
{
var client = await GetManagementClientAsync(cancellationToken);
var rolesPager = await client.Roles.ListAsync(new ListRolesRequestParameters(), cancellationToken: cancellationToken);
var client = await GetManagementClientAsync(cancellationToken).ConfigureAwait(false);
var rolesPager = await client.Roles.ListAsync(new ListRolesRequestParameters(), cancellationToken: cancellationToken).ConfigureAwait(false);
var roles = new List<RoleDto>();
await foreach (var role in rolesPager)
{
roles.Add(new RoleDto(role.Id ?? string.Empty, role.Name ?? string.Empty));
}
return Result.Ok<IReadOnlyList<RoleDto>>(roles);
}
catch (Exception ex)
catch (OperationCanceledException)
{
throw;
}
catch (InvalidOperationException ex)
{
return Result.Fail<IReadOnlyList<RoleDto>>(ex.Message);
}
catch (HttpRequestException ex)
{
return Result.Fail<IReadOnlyList<RoleDto>>(ex.Message);
}
#pragma warning disable CA1031 // Intentional: top-level handler converts unexpected failures to Result to keep UI stable
catch (Exception)
{
return Result.Fail<IReadOnlyList<RoleDto>>("An unexpected error occurred.");
}
#pragma warning restore CA1031
}

private async Task<ManagementApiClient> GetManagementClientAsync(CancellationToken cancellationToken)
Expand All @@ -124,9 +180,9 @@ private async Task<ManagementApiClient> GetManagementClientAsync(CancellationTok
client_secret = clientSecret,
audience = $"https://{domain}/api/v2/",
grant_type = "client_credentials"
}, cancellationToken);
}, cancellationToken).ConfigureAwait(false);
tokenResponse.EnsureSuccessStatusCode();
var tokenData = await tokenResponse.Content.ReadFromJsonAsync<TokenResponse>(cancellationToken);
var tokenData = await tokenResponse.Content.ReadFromJsonAsync<TokenResponse>(cancellationToken).ConfigureAwait(false);
return new ManagementApiClient(
token: tokenData!.AccessToken,
clientOptions: new ClientOptions { BaseUrl = $"https://{domain}/api/v2" });
Expand Down
Loading