feat(frontend): typed API client generated from the OpenAPI spec + gen-verify CI#59
feat(frontend): typed API client generated from the OpenAPI spec + gen-verify CI#59AgentWrapper wants to merge 2 commits into
Conversation
Greptile SummaryThis PR introduces a typed
Confidence Score: 5/5Safe to merge — the change is a thin, well-documented client wrapper with no runtime logic beyond URL construction and type re-exports. The client module introduces no application logic that could regress: it delegates URL construction to the caller, exports a fallback constant with explicit comments warning against relying on it, and passes types straight through from the generated schema. The dependency additions are minimal and the lock file hashes are well-formed. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Renderer Process] -->|calls| B[createApiClient baseUrl]
C[Main Process / IPC] -->|reads running.json Port| D[baseURLForPort port]
D -->|http://127.0.0.1:port| B
E[DEFAULT_PORT = 3001] -.->|last-resort fallback only| D
B -->|createClient paths baseUrl| F[openapi-fetch Client]
F -->|typed GET/POST/PATCH/DELETE| G[Daemon /api/v1/...]
H[schema.ts generated] -->|paths, components types| B
Reviews (2): Last reviewed commit: "fix(frontend): derive API base URL from ..." | Re-trigger Greptile |
…chema names Resolve the four review comments on #59: - ProjectOrDegraded.MarshalJSON now errors when neither Project nor Degraded is set instead of silently emitting `{"project": null}`, which would breach the required oneOf[Project, Degraded] contract. - requestBody.required: true is now set for addProject and updateProjectConfig via WithCustomize — swaggest left it absent (== optional) before. - schemaName replaces the TrimPrefix catch-all with an exhaustive default→clean map; an unrecognised type is returned verbatim so it surfaces in the diff rather than silently colliding with an existing schema. - nonNullableSlices strips the spurious "null" swaggest unions into every Go slice, so `projects` is `Summary[]` not `Summary[] | null`; the list handler normalises a nil slice to [] so the wire matches the non-nullable schema. Regenerated openapi.yaml + frontend schema.d.ts. Refs #59.
0de7ce0 to
49c7883
Compare
Addresses the P1 follow-up on #59: returning an error from ProjectOrDegraded.MarshalJSON does not "surface" the contract violation — envelope.WriteJSON discards the encode error after the 200 status and a partial JSON frame have already been flushed, leaving the client with a truncated, unparseable 200 (worse than the previous null). Validate the invariant upstream instead: newGetProjectResponse now returns an error when the GetResult sets neither Project nor Degraded, and the get handler maps that to a 500 envelope before any status/body is written. MarshalJSON keeps the error branch only as an unreachable last-resort backstop, with the comment corrected to say so. Adds TestProjectsAPI_GetEmptyResultIs500 to lock the clean-500 behavior. Refs #59.
…penAPI
- Remove POST /reload, PATCH /{id}, POST /{id}/repair routes and their
Manager methods (Reload, UpdateConfig, Repair) and DTOs (ReloadResult,
UpdateConfigInput) — not needed at this stage
- Merge Manager interface into manager.go; delete project.go (single-impl
split served no purpose)
- Remove dead notImplemented helper from errors.go
- Port PR #59 code-first OpenAPI generation: controllers/dto.go named
response types, specgen/build.go (4 routes), parity + drift tests,
cmd/genspec, go generate wiring; regenerate openapi.yaml
- Add swaggest deps; add YAML() method to apispec.Spec
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…chema names Resolve the four review comments on #59: - ProjectOrDegraded.MarshalJSON now errors when neither Project nor Degraded is set instead of silently emitting `{"project": null}`, which would breach the required oneOf[Project, Degraded] contract. - requestBody.required: true is now set for addProject and updateProjectConfig via WithCustomize — swaggest left it absent (== optional) before. - schemaName replaces the TrimPrefix catch-all with an exhaustive default→clean map; an unrecognised type is returned verbatim so it surfaces in the diff rather than silently colliding with an existing schema. - nonNullableSlices strips the spurious "null" swaggest unions into every Go slice, so `projects` is `Summary[]` not `Summary[] | null`; the list handler normalises a nil slice to [] so the wire matches the non-nullable schema. Regenerated openapi.yaml + frontend schema.d.ts. Refs #59.
Addresses the P1 follow-up on #59: returning an error from ProjectOrDegraded.MarshalJSON does not "surface" the contract violation — envelope.WriteJSON discards the encode error after the 200 status and a partial JSON frame have already been flushed, leaving the client with a truncated, unparseable 200 (worse than the previous null). Validate the invariant upstream instead: newGetProjectResponse now returns an error when the GetResult sets neither Project nor Degraded, and the get handler maps that to a 500 envelope before any status/body is written. MarshalJSON keeps the error branch only as an unreachable last-resort backstop, with the comment corrected to say so. Adds TestProjectsAPI_GetEmptyResultIs500 to lock the clean-500 behavior. Refs #59.
63f5daa to
4645ab9
Compare
…penAPI
- Remove POST /reload, PATCH /{id}, POST /{id}/repair routes and their
Manager methods (Reload, UpdateConfig, Repair) and DTOs (ReloadResult,
UpdateConfigInput) — not needed at this stage
- Merge Manager interface into manager.go; delete project.go (single-impl
split served no purpose)
- Remove dead notImplemented helper from errors.go
- Port PR #59 code-first OpenAPI generation: controllers/dto.go named
response types, specgen/build.go (4 routes), parity + drift tests,
cmd/genspec, go generate wiring; regenerate openapi.yaml
- Add swaggest deps; add YAML() method to apispec.Spec
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
* refactor(project): manager talks to the sqlite store; drop the in-memory store The project Manager now runs only against the durable backend store: remove the process-local MemoryStore (and NewMemoryManager), and require a real Store. The daemon already wires the sqlite store; tests now build a real temp-dir sqlite store instead of the mock. - Move Row + the Store port to project/store.go. The Store interface stays because it is the dependency-inversion port that lets the manager reach the backend without an import cycle (storage imports project.Row), not an extra mock layer — there is no longer any in-memory implementation. - NewManager requires a non-nil Store (no in-memory fallback). - Add project/manager_test.go: List/Add/Get/Remove happy paths + PATH_REQUIRED/NOT_A_GIT_REPO/PATH_ALREADY_REGISTERED/ID_ALREADY_REGISTERED, PROJECT_NOT_FOUND/INVALID_PROJECT_ID, and UpdateConfig — all against a real sqlite store (the service-logic tests #47 lacked). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(project): trim routes, consolidate package, add code-first OpenAPI - Remove POST /reload, PATCH /{id}, POST /{id}/repair routes and their Manager methods (Reload, UpdateConfig, Repair) and DTOs (ReloadResult, UpdateConfigInput) — not needed at this stage - Merge Manager interface into manager.go; delete project.go (single-impl split served no purpose) - Remove dead notImplemented helper from errors.go - Port PR #59 code-first OpenAPI generation: controllers/dto.go named response types, specgen/build.go (4 routes), parity + drift tests, cmd/genspec, go generate wiring; regenerate openapi.yaml - Add swaggest deps; add YAML() method to apispec.Spec Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> * fix(project): address PR review comments - t.Skipf → t.Fatalf in gitRepo helper: git failures now hard-fail instead of silently skipping manager tests on a misconfigured runner - FindProjectByPath: add AND archived_at IS NULL so archived paths don't permanently block re-registration (update queries/projects.sql and generated gen/projects.sql.go) - Add TestManager_ReaddAfterRemove to lock the fix Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> * fixed lint and fmt * addressed greptile comments * Apply suggestions from code review Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * project tests fix * project_tests fix * fix: Linting and formatting fix * refactor: move project manager into service layer (#68) * refactor: split service package by resource (#68) * fix: ignore archived project id conflicts (#68) * refactor: move pr manager into service layer (#68) --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: itrytoohard <ayetrytoohard@gmail.com>
064c188 to
019c985
Compare
Main already generates frontend/src/api/schema.ts from the backend OpenAPI document (#103) but ships only the types. This adds the thin runtime layer: an openapi-fetch client typed by those generated paths, so the renderer makes fully typed requests against the daemon's loopback /api/v1 surface instead of hand-writing fetch calls. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
019c985 to
c5d667f
Compare
The daemon binds AO_PORT (default 3001) and writes the actual port to running.json. Baking http://127.0.0.1:3001 into the client meant any user overriding AO_PORT would silently hit the wrong address. Replace the pre-built constant-port client with a createApiClient(baseUrl) factory plus a baseURLForPort helper, so the wiring layer passes the port discovered from running.json (via IPC). DEFAULT_PORT remains only as a documented fallback. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Why (scope change)
This PR originally added the code-first OpenAPI generator + a frontend client. The backend generator has since landed on
main(via #68/#65) and now covers projects and sessions — superseding this branch's backend work. Rather than duplicate it, this PR is repurposed to the partsmainstill lacks.What's here
frontend/src/api/schema.d.ts— generated frombackend/internal/httpd/apispec/openapi.yamlviaopenapi-typescript(npm run gen:api). Covers projects, sessions, and orchestrators.frontend/src/api/client.ts— a smallopenapi-fetchclient typed by that schema, so the renderer's request/response types come from the daemon contract instead of being hand-maintained.gen-verifyCI job — regenerates the spec from Go and the TS client from the spec, failing if either committed artifact is stale. Backend drift is already covered by theapispectests; this additionally guards the frontend artifact, which nothing else checks.Verification
Rebased on current
main; no overlap with the backend codegen or the recent CLI PRs.🤖 Generated with Claude Code