From f741c0a9ae8713789c3ce87386701b2d77dd2337 Mon Sep 17 00:00:00 2001 From: "Aaron K. Clark" Date: Tue, 19 May 2026 13:10:48 -0500 Subject: [PATCH] docs(openapi): declare POST /v1/timeentry 201 envelope shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parallel to #316 (customer POST envelope fix). The timeentry POST 201 had a description and the `Idempotency-Replay` header declaration but NO content schema at all — strictly worse than the customer case before #316, where the body was at least declared (wrongly) as a bare entity. SDK code-gen here couldn't reach the row inside the envelope from generated client types because the body shape was completely unspecified. The controller emits `{message, timeEntry}`. Pin that envelope in the spec and add a corresponding test in `tests/api/openapi.test.js` so a regression on either side fails CI. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/config/openapi.js | 16 ++++++++++++++++ tests/api/openapi.test.js | 15 +++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/app/config/openapi.js b/app/config/openapi.js index f0e3151..87006fa 100644 --- a/app/config/openapi.js +++ b/app/config/openapi.js @@ -734,6 +734,22 @@ const spec = { // Declare the replay header so SDK generators surface // the field on single-create writes. headers: idempotencyReplayResponseHeader, + // Controller returns `{message, timeEntry}` — same + // envelope drift fixed for /v1/customer in #316. + // Pin the shape so SDK code-gen builds the right + // client type instead of leaving the body + // unspecified. + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + timeEntry: { $ref: '#/components/schemas/TimeEntry' }, + }, + }, + }, + }, }, 400: { description: 'Bad request — missing teCustId or teStartedAt' }, 403: { description: 'Missing or invalid authKey' }, diff --git a/tests/api/openapi.test.js b/tests/api/openapi.test.js index 2d2f90f..7c33af3 100644 --- a/tests/api/openapi.test.js +++ b/tests/api/openapi.test.js @@ -70,6 +70,21 @@ describe('OpenAPI spec', () => { expect(schemas.TimeEntry.properties.teStartedAt).toBeDefined(); }); + 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 + // content schema at all, so SDK code-gen modeled the body + // as a bare untyped 201 — strictly worse than the customer + // case because the row inside the envelope was unreachable + // from generated client types. + const res = await request(app).get('/openapi.json'); + const r201 = res.body.paths['/v1/timeentry'].post.responses['201']; + const schema = r201.content['application/json'].schema; + expect(schema.type).toBe('object'); + expect(schema.properties.message).toBeDefined(); + expect(schema.properties.timeEntry.$ref).toBe('#/components/schemas/TimeEntry'); + }); + 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