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}`