From b75219d704f66276dc9ccd9fc38a2333d5eae59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 23 Oct 2026 15:04:37 +0200 Subject: [PATCH] fix(openapi): declare the duplicate flags and the missing-link 404 of a document upload A duplicate upload has always answered meta.duplicate (and meta.deleted for a deleted source key) beside the row, as the description says, but the envelope schema declared neither. An episode or visit id that is not the caller's has always answered 404, which no response declared. --- docs/api/openapi.yaml | 16 +++++++++++++ src/lib/openapi/routes/documents.ts | 35 ++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index f53659552..c8049282e 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -19188,6 +19188,13 @@ paths: $ref: "#/components/schemas/DocumentUploadEnvelope" "401": *a1 "403": *a9 + "404": + description: An `episodeIds` or `encounterIds` id is not a live episode or visit of the caller + (`documents.inbound.episodeNotFound` / `documents.inbound.encounterNotFound`). Nothing was stored. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" "409": description: The same bytes are already stored under 20 other import source ids (`documents.inbound.sourceAliasLimit`). Nothing was stored or remembered. @@ -78996,6 +79003,15 @@ components: properties: requestId: type: string + duplicate: + description: "Present on a 200: the bytes or the source key were already stored, and `data` is the existing row (or the + receipt)." + type: boolean + const: true + deleted: + description: Present with `duplicate` when the source key belongs to a document the owner deleted. Nothing was stored. + type: boolean + const: true additionalProperties: false required: - data diff --git a/src/lib/openapi/routes/documents.ts b/src/lib/openapi/routes/documents.ts index 0808f1686..182a0cee2 100644 --- a/src/lib/openapi/routes/documents.ts +++ b/src/lib/openapi/routes/documents.ts @@ -379,11 +379,31 @@ const documentUploadReceipt = z }); // Shared by the POST 200 (duplicate) and 201 (created) responses — the -// envelope id must be registered exactly once. -const documentUploadEnvelope = dataEnvelope( - z.union([inboundDocument, documentUploadReceipt]), - "DocumentUploadEnvelope", -); +// envelope id must be registered exactly once. The duplicate answer flags +// itself at the envelope level, beside the request id. +const documentUploadEnvelope = z + .object({ + data: z.union([inboundDocument, documentUploadReceipt]), + error: z.null(), + meta: z + .object({ + requestId: z.string().optional(), + duplicate: z + .literal(true) + .optional() + .describe( + "Present on a 200: the bytes or the source key were already stored, and `data` is the existing row (or the receipt).", + ), + deleted: z + .literal(true) + .optional() + .describe( + "Present with `duplicate` when the source key belongs to a document the owner deleted. Nothing was stored.", + ), + }) + .optional(), + }) + .meta({ id: "DocumentUploadEnvelope" }); /** * A `DOCUMENT_EXTRACT` run's result. Smaller than the synchronous extract @@ -672,6 +692,11 @@ export const inboundDocumentPaths: NonNullable = { 'Unsupported or unidentifiable type (`meta.reason = "unsupportedType"`). Executables, HTML/SVG, and generic archives are always refused.', content: { "application/json": { schema: errorEnvelope } }, }, + "404": { + description: + "An `episodeIds` or `encounterIds` id is not a live episode or visit of the caller (`documents.inbound.episodeNotFound` / `documents.inbound.encounterNotFound`). Nothing was stored.", + content: { "application/json": { schema: errorEnvelope } }, + }, ...stdResponses, }, },