Skip to content

feat(api): GET /projects list endpoint#229

Merged
thewrz merged 2 commits into
mainfrom
feat/api-projects-list
Jun 21, 2026
Merged

feat(api): GET /projects list endpoint#229
thewrz merged 2 commits into
mainfrom
feat/api-projects-list

Conversation

@thewrz

@thewrz thewrz commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Why

The examples/web_ui_demo project switcher needs to enumerate projects. listProjects() exists in the DB layer (backs the MCP list_projects tool) but had no REST endpoint — GET /projects 404'd. Second of the roadmap-first reads unblocking the demo (after #226 / #227).

What

  • GET /projectsApiResponse<ProjectListItem[]> ({id, name}, ordered by name), reusing listProjects(pool).
  • Adds the get: op to the existing /projects: openapi path + a ProjectListItem schema; contract gate response-covers it.

Testing

  • Integration: GET /projects lists {id, name} incl. the test project (added to projects.integration.test.ts)
  • Contract gate green (route↔spec + response-schema validation)
  • pnpm lint clean (eslint + tsc + prettier)
  • CI green

⚠️ Same pre-existing src/parser/docx/cpi.integration.test.ts failure on main (CPI source-inference) is unrelated to this PR.

🤖 Co-authored by Claude Opus 4.8. Closes #228.

Summary by CodeRabbit

New Features

  • Added an unauthenticated GET /projects endpoint that returns a list of projects (including each project’s identifier and name) and orders them by project name.

Tests

  • Expanded API integration coverage to validate the new /projects response, including contract/schema checks and deterministic name ordering.

