-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add DeleteStatus archive handler and endpoint #135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
884e57f
892bcae
caf1920
599d44b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,6 +66,20 @@ public static IEndpointRouteBuilder MapCategoryEndpoints(this IEndpointRouteBuil | |
| .Produces(StatusCodes.Status404NotFound) | ||
| .RequireAuthorization(); | ||
|
|
||
| group.MapDelete("{id}", async (string id, DeleteCategoryHandler handler) => | ||
| { | ||
| if (!ObjectId.TryParse(id, out var objectId)) | ||
| return Results.BadRequest("Invalid ID format"); | ||
| var command = new DeleteCategoryCommand { Id = objectId }; | ||
| var result = await handler.Handle(command); | ||
| return result.Success ? Results.NoContent() : Results.NotFound(); | ||
| }) | ||
|
Comment on lines
+69
to
+76
|
||
| .WithName("DeleteCategory") | ||
| .WithSummary("Delete (archive) a category") | ||
| .Produces(StatusCodes.Status204NoContent) | ||
| .Produces(StatusCodes.Status404NotFound) | ||
| .RequireAuthorization(); | ||
|
Comment on lines
+69
to
+81
|
||
|
|
||
| return app; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -70,6 +70,20 @@ public static IEndpointRouteBuilder MapStatusEndpoints(this IEndpointRouteBuilde | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .Produces(StatusCodes.Status404NotFound) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .RequireAuthorization(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| group.MapDelete("{id}", async (string id, DeleteStatusHandler handler) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!ObjectId.TryParse(id, out var objectId)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Results.BadRequest("Invalid ID format"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var command = new DeleteStatusCommand { Id = objectId }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var result = await handler.Handle(command); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return result.Success ? Results.NoContent() : Results.NotFound(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+73
to
+79
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .WithName("DeleteStatus") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .WithSummary("Delete (archive) a status") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .Produces(StatusCodes.Status204NoContent) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .Produces(StatusCodes.Status404NotFound) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+77
to
+84
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var command = new DeleteStatusCommand { Id = objectId }; | |
| var result = await handler.Handle(command); | |
| return result.Success ? Results.NoContent() : Results.NotFound(); | |
| }) | |
| .WithName("DeleteStatus") | |
| .WithSummary("Delete (archive) a status") | |
| .Produces(StatusCodes.Status204NoContent) | |
| .Produces(StatusCodes.Status404NotFound) | |
| var command = new DeleteStatusCommand { Id = objectId }; | |
| var result = await handler.Handle(command); | |
| if (result.Success) | |
| return Results.NoContent(); | |
| return result.ErrorCode switch | |
| { | |
| ResultErrorCode.NotFound => Results.NotFound(), | |
| ResultErrorCode.Validation => Results.BadRequest(result.Error), | |
| _ => Results.Problem( | |
| detail: result.Error, | |
| statusCode: StatusCodes.Status500InternalServerError) | |
| }; | |
| }) | |
| .WithName("DeleteStatus") | |
| .WithSummary("Delete (archive) a status") | |
| .Produces(StatusCodes.Status204NoContent) | |
| .Produces(StatusCodes.Status400BadRequest) | |
| .Produces(StatusCodes.Status404NotFound) | |
| .Produces(StatusCodes.Status500InternalServerError) |
Copilot
AI
Apr 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This endpoint is described as “Admin only” in the PR metadata, but it currently uses RequireAuthorization() (any authenticated user). If admin restriction is required, enforce the appropriate policy/role requirement here (and ensure it’s registered) so DELETE is actually admin-scoped.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,4 +32,14 @@ public record ListIssuesQuery | |
| /// Gets or sets the author name for filtering by author. | ||
| /// </summary> | ||
| public string? AuthorName { get; init; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the status name for filtering by status. | ||
| /// </summary> | ||
| public string? StatusName { get; init; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the category name for filtering by category. | ||
| /// </summary> | ||
| public string? CategoryName { get; init; } | ||
|
Comment on lines
+35
to
+44
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,8 +17,10 @@ public interface IIssueApiClient | |
| /// <param name="pageSize">The number of items per page.</param> | ||
| /// <param name="searchTerm">Optional search term to filter by title or description.</param> | ||
| /// <param name="authorName">Optional author name to filter by.</param> | ||
| /// <param name="statusName">Optional status name to filter by.</param> | ||
| /// <param name="categoryName">Optional category name to filter by.</param> | ||
| /// <param name="cancellationToken">Cancellation token.</param> | ||
| Task<PaginatedResponse<IssueDto>> GetAllAsync(int page = 1, int pageSize = 20, string? searchTerm = null, string? authorName = null, CancellationToken cancellationToken = default); | ||
| Task<PaginatedResponse<IssueDto>> GetAllAsync(int page = 1, int pageSize = 20, string? searchTerm = null, string? authorName = null, string? statusName = null, string? categoryName = null, CancellationToken cancellationToken = default); | ||
|
Comment on lines
17
to
+23
|
||
|
|
||
| /// <summary>Gets an issue by its identifier.</summary> | ||
| Task<IssueDto?> GetByIdAsync(string id, CancellationToken cancellationToken = default); | ||
|
|
@@ -45,7 +47,7 @@ public class IssueApiClient : IIssueApiClient | |
| public IssueApiClient(HttpClient httpClient) => _httpClient = httpClient; | ||
|
|
||
| /// <inheritdoc/> | ||
| public async Task<PaginatedResponse<IssueDto>> GetAllAsync(int page = 1, int pageSize = 20, string? searchTerm = null, string? authorName = null, CancellationToken cancellationToken = default) | ||
| public async Task<PaginatedResponse<IssueDto>> GetAllAsync(int page = 1, int pageSize = 20, string? searchTerm = null, string? authorName = null, string? statusName = null, string? categoryName = null, CancellationToken cancellationToken = default) | ||
| { | ||
| try | ||
| { | ||
|
|
@@ -58,6 +60,14 @@ public async Task<PaginatedResponse<IssueDto>> GetAllAsync(int page = 1, int pag | |
| { | ||
| url += $"&authorName={Uri.EscapeDataString(authorName)}"; | ||
| } | ||
| if (!string.IsNullOrWhiteSpace(statusName)) | ||
| { | ||
| url += $"&statusName={Uri.EscapeDataString(statusName)}"; | ||
| } | ||
| if (!string.IsNullOrWhiteSpace(categoryName)) | ||
| { | ||
| url += $"&categoryName={Uri.EscapeDataString(categoryName)}"; | ||
| } | ||
|
|
||
| var result = await _httpClient.GetFromJsonAsync<PaginatedResponse<IssueDto>>(url, cancellationToken).ConfigureAwait(false); | ||
| return result ?? PaginatedResponse<IssueDto>.Empty; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIssueRepository.GetAllAsync signature change adds new parameters; any existing repository stubs/mocks using the old 5-arg overload will no longer compile. Update those call sites (especially unit/integration tests) to pass statusName and categoryName (or use named arguments) to match the new contract.