Skip to content

feat(api): library write endpoints — POST /libraries/clients, PATCH /libraries/:id#233

Merged
thewrz merged 2 commits into
mainfrom
feat/api-library-write
Jun 22, 2026
Merged

feat(api): library write endpoints — POST /libraries/clients, PATCH /libraries/:id#233
thewrz merged 2 commits into
mainfrom
feat/api-library-write

Conversation

@thewrz

@thewrz thewrz commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Why

The web UI demo's Library panel creates and renames client libraries, but those endpoints don't exist on main (gated behind libraryWrites by the conform #231). This re-enables that panel. Track A of the demo-backend program (after #227/#229/#231).

What

  • POST /libraries/clients → create a client-tier library. Body { name, parentLibraryId? }; parent defaults to the Default Company Master (resolved by name, not a hardcoded id), and an explicit parent must be company-tier (422). owner is set to the name. 201; 409 duplicate name; 404 unknown parent.
  • PATCH /libraries/:id → rename (name only — owner unchanged, per spec §8). Only client-tier libraries are renameable — built-in reference/company masters return 422 (renaming them would break name-based resolution like resolveDefaultLibraryId). 200; 404 unknown; 409 duplicate.
  • New updateLibraryName query; openapi.yaml documents both (shared Conflict/UnprocessableEntity responses); contract gate covers them.

Testing

  • Integration: create (default + explicit parent), rename, and every 400/404/409/422 path — libraries.integration.test.ts (25/25 with the read tests + contract gate)
  • Contract gate green (route↔spec coverage; writes allowlisted like POST /projects)
  • pnpm lint clean (eslint + tsc + prettier)
  • CI green

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

Summary by CodeRabbit

Release Notes

  • New Features
    • Added support for creating client-tier libraries via POST /libraries/clients, including optional parent assignment with sensible defaults.
    • Added support for renaming client-tier libraries via PATCH /libraries/{id}.
  • Bug Fixes
    • Improved validation and error handling for library creation/renaming scenarios (invalid input, missing/unknown resources, and duplicate names).

…libraries/:id

Re-enables the demo's Library panel (gated behind libraryWrites in #231).

- POST /libraries/clients: create a client-tier library. Body { name,
  parentLibraryId? }; parent defaults to the Default Company Master (resolved by
  name), and an explicit parent must be company-tier (422 otherwise). owner is
  set to the name. 201; 409 on duplicate name; 404 unknown parent.
- PATCH /libraries/:id: rename (name only — owner unchanged, §8). Only
  client-tier libraries are renameable; built-in masters return 422 (renaming
  them would break name-based resolution). 200; 404 unknown; 409 duplicate.
- New updateLibraryName query; openapi documents both; contract gate covers them.

Closes #232

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

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Two new library write endpoints are added end-to-end: POST /libraries/clients (create a client-tier library with optional parent resolution) and PATCH /libraries/:id (rename a client-tier library). Changes span a new updateLibraryName DB query, two exported API handlers with Zod validation and error mapping, router wiring, OpenAPI documentation, contract allowlist updates, and integration tests.

Changes

Library Write Endpoints

Layer / File(s) Summary
DB query: updateLibraryName
src/db/queries/libraries.ts, src/db/index.ts
Adds updateLibraryName issuing UPDATE … RETURNING on libraries.name, returning the updated Library or null on no match, with DatabaseError propagation. Re-exports the function from the db index.
API handlers and router wiring
src/api/libraries.ts, src/api/router.ts
Adds Zod schemas for create/rename bodies, resolveClientParent for parent resolution (explicit company-tier or seeded default), and exports createClientLibraryHandler / renameLibraryHandler with full validation, tier checks, DB calls, and pgErrorToHttp error mapping. Router gains POST /libraries/clients and PATCH /libraries/:id.
OpenAPI spec and contract allowlist
openapi.yaml, src/api/contract.integration.test.ts
Documents both new endpoints in openapi.yaml with request/response schemas and all error codes (201/200, 400, 404, 409, 422, 500). Updates RESPONSE_ALLOWLIST to include the new operations.
Integration tests
src/api/libraries.integration.test.ts
Adds post()/patch() helpers and test utilities, then covers POST /libraries/clients (default/explicit parent, 400/404/409/422) and PATCH /libraries/:id (successful rename with immutable owner, 400/404/409/422 error paths).

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Router
  participant createClientLibraryHandler
  participant renameLibraryHandler
  participant DB

  rect rgba(70, 130, 180, 0.5)
    note over Client,DB: POST /libraries/clients
    Client->>Router: POST /libraries/clients {name, parentLibraryId?}
    Router->>createClientLibraryHandler: validated request
    createClientLibraryHandler->>DB: getLibrary(parentLibraryId) or getDefaultCompanyLibrary()
    DB-->>createClientLibraryHandler: parent Library | null
    createClientLibraryHandler->>DB: createLibrary(name, parentId)
    DB-->>createClientLibraryHandler: Library | unique violation
    createClientLibraryHandler-->>Client: 201 Library | 400/404/409/422/500
  end

  rect rgba(60, 179, 113, 0.5)
    note over Client,DB: PATCH /libraries/:id
    Client->>Router: PATCH /libraries/:id {name}
    Router->>renameLibraryHandler: validated request
    renameLibraryHandler->>DB: getLibrary(id)
    DB-->>renameLibraryHandler: Library | null
    renameLibraryHandler->>DB: updateLibraryName(id, name)
    DB-->>renameLibraryHandler: Library | unique violation
    renameLibraryHandler-->>Client: 200 Library | 400/404/409/422/500
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • wrzonance/SpecR#212: Introduced the OpenAPI contract enforcement gate and RESPONSE_ALLOWLIST that this PR extends to cover the new POST /libraries/clients and PATCH /libraries/:id operations.

Suggested labels

style-fidelity:1

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% 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
Title check ✅ Passed The pull request title accurately summarizes the main change: adding two new library management endpoints (POST /libraries/clients and PATCH /libraries/:id) with concise, clear phrasing.
Linked Issues check ✅ Passed All linked issue requirements are met: both endpoints are implemented with correct behavior (create with default/explicit parent, rename client-tier only), OpenAPI documentation added, integration tests cover all paths (400/404/409/422), and linting passes.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the two library endpoints. No out-of-scope modifications detected: router updates, handlers, database queries, schemas, tests, and OpenAPI specs all align with the stated objectives.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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-library-write

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.

Actionable comments posted: 2

🤖 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.

Inline comments:
In `@src/api/libraries.integration.test.ts`:
- Around line 65-71: The companyMasterId function currently filters libraries
only by tier === 'company', which can hide regressions if multiple company-tier
libraries exist. Modify the find condition in companyMasterId to also filter by
the specific name of the seeded default company master library (in addition to
the tier check), ensuring the function correctly validates the default parent
resolution by name requirement rather than just picking the first company-tier
library found.

In `@src/api/libraries.ts`:
- Around line 78-83: In the `resolveClientParent` function, after retrieving the
library with `findLibraryByName(DEFAULT_COMPANY_LIBRARY)`, add a validation
check to ensure the returned library has the correct tier (company tier) before
returning it, not just checking if it exists. This ensures consistency with how
explicit parentLibraryId values are validated elsewhere in the function,
preventing client libraries from being attached to misconfigured non-company
parent libraries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e2a45b98-5442-4f5e-8ac7-2b25309338ff

📥 Commits

Reviewing files that changed from the base of the PR and between bb6a515 and 98e6fa7.

📒 Files selected for processing (7)
  • openapi.yaml
  • src/api/contract.integration.test.ts
  • src/api/libraries.integration.test.ts
  • src/api/libraries.ts
  • src/api/router.ts
  • src/db/index.ts
  • src/db/queries/libraries.ts

Comment thread src/api/libraries.integration.test.ts
Comment thread src/api/libraries.ts
Addresses two CodeRabbit findings on #233:
- resolveClientParent now validates the seeded Default Company Master is
  company-tier (500 if misconfigured), matching the explicit-parent path —
  defends against a misconfigured seed attaching a client to a non-company parent.
- The library test's companyMasterId helper matches by name (Default Company
  Master), not just tier, so the "default parent resolves by name" contract
  can't be masked by a stray extra company-tier library.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thewrz thewrz merged commit bc201cd into main Jun 22, 2026
13 checks passed
@thewrz thewrz deleted the feat/api-library-write branch June 22, 2026 05:19
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): library write endpoints — POST /libraries/clients, PATCH /libraries/:id

1 participant