fix(api): accept an Idempotency-Key on the titration step and the custom symptom

Two more creates the client replays out of its offline outbox ran the
handler again on the replay. A titration step carries no natural key at
all, so a lost success response left the medication with two dose
changes on the same day and the runway estimate reading off the wrong
one. A custom cycle symptom is unique on its label, so the replay did
not duplicate — it answered the 409 that uniqueness raises, reporting a
conflict the client cannot act on for a row it has no record of
creating.

Both wrap in `withIdempotency`, the composition their siblings already
ship, and both operations publish the header and the in-flight 409 so a
client generated against the contract can tell a retry is safe.

The integration file that drives the medication creates twice against
real Postgres gains the titration case: one dose-change row after the
replay, and a replayed 201 carrying `X-Idempotent-Replay: true`.

`POST /api/mcp/tokens` is deliberately not on this list. Its response
carries a freshly minted `hlk_` credential, and the wrapper refuses to
persist a secret-shaped body — publishing the header there would
promise a replay the mechanism will never perform.
This commit is contained in:
Marc-André Bombeck committed 2026-09-10 07:40:05 +02:00
1 parent 3e44d68518
commit 040b430e73
6 files changed
+211 -140

No files matched your search

+2
View File
@@ -516,6 +516,7 @@ export const cyclePaths: NonNullable<ZodOpenApiObject["paths"]> = {
},
},
post: {
parameters: [idempotencyKeyParameter],
tags: ["Cycle"],
summary: "Create a custom cycle symptom (v1.15.1)",
description:
@@ -527,6 +528,7 @@ export const cyclePaths: NonNullable<ZodOpenApiObject["paths"]> = {
},
},
responses: {
...idempotentWrite(),
"201": {
description: "The created custom symptom.",
content: {
@@ -321,6 +321,7 @@ export const medicationPaths: NonNullable<ZodOpenApiObject["paths"]> = {
},
},
post: {
parameters: [idempotencyKeyParameter],
tags: ["Medications"],
summary: "Record a titration step or a legacy stock correction",
description:
@@ -331,6 +332,7 @@ export const medicationPaths: NonNullable<ZodOpenApiObject["paths"]> = {
content: { "application/json": { schema: glp1PostBodySchema } },
},
responses: {
...idempotentWrite(),
...recordRefusal(),
"201": {
description: