diff --git a/src/Web/Features/BlogPosts/Create/CreateBlogPostHandler.cs b/src/Web/Features/BlogPosts/Create/CreateBlogPostHandler.cs index 6a4c5821..1b549d8d 100644 --- a/src/Web/Features/BlogPosts/Create/CreateBlogPostHandler.cs +++ b/src/Web/Features/BlogPosts/Create/CreateBlogPostHandler.cs @@ -21,13 +21,23 @@ public async Task> 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(post.Id); } -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 } } diff --git a/src/Web/Features/BlogPosts/Delete/DeleteBlogPostHandler.cs b/src/Web/Features/BlogPosts/Delete/DeleteBlogPostHandler.cs index 3be3346b..862f3fd6 100644 --- a/src/Web/Features/BlogPosts/Delete/DeleteBlogPostHandler.cs +++ b/src/Web/Features/BlogPosts/Delete/DeleteBlogPostHandler.cs @@ -20,9 +20,9 @@ public async Task 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) @@ -31,9 +31,19 @@ public async Task 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 } } diff --git a/src/Web/Features/BlogPosts/Edit/EditBlogPostHandler.cs b/src/Web/Features/BlogPosts/Edit/EditBlogPostHandler.cs index e3357a74..c3236180 100644 --- a/src/Web/Features/BlogPosts/Edit/EditBlogPostHandler.cs +++ b/src/Web/Features/BlogPosts/Edit/EditBlogPostHandler.cs @@ -22,13 +22,13 @@ public async Task 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) @@ -37,10 +37,20 @@ public async Task 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> Handle(GetBlogPostByIdQuery request, CancellationToken cancellationToken) @@ -51,14 +61,24 @@ public async Task 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(dto); } -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 } } diff --git a/src/Web/Features/BlogPosts/List/GetBlogPostsHandler.cs b/src/Web/Features/BlogPosts/List/GetBlogPostsHandler.cs index 83a918b2..03829ba8 100644 --- a/src/Web/Features/BlogPosts/List/GetBlogPostsHandler.cs +++ b/src/Web/Features/BlogPosts/List/GetBlogPostsHandler.cs @@ -24,14 +24,24 @@ public async Task>> Handle( var result = await cache.GetOrFetchAllAsync( async () => { -var all = await repo.GetAllAsync(cancellationToken); +var all = await repo.GetAllAsync(cancellationToken).ConfigureAwait(false); return (IReadOnlyList)all.Select(p => p.ToDto()).ToList(); -}, cancellationToken); +}, cancellationToken).ConfigureAwait(false); return Result.Ok>(result); } -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 } } diff --git a/src/Web/Features/UserManagement/UserManagementHandler.cs b/src/Web/Features/UserManagement/UserManagementHandler.cs index 8d841d15..d8688c5b 100644 --- a/src/Web/Features/UserManagement/UserManagementHandler.cs +++ b/src/Web/Features/UserManagement/UserManagementHandler.cs @@ -27,13 +27,13 @@ public async Task>> 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(); 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(); await foreach (var role in rolesPager) { @@ -47,52 +47,94 @@ public async Task>> Handle( } return Result.Ok>(result); } -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 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 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>> 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(); await foreach (var role in rolesPager) { @@ -100,10 +142,24 @@ public async Task>> Handle(GetAvailableRolesQuery } return Result.Ok>(roles); } -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 } private async Task GetManagementClientAsync(CancellationToken cancellationToken) @@ -124,9 +180,9 @@ private async Task 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(cancellationToken); +var tokenData = await tokenResponse.Content.ReadFromJsonAsync(cancellationToken).ConfigureAwait(false); return new ManagementApiClient( token: tokenData!.AccessToken, clientOptions: new ClientOptions { BaseUrl = $"https://{domain}/api/v2" });