fix: wire filter/search params to Issues API (#116)#136
Conversation
- Add StatusName and CategoryName properties to ListIssuesQuery - Add statusName and categoryName parameters to IIssueRepository.GetAllAsync - Add MongoDB filtering for Status.StatusName and Category.CategoryName in IssueRepository - Pass StatusName and CategoryName from ListIssuesHandler to repository - Add statusName and categoryName query params to IssueEndpoints MapGet - Add statusName and categoryName to IssueApiClient interface and implementation - Wire _statusFilter and _categoryFilter from IssuesPage to API client - Update all test signatures to match new repository method signature Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Wires IssuesPage filter inputs (search/status/category) through the Web API client, API endpoint/handler, and MongoDB repository so list results can be filtered server-side (Closes #116).
Changes:
- Added
StatusName/CategoryNamefilters toListIssuesQueryand threaded them through handler → repository. - Extended
/api/v1/issuesendpoint and WebIIssueApiClientto acceptstatusName/categoryNamequery parameters. - Implemented MongoDB filtering on
Status.StatusNameandCategory.CategoryNameand updated unit test mocks for the new repository signature.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Api.Tests.Unit/Handlers/Issues/ListIssuesHandlerTests.cs | Updates repository mock calls to new GetAllAsync signature. |
| tests/Api.Tests.Unit/Endpoints/IssueEndpointsTests.cs | Updates endpoint test repository mock signature for list route. |
| src/Web/Components/Features/Issues/IssuesPage.razor | Passes _searchTerm, _statusFilter, _categoryFilter into API client call. |
| src/Web/Components/Features/Issues/IssueApiClient.cs | Adds statusName/categoryName to client contract and query-string construction. |
| src/Shared/Contracts/ListIssuesQuery.cs | Adds StatusName and CategoryName properties to the shared query contract. |
| src/Api/Handlers/Issues/ListIssuesHandler.cs | Forwards new query fields into repository call. |
| src/Api/Handlers/Issues/IssueEndpoints.cs | Adds new query params to the list endpoint and maps them into ListIssuesQuery. |
| src/Api/Data/IssueRepository.cs | Adds MongoDB filters for status/category name. |
| src/Api/Data/Interfaces/IIssueRepository.cs | Extends repository interface signature to accept status/category filters. |
Comments suppressed due to low confidence (1)
tests/Api.Tests.Unit/Endpoints/IssueEndpointsTests.cs:47
- This endpoint test only verifies a 200 response and doesn’t cover the new query parameters. Add a test that calls
/api/v1/issues?statusName=...&categoryName=...and asserts the repository substitute receivedGetAllAsyncwith the expectedstatusName/categoryNamevalues, so the new binding/forwarding is covered.
public async Task ListIssues_ReturnsOk()
{
// Arrange
IReadOnlyList<IssueDto> items = [];
_factory.IssueRepository
.GetAllAsync(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<CancellationToken>())
.Returns(Result<(IReadOnlyList<IssueDto> Items, long Total)>.Ok((items, 0L)));
// Act
var response = await _client.GetAsync("/api/v1/issues").ConfigureAwait(false);
| /// <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; } |
There was a problem hiding this comment.
ListIssuesQueryValidator currently enforces max lengths for SearchTerm and AuthorName, but the newly added StatusName/CategoryName aren’t validated. To keep request validation consistent and avoid overly long inputs reaching the repository, add similar maximum-length rules for these new properties.
| try | ||
| { | ||
| var response = await IssueClient.GetAllAsync(page, 20); | ||
| var response = await IssueClient.GetAllAsync(page, 20, _searchTerm, null, _statusFilter, _categoryFilter); |
There was a problem hiding this comment.
This call is now relying on positional arguments with an in-the-middle null placeholder for authorName, which is brittle as the method signature grows. Use named arguments for the optional parameters (e.g., searchTerm: _searchTerm, statusName: _statusFilter, categoryName: _categoryFilter) to prevent accidental mis-ordering.
| var response = await IssueClient.GetAllAsync(page, 20, _searchTerm, null, _statusFilter, _categoryFilter); | |
| var response = await IssueClient.GetAllAsync( | |
| page, | |
| 20, | |
| searchTerm: _searchTerm, | |
| authorName: null, | |
| statusName: _statusFilter, | |
| categoryName: _categoryFilter); |
| var query = new ListIssuesQuery { Page = 1, PageSize = 20 }; | ||
|
|
||
| var issues = GenerateIssueDtos(20); | ||
| _repository.GetAllAsync(1, 20, null, null, Arg.Any<CancellationToken>()) | ||
| _repository.GetAllAsync(1, 20, null, null, null, null, Arg.Any<CancellationToken>()) | ||
| .Returns(((IReadOnlyList<IssueDto>)issues, 42L)); | ||
|
|
There was a problem hiding this comment.
The handler tests were updated for the new repository signature, but there’s no test exercising the new StatusName/CategoryName forwarding behavior. Add a test that sets query.StatusName/query.CategoryName and asserts the repository is called with those values (and that the returned response uses the result).
| group.MapGet("", async (int? page, int? pageSize, string? searchTerm, string? authorName, string? statusName, string? categoryName, ListIssuesHandler handler) => | ||
| { | ||
| var query = new ListIssuesQuery | ||
| { | ||
| Page = page ?? 1, | ||
| PageSize = pageSize ?? 20, | ||
| SearchTerm = searchTerm, | ||
| AuthorName = authorName | ||
| AuthorName = authorName, | ||
| StatusName = statusName, | ||
| CategoryName = categoryName | ||
| }; | ||
| var result = await handler.Handle(query); | ||
| return Results.Ok(result); |
There was a problem hiding this comment.
The ListIssues endpoint doesn’t pass a CancellationToken into ListIssuesHandler.Handle, so request aborts/timeouts won’t cancel repository calls. Add a CancellationToken parameter to the delegate (e.g., CancellationToken ct) and pass it through to handler.Handle(query, ct).
| if (!string.IsNullOrWhiteSpace(statusName)) | ||
| { | ||
| filters.Add(filterBuilder.Regex(x => x.Status.StatusName, new BsonRegularExpression(statusName, "i"))); | ||
| } | ||
|
|
||
| if (!string.IsNullOrWhiteSpace(categoryName)) | ||
| { | ||
| filters.Add(filterBuilder.Regex(x => x.Category.CategoryName, new BsonRegularExpression(categoryName, "i"))); | ||
| } |
There was a problem hiding this comment.
These new filters build MongoDB regexes directly from user input. This can lead to unintended regex semantics and expensive patterns; it’s also less efficient than an equality/normalized match for status/category values. Consider using an exact match (e.g., Eq on a normalized field) or at least escaping/anchoring the pattern before constructing BsonRegularExpression.
AdminPageTests and IssuesPageTests mocked IIssueApiClient.GetAllAsync with 5 params (missing statusName and categoryName). Update all call sites to the correct 7-parameter signature. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #136 +/- ##
=======================================
Coverage 55.39% 55.39%
=======================================
Files 128 128
Lines 2939 2939
Branches 332 332
=======================================
Hits 1628 1628
Misses 1068 1068
Partials 243 243 🚀 New features to boost your workflow:
|
🏗️ PR Added to Squad Triage QueueThis PR has been labeled with Next steps:
|
Closes #116
Summary
Wires the filter/search parameters from the IssuesPage UI through all backend layers to the MongoDB repository, enabling users to actually filter issues by status and category.
Changes Made
Testing
Working as
🤖 Working as Sam (Backend Developer)