mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
Correct three contract claims the published spec got wrong
The `type` query parameter on GET /api/insights/derived carried the eleven
VITALS_BASELINE vitals and described itself as being for that metric only.
SAME_TIME_BASELINE takes a `type` too, and its four cumulative day metrics
were absent from the enum, so a client generated against the spec could not
send them at all. The parameter now publishes the union of both registry
sets and the description says which values belong to which metric. A guard
reads the enum back out of the assembled document and pins it to the
registry, so a type added to either engine reaches the contract with it.
The idempotency wrapper's two 409 arms sent `error` as `{ message }`. That
was the only object-shaped error the application emitted; every other route
sends the documented `{ data, error: <string> }` envelope, which is also
what the spec has always promised. The code was wrong, not the spec. Both
arms now build their response through one helper that sends the string, so
every route under withIdempotency serves the envelope its clients decode.
The hero score-ring comment still described MED_COMPLIANCE as pooled 7-day
adherence. It has been today's dose progress since v1.27.8.
This commit is contained in:
1 parent
fb1bf710e6
commit
43c591fe19
5 files changed
+266
-28
No files matched your search
+27
-20
@@ -329,6 +329,31 @@ export async function defaultUserIdResolver(): Promise<string | null> {
|
||||
*
|
||||
* No-op when the header is missing or the value is malformed.
|
||||
*/
|
||||
/**
|
||||
* The 409 both in-flight arms return.
|
||||
*
|
||||
* `error` is a STRING, matching the envelope `@/lib/api-response` defines
|
||||
* for every other route (`{ data: null, error: <prose>, meta? }`). Until
|
||||
* v1.35.1 these two literals sent `error` as `{ message }` — the only
|
||||
* object-shaped error the app ever emitted, inherited by every route under
|
||||
* `withIdempotency`, and never what the published contract promised. A
|
||||
* client decoding the documented envelope now gets what it was generated
|
||||
* against.
|
||||
*
|
||||
* Built once rather than twice so the two arms cannot drift apart again.
|
||||
* The response is hand-built rather than routed through `apiError` because
|
||||
* the 409 carries the `X-Idempotent-Replay` header.
|
||||
*/
|
||||
function inflightConflictResponse(): Response {
|
||||
return NextResponse.json(
|
||||
{
|
||||
data: null,
|
||||
error: "A request with this Idempotency-Key is already in progress",
|
||||
},
|
||||
{ status: 409, headers: { "X-Idempotent-Replay": "false" } },
|
||||
);
|
||||
}
|
||||
|
||||
export function withIdempotency<
|
||||
Args extends [Request | NextRequest, ...unknown[]],
|
||||
>(
|
||||
@@ -368,16 +393,7 @@ export function withIdempotency<
|
||||
action: { name: "idempotency.inflight_conflict" },
|
||||
meta: { method: ctx.method, path: ctx.path },
|
||||
});
|
||||
return NextResponse.json(
|
||||
{
|
||||
data: null,
|
||||
error: {
|
||||
message:
|
||||
"A request with this Idempotency-Key is already in progress",
|
||||
},
|
||||
},
|
||||
{ status: 409, headers: { "X-Idempotent-Replay": "false" } },
|
||||
);
|
||||
return inflightConflictResponse();
|
||||
}
|
||||
|
||||
// Claim the key before running the handler. If a racing request beats
|
||||
@@ -389,16 +405,7 @@ export function withIdempotency<
|
||||
action: { name: "idempotency.inflight_conflict" },
|
||||
meta: { method: ctx.method, path: ctx.path },
|
||||
});
|
||||
return NextResponse.json(
|
||||
{
|
||||
data: null,
|
||||
error: {
|
||||
message:
|
||||
"A request with this Idempotency-Key is already in progress",
|
||||
},
|
||||
},
|
||||
{ status: 409, headers: { "X-Idempotent-Replay": "false" } },
|
||||
);
|
||||
return inflightConflictResponse();
|
||||
}
|
||||
|
||||
let response: Response;
|
||||
|
||||
Reference in new issue
Block a user