--- spec: add-issue-status-filter status: ready owner: TODO-github-handle related-routes: - GET /api/v2/projects/:projectId/issues files: - src/api/v2/projects/issues/issues.routes.ts - src/api/v2/projects/issues/issues.controller.ts - src/api/v2/projects/issues/issues.validator.ts - src/services/issues.service.ts - src/types/issueComments.ts --- # Filter issues list by status ## Goal Construction managers want to filter the issues list by status so they can focus on what's open without scrolling through closed items. The current list endpoint returns every issue for a project regardless of status. ## Context - Endpoint: `GET /api/v2/projects/:projectId/issues` - Existing controller: `src/api/v2/projects/issues/issues.controller.ts` - Existing service: `src/services/issues.service.ts` - Status values are already stored in the DB; see `src/types/issueComments.ts` for the canonical enum. - Pagination already exists via `parsePagingQueryParam` and `buildPaginatedQueryResponse`. ## Behavior changes - Add an optional query parameter `status` to `GET /api/v2/projects/:projectId/issues`. - Allowed values: `open`, `closed`, `in-review`. (Mirror the existing enum exactly — do not invent new values.) - When `status` is provided, only issues with a matching status are returned. - When `status` is omitted, the response is identical to today's behavior (regression-safe). - Invalid value → `400 Bad Request` with `{ "code": "INVALID_STATUS", "message": "status must be one of: open, closed, in-review" }`. - Pagination, auth, and other filters must continue to work unchanged. ## Acceptance criteria - [ ] `GET /api/v2/projects/:projectId/issues?status=open` returns only issues whose status is `open`. - [ ] `GET /api/v2/projects/:projectId/issues?status=closed` returns only `closed` issues. - [ ] `GET /api/v2/projects/:projectId/issues?status=in-review` returns only `in-review` issues. - [ ] `GET /api/v2/projects/:projectId/issues?status=banana` returns 400 with code `INVALID_STATUS`. - [ ] `GET /api/v2/projects/:projectId/issues` (no `status`) returns the same set of issues as before this change. - [ ] `GET /api/v2/projects/:projectId/issues?status=open&page=2&size=50` paginates correctly within the filtered set. - [ ] An unauthenticated request still returns 401 (auth behavior unchanged). - [ ] Swagger JSDoc on the route documents the new `status` parameter and possible 400 response. ## Out of scope - Filtering by multiple statuses in one call (e.g. `status=open,in-review`). - Sorting by status. - Bulk status updates. - Migrating any historical data. ## Test plan - **Unit** (`test/unit/services/issues.service.spec.ts`): three cases covering each valid status filter, plus one with no filter to assert query parity with the old behavior. - **Unit** (`test/unit/api/issues.controller.spec.ts`): one case for invalid status → `BadRequestError`. - **E2E** (`test/e2e/api/issues.spec.ts`): the six "GET …" acceptance scenarios above. Mirror the auth/setup helpers used by neighboring specs. - Run with: - `npm test -- --grep "Issues"` - `npm run test:e2e:file -- test/e2e/api/issues.spec.ts` ## Notes / open questions - None — values match the existing enum, so no schema migration is required. ## Decisions log (Filled in by Claude during implementation.)