From d1af21dc7ab2868b867b749a5f7c664311584d59 Mon Sep 17 00:00:00 2001 From: "Aaron K. Clark" Date: Tue, 19 May 2026 13:28:49 -0500 Subject: [PATCH] docs(openapi): declare POST /v1/customer/bulk 201 envelope shape Same drift pattern as #316 (customer POST) and #326 (timeentry POST), now for the bulk variant. The spec declared description + the Idempotency-Replay header but no content schema, so SDK code-gen modeled the response body as untyped. The controller (`_bulk-helpers.makeBulkCreate`) emits: { message, count, customers: Customer[] } Pin that shape in the spec and add a corresponding test in `tests/api/openapi.test.js`. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/config/openapi.js | 21 +++++++++++++++++++++ tests/api/openapi.test.js | 16 ++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/app/config/openapi.js b/app/config/openapi.js index 87006fa..12724ed 100644 --- a/app/config/openapi.js +++ b/app/config/openapi.js @@ -572,6 +572,27 @@ const spec = { // see it on all 13 bulk endpoints, not 12-of-13. // Matches the factory output from #168. headers: idempotencyReplayResponseHeader, + // Controller (`_bulk-helpers.makeBulkCreate`) emits + // `{message, count, customers}` on success. The spec + // previously left the body unspecified — same drift + // pattern fixed for single-create POST endpoints in + // #316 (customer) and #326 (timeentry). Pin the + // envelope here so SDK code-gen models the response. + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + count: { type: 'integer' }, + customers: { + type: 'array', + items: { $ref: '#/components/schemas/Customer' }, + }, + }, + }, + }, + }, }, 400: { description: 'Validation failure (array empty / master without custCompId on some entry)' }, 403: { description: 'Missing authKey or cross-tenant create attempt' }, diff --git a/tests/api/openapi.test.js b/tests/api/openapi.test.js index 7c33af3..4adb070 100644 --- a/tests/api/openapi.test.js +++ b/tests/api/openapi.test.js @@ -70,6 +70,22 @@ describe('OpenAPI spec', () => { expect(schemas.TimeEntry.properties.teStartedAt).toBeDefined(); }); + test('POST /v1/customer/bulk 201 declares the {message, count, customers} envelope', async () => { + // makeBulkCreate (app/controllers/_bulk-helpers.js) emits + // {message, count, }. The spec previously had + // description + headers but no content schema, so SDK + // code-gen modeled the response as untyped. Same drift + // pattern as #316 / #326 but for the bulk variant. + const res = await request(app).get('/openapi.json'); + const r201 = res.body.paths['/v1/customer/bulk'].post.responses['201']; + const schema = r201.content['application/json'].schema; + expect(schema.type).toBe('object'); + expect(schema.properties.message).toBeDefined(); + expect(schema.properties.count.type).toBe('integer'); + expect(schema.properties.customers.type).toBe('array'); + expect(schema.properties.customers.items.$ref).toBe('#/components/schemas/Customer'); + }); + test('POST /v1/timeentry 201 declares the {message, timeEntry} envelope', async () => { // Same envelope-drift fix as #316 (customer POST) and #312 // (customer GET). Pre-fix the timeentry POST 201 had no