Merge work/fhir-retire

This commit is contained in:
Marc-André Bombeck committed 2026-08-01 11:38:55 +02:00
commit f6bd8be6d5
5 files changed
+12 -71

No files matched your search

-16
View File
@@ -16615,12 +16615,6 @@ components:
type: boolean
needsReselection:
type: boolean
allowFhirApi:
type: boolean
resourceTypes:
type: array
items:
type: string
expiresAt:
type: string
createdAt:
@@ -16659,8 +16653,6 @@ components:
- documentCount
- documentOnly
- needsReselection
- allowFhirApi
- resourceTypes
- expiresAt
- createdAt
- revokedAt
@@ -16713,12 +16705,6 @@ components:
type: boolean
needsReselection:
type: boolean
allowFhirApi:
type: boolean
resourceTypes:
type: array
items:
type: string
expiresAt:
type: string
createdAt:
@@ -16744,8 +16730,6 @@ components:
- documentCount
- documentOnly
- needsReselection
- allowFhirApi
- resourceTypes
- expiresAt
- createdAt
- revokedAt
+11 -20
View File
@@ -374,8 +374,6 @@ describe("DELETE /api/share-links/[id] — revoke", () => {
* Mutation checks:
* - drop the `SHARE_LINK_FORBIDDEN_LEAVES` check in the route → "refuses the
* insurance leaf by name" goes red.
* - drop `RETIRED_FHIR_SCOPE_FIELDS` from the create response → "keeps the
* retired scope fields on the response" goes red.
* - make `needsReselection` always false → "flags a link whose frozen scope
* predates the selection model" goes red.
*/
@@ -433,21 +431,17 @@ describe("POST /api/share-links — the frozen scope", () => {
expect(createArg.data.sectionsJson).toEqual({ v: 2, leaves: [] });
});
it("keeps the retired scope fields on the response", async () => {
// Response-only, serving constants. The request refuses them and the
// columns are gone; they stay here until the native client confirms it
// does not decode them.
it("the retired FHIR scope fields are gone from the create response", async () => {
// #70 — the native client confirmed removal breaks no shipped decoder.
vi.mocked(prisma.clinicianShareLink.create).mockResolvedValue(
storedRow() as never,
);
const res = await POST(
postReq(validBody({ selection: { v: 2, leaves: ["WEIGHT"] } })),
);
const body = (await res.json()) as {
data: { allowFhirApi: boolean; resourceTypes: string[] };
};
expect(body.data.allowFhirApi).toBe(false);
expect(body.data.resourceTypes).toEqual([]);
const body = (await res.json()) as { data: Record<string, unknown> };
expect(body.data).not.toHaveProperty("allowFhirApi");
expect(body.data).not.toHaveProperty("resourceTypes");
});
});
@@ -465,12 +459,9 @@ describe("GET /api/share-links — the pick-again state", () => {
const res = await GET();
const body = (await res.json()) as {
data: {
shareLinks: Array<{
id: string;
needsReselection: boolean;
allowFhirApi: boolean;
resourceTypes: string[];
}>;
shareLinks: Array<
{ id: string; needsReselection: boolean } & Record<string, unknown>
>;
};
};
const byId = new Map(body.data.shareLinks.map((l) => [l.id, l]));
@@ -479,8 +470,8 @@ describe("GET /api/share-links — the pick-again state", () => {
// A documents-only link carried no report scope to begin with, so it was
// never revoked and is not asked to pick again.
expect(byId.get("docs")!.needsReselection).toBe(false);
// The retired fields ride the list response too.
expect(byId.get("current")!.allowFhirApi).toBe(false);
expect(byId.get("current")!.resourceTypes).toEqual([]);
// The retired FHIR scope fields are gone from the list response too.
expect(byId.get("current")).not.toHaveProperty("allowFhirApi");
expect(byId.get("current")).not.toHaveProperty("resourceTypes");
});
});
+1 -21
View File
@@ -87,22 +87,6 @@ const shareLinkSummarySelect = {
_count: { select: { documents: true } },
} as const;
/**
* RETIRED, kept on the response only.
*
* These two fields described a scoped FHIR face for a share link that was
* never built. The request schema refuses them — sending either is a 422 —
* their columns are dropped, and nothing reads or honours them. They stay in
* the create and list responses, serving the constant `false` and `[]`,
* because the native client was given this response shape and a missing
* non-optional key fails a whole Swift decode. They come out once #70 in the
* native repository confirms the client does not decode them.
*/
const RETIRED_FHIR_SCOPE_FIELDS = {
allowFhirApi: false,
resourceTypes: [] as string[],
} as const;
/** Project a stored row to the safe owner-facing shape (never the token). */
function toSummary(row: {
id: string;
@@ -279,7 +263,6 @@ export const POST = apiHandler(async (request: NextRequest) => {
return apiSuccess(
{
...toSummary(created),
...RETIRED_FHIR_SCOPE_FIELDS,
token: rawToken,
passphrase: rawPassphrase,
shareUrl,
@@ -300,9 +283,6 @@ export const GET = apiHandler(async () => {
});
return apiSuccess({
shareLinks: rows.map((row) => ({
...toSummary(row),
...RETIRED_FHIR_SCOPE_FIELDS,
})),
shareLinks: rows.map((row) => toSummary(row)),
});
});
@@ -83,14 +83,6 @@ export interface ShareLinkSummary {
* hashes). `qrUrl` carries the passphrase in the URL fragment (`#k=`).
*/
export interface ShareLinkCreated extends ShareLinkSummary {
/**
* RETIRED, response-only. They configured a FHIR face that was never built;
* the request refuses them and the columns are gone. They stay here because
* the native client was given this shape and a missing non-optional key
* fails a whole Swift decode — see #70 in the native repository.
*/
allowFhirApi: boolean;
resourceTypes: string[];
token: string;
passphrase: string;
shareUrl: string;
-6
View File
@@ -36,12 +36,6 @@ const shareLinkSummary = z.object({
// link was revoked on upgrade; the sharing list surfaces this so the owner
// knows to re-mint rather than wondering why a working link stopped.
needsReselection: z.boolean(),
// RETIRED, response-only, serving the constants `false` and `[]`. They
// configured a FHIR face that was never built; the request refuses them and
// their columns are gone. They stay on the response until the native client
// confirms it does not decode them.
allowFhirApi: z.boolean(),
resourceTypes: z.array(z.string()),
expiresAt: z.string(),
createdAt: z.string(),
revokedAt: z.string().nullable(),