From c3449ab58458a0982370cd5f16780ecae946dd45 Mon Sep 17 00:00:00 2001 From: "Aaron K. Clark" Date: Tue, 19 May 2026 12:41:22 -0500 Subject: [PATCH] docs(openapi): fix POST /v1/customer 201 response-shape drift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Companion to #312 (which fixed the same drift on the GET endpoint). The OpenAPI spec declared the POST 201 body as a bare Customer (`$ref: '#/components/schemas/Customer'`). The controller actually wraps the row in a `{message, customer}` envelope (no `customers` plural alias here — that wart is GET-only). SDK generators reading the spec built clients that expected the bare row and then failed to find the fields at runtime because they live one level deeper inside the envelope. Same client-side breakage; same fix here. Pin the new shape with a test in `tests/api/openapi.test.js` mirroring the GET-endpoint assertion. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/config/openapi.js | 16 +++++++++++++++- tests/api/openapi.test.js | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/app/config/openapi.js b/app/config/openapi.js index 870406a..f0e3151 100644 --- a/app/config/openapi.js +++ b/app/config/openapi.js @@ -694,7 +694,21 @@ const spec = { // Declare it so SDK generators surface the field // on the client side. headers: idempotencyReplayResponseHeader, - content: { 'application/json': { schema: { $ref: '#/components/schemas/Customer' } } }, + // Controller wraps the row in {message, customer} — + // same envelope pattern fixed for GET in #312. + // Surface the envelope here so SDK code-gen + // doesn't model the body as a bare Customer. + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + customer: { $ref: '#/components/schemas/Customer' }, + }, + }, + }, + }, }, 400: { description: 'Bad request' }, 403: { description: 'Missing or invalid authKey' }, diff --git a/tests/api/openapi.test.js b/tests/api/openapi.test.js index 0ba3053..2d2f90f 100644 --- a/tests/api/openapi.test.js +++ b/tests/api/openapi.test.js @@ -70,6 +70,20 @@ describe('OpenAPI spec', () => { expect(schemas.TimeEntry.properties.teStartedAt).toBeDefined(); }); + test('POST /v1/customer 201 declares the {message, customer} envelope', async () => { + // Same envelope-drift fix as the GET endpoint below (#312) + // but for the create path. The controller responds with + // `{message, customer}` on a successful 201; the spec + // previously said the body was a bare Customer, so SDK + // generators built clients that failed to find the row. + const res = await request(app).get('/openapi.json'); + const r201 = res.body.paths['/v1/customer'].post.responses['201']; + const schema = r201.content['application/json'].schema; + expect(schema.type).toBe('object'); + expect(schema.properties.message).toBeDefined(); + expect(schema.properties.customer.$ref).toBe('#/components/schemas/Customer'); + }); + test('GET /v1/customer/{id} 200 declares the {message, customer, customers} envelope', async () => { // Pre-#292 the spec said the body was a bare Customer. The // controller actually returns a `{message, customer, customers}`