Repository navigation
feat(GAT-9591): port the JSON API resource routes - #128
Conversation
|
🎉 Great job! Your PR title follows the correct format. 🚀 |
3bfa760 to
c478de7
Compare
c478de7 to
39a726d
Compare
39a726d to
6a7a699
Compare
|
Scope expanded 2026-09-21 — this PR now also carries the API contract fixes that were #129. #129 existed only because this PR was already open when the divergences were found, and its rationale was the production fixture replay, which has been removed from the repo. It is closed; its single commit is now That commit restores five behaviours the port had dropped relative to production:
Route count is 11, not the 10 this PR originally landed — The title still says "port the JSON API resource routes"; |
6a7a699 to
e6a816f
Compare
Ports the twelve JSON API resource routes into app/routes/api/ byte-identical
to poc/GAT-XXXX and registers them in app/routes.ts: /translate, /validate,
/find, /list/{schemas,templates,translations,datasets},
/get/{schema,map,form_hydration,dataset} and /openapi.json.
These are the live contracts Gateway API, gateway-web-2 and the federation
service call today. They land together because all twelve depend only on the
ported module set, and because the spec endpoint enumerates its siblings.
Also fixes /openapi.json for production. openapi.json.ts globbed
app/routes/api/*.ts at runtime, but Dockerfile.prod's final stage copies only
package.json, package-lock.json, node_modules and build/ -- app/ is not in the
image, so the glob matched nothing and the spec shipped with zero paths. Local
runs and CI could never see it because the repo is the working directory.
The swagger definition moves to app/lib/openapi-definition.json so that the
route and a new scripts/build-openapi.mjs share one source, and npm run build
now generates build/openapi.json after react-router build (which clears
build/). The generator exits non-zero rather than writing a spec with no
paths, so the failure can never be silent again. The loader reads that
artefact; outside production it falls back to globbing sources so npm run dev
still works, and in production it throws rather than serving an empty spec.
The @openapi JSDoc annotations are untouched — they are the generator's input,
not commentary. Only when and where the spec is compiled has changed.
/list/datasets and /get/dataset read the dataset file cache, which the Express service does not have: src/routes/list.js serves only templates, schemas and translations, and src/routes/get.js only schema, map and form_hydration. Both endpoints are new surface built for the admin tooling, so they move to WS4 PR01 with cache.server.ts. Neither carried an @openapi block, so the generated spec is unchanged by this commit. Refs: upgrade-plans/07-strip-admin-surface-from-ws1.md
Fixes the five divergences that replaying the production fixtures against the
ported routes turned up. Kept separate from the port itself so that PR remains
reviewable as a verbatim port and this one is reviewed as behaviour.
- GET /status was missing entirely and answered 404. Express defines it
(src/routes/index.js:13) and it is the likely liveness-probe target. Restored
as a four-line resource route that touches no schemas, templates or cache, so
it cannot fail for reasons unrelated to liveness.
- /translate lost the express-validator error envelope, returning
{message} instead of {message, errors:[{type,msg,path,location}]}. A consumer
doing errors[0].msg got a TypeError. /validate had the same defect. Both
restored, including express-validator's quirk that a missing metadata key
trips isObject() and notEmpty() separately and so produces two identical
entries, while a non-object non-empty value produces one.
- /translate stopped validating validate_input / validate_output, silently
accepting anything that was not "0". Now 1/true are true, 0/false are false,
and anything else is a 400 carrying production's exact message. Accepting
true/false is a deliberate widening over production, which took only "1"/"0";
no fixture sends them and "2" still errors identically. An empty value keeps
defaulting to true, matching express-validator's .default("1").
- /translate stopped validating that extra is an object, forwarding a string
into the JSONata binding where templates dereference extra.*.
- /get/map returned 400 where production returned 200 with
translation_map: null. A consumer branching on res.ok took the error path
instead of seeing null. Restored to 200, and since most such pairs are still
reachable by chaining templates, the response additively gains
translation_path and translation_maps. Every field production returned is
unchanged in name and type.
Fixture replay goes from 6 exact / 2 status-mismatch to 13 exact / 0.
e6a816f to
caf2cce
Compare
What
Ports the twelve JSON API resource routes into
app/routes/api/byte-identical topoc/GAT-XXXXand registers them inapp/routes.ts:/translate,/validate,/find,/list/{schemas,templates,translations,datasets},/get/{schema,map,form_hydration,dataset}and/openapi.json.Why
These are the live contracts Gateway API, gateway-web-2 and the federation service call today. They land together because all twelve depend only on the ported engine, and because the spec endpoint enumerates its siblings.
Testing
npm run typecheck,npm run lint,npm run buildall exit 0; the server bundle jumps from 5 kB to 69 kB, which is the first real exercise of Vite'sssr.externalhandling for the CJS-only packages. The/openapi.jsonfix was verified inside a built production image (docker exec … ls /app/app→ no such directory, confirming the defect; spec then served with all paths present).Notes
/openapi.jsonin production. The loader globbedapp/routes/api/*.tsat runtime, butDockerfile.prod's final stage does not copyapp/, so the glob matched nothing and the spec shipped with zero paths. Local runs and CI could never see it, because there the repo is the working directory. The spec is now compiled at build time; the generator exits non-zero rather than writing an empty spec, so it cannot fail silently again.@openapiJSDoc annotations are untouched — they are the generator's input, not commentary.