The web UI demo's project switcher needs to enumerate projects; listProjects()
exists in the DB layer (backs the MCP list_projects tool) but had no REST
endpoint — GET /projects 404'd. Second of the roadmap-first reads unblocking
the demo (after #226).

- GET /projects → ProjectListItem[] ({id, name}, ordered by name), reusing
  listProjects(pool).
- openapi.yaml adds the get: op on /projects + ProjectListItem schema; contract
  gate response-covers it.

Closes #228

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: de89fdf6-4aef-4c88-98c2-6a5e82e106ea

📥 Commits

Reviewing files that changed from the base of the PR and between f935847 and 7819acb.

📒 Files selected for processing (1)
  • src/api/projects.integration.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/api/projects.integration.test.ts

📝 Walkthrough

Walkthrough

Adds a GET /projects REST endpoint that returns all projects ordered by name. The change introduces the ProjectListItem schema and path operation in openapi.yaml, implements listProjectsHandler in src/api/projects.ts reusing the existing listProjects(pool) DB function, registers the route in router.ts, and validates with integration and contract tests.

Changes

GET /projects list endpoint

Layer / File(s) Summary
OpenAPI schema and endpoint definition
openapi.yaml
Adds the ProjectListItem schema (id UUID, name string) and the GET /projects path operation (operationId: listProjects) with 200 returning an array of ProjectListItem and 500 mapping to InternalServerError.
Handler implementation and router wiring
src/api/projects.ts, src/api/router.ts
Extends the ../db/index.js import to include listProjects, adds the exported listProjectsHandler (returns 200 with projects array on success, 500 on error), imports the handler in router.ts, and registers router.get('/projects', listProjectsHandler).
Integration and contract tests
src/api/contract.integration.test.ts, src/api/projects.integration.test.ts
Adds get /projects to RESPONSE_COVERED, adds a contract test asserting the 200 response matches the OpenAPI schema, and adds a GET /projects integration test verifying response shape and presence of a seeded project.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • wrzonance/SpecR#212: Established the response-contract gate infrastructure (RESPONSE_COVERED, assertResponse) that this PR directly extends by adding get /projects to the covered operations.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(api): GET /projects list endpoint' directly and clearly summarizes the main change: adding a GET /projects API endpoint to list projects.
Linked Issues check ✅ Passed All acceptance criteria from issue #228 are met: OpenAPI documentation is added with contract validation, integration tests verify endpoint returns projects with {id, name}, and code passes linting.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the GET /projects endpoint as defined in issue #228; no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/api-projects-list

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/api/projects.integration.test.ts (1)

210-226: ⚡ Quick win

Pin the documented name-order guarantee in this integration test.

This test currently checks shape and membership, but not the “ordered by name” behavior the endpoint promises. Add an assertion that body.data is nondecreasing by name (and id as tie-breaker if you want to mirror DB ordering).

Suggested test assertion
   expect(Array.isArray(body.data)).toBe(true);
+  const sorted = [...body.data].sort((a, b) => {
+    const byName = a.name.localeCompare(b.name);
+    return byName !== 0 ? byName : a.id.localeCompare(b.id);
+  });
+  expect(body.data).toEqual(sorted);
+
   const mine = body.data.find((p) => p.id === testProjectId);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/projects.integration.test.ts` around lines 210 - 226, The integration
test for listing projects does not verify the documented ordering guarantee that
results are sorted by name. After the existing loop that validates each
project's structure (in the test function that checks 'returns 200 listing
projects as {id, name}, including the test project'), add an assertion that
validates body.data is sorted in non-decreasing order by the name field,
optionally using id as a tie-breaker to match database ordering if needed. This
ensures the test pins down the documented API contract about result ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/api/projects.integration.test.ts`:
- Around line 210-226: The integration test for listing projects does not verify
the documented ordering guarantee that results are sorted by name. After the
existing loop that validates each project's structure (in the test function that
checks 'returns 200 listing projects as {id, name}, including the test
project'), add an assertion that validates body.data is sorted in non-decreasing
order by the name field, optionally using id as a tie-breaker to match database
ordering if needed. This ensures the test pins down the documented API contract
about result ordering.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a876a28b-cad9-4ba0-8e24-23f1d4570765

📥 Commits

Reviewing files that changed from the base of the PR and between 9dceb3f and f935847.

📒 Files selected for processing (5)
  • openapi.yaml
  • src/api/contract.integration.test.ts
  • src/api/projects.integration.test.ts
  • src/api/projects.ts
  • src/api/router.ts

Addresses CodeRabbit's nitpick that the ordering guarantee was untested.
Asserts a controlled 'zzz-order-a'/'zzz-order-b' subset returns in name order
rather than a full toEqual(sorted) over the whole table — the latter is flaky
because it depends on the Postgres collation matching JS localeCompare and on
no other suite's projects being present.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thewrz

thewrz commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

CodeRabbit body nitpick addressed (no inline thread to resolve): pin the name-order guarantee in projects.integration.test.ts. Fixed in 7819acb.

I implemented it deterministically rather than the suggested global expect(body.data).toEqual(sorted): that assertion compares the Postgres ORDER BY name result against a JS localeCompare sort, which is flaky when the DB collation differs from JS ordering and when other suites' projects (e.g. the contract test's lowercase contract-… project) are present in the shared DB. Instead I insert two controlled projects (zzz-order-a/zzz-order-b) and assert that subset comes back in order — pinning the guarantee without collation/cross-suite flakiness.

@thewrz thewrz marked this pull request as ready for review June 20, 2026 23:03
@thewrz thewrz merged commit 2ae1d1e into main Jun 21, 2026
5 checks passed
@thewrz thewrz deleted the feat/api-projects-list branch June 21, 2026 02:52
thewrz added a commit that referenced this pull request Jun 21, 2026
With GET /libraries (#227) and GET /projects (#229) on main, the demo now
bootstraps. This conforms the rest of it to main's actual API:

- features.js capability map: panels check a flag and degrade to a clear
  "not available in this build" state instead of calling endpoints that 404.
  libraries/projectsList on (landed); library writes, project settings/sources,
  coordination/required-sections, and hard-delete stay off.
- Repoint getSpecTree -> GET /specs/:id (the single-spec read already returns
  the tree). Drop the explicit deleteReference call — references are derived
  from paragraph text, so the paragraph PATCH re-derives them.
- Restore the reference-web arcs by fetching project-scoped outbound references
  (GET /projects/:id/specs/:specId/references, ADR-024) per loaded spec.

Verified end-to-end with Playwright: clean boot, default project auto-created,
all bootstrap calls 200/201, zero console 404s, gated endpoints never hit.

Closes #230

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(api): GET /projects list endpoint

1 participant