Skip to content

Commit 6a47291

Browse files
committed
refactor(shl): treat complete as an affirmation, drop the withheld flag
`maxhealth_records_withheld` was added an hour ago for a warning neither viewer is going to show, so it ships nowhere. Removed before it became a field we had to keep emitting for someone. What replaces it costs nothing: `complete` is sound as long as only `true` is acted on. `true` means no selective scope and no study scope — the patient's whole record, which is worth saying. `false` is the ambiguous half, and no longer has to carry meaning: what a narrowed share covers is what the spec's `query` hints now say. So the recipient is told something only when it can be said truthfully, and a narrowed share says nothing rather than raising a caveat the reader has to interpret. isCompleteShare is documented as affirmation-only rather than deprecated.
1 parent 6f97a69 commit 6a47291

3 files changed

Lines changed: 8 additions & 41 deletions

File tree

backend/src/lib/shl-scope.ts

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -197,32 +197,21 @@ interface FhirBundleLike {
197197
}
198198

199199
/**
200-
* The `query` hints for this share — the SHL spec's optional field on
201-
* `application/smart-api-access`, "hints to the client, indicating queries it
202-
* might want to make".
200+
* The SHL spec's `query` hints. Carries the identifier filter `scopeFhirRequest`
201+
* forces, so what the recipient is told to run is what will be allowed.
203202
*
204-
* A study-scoped link has no other way to say what it is about, so a recipient
205-
* fires its usual sweep and the default-deny proxy rejects nearly all of it. The
206-
* hint carries the same identifier filter `scopeFhirRequest` forces, so what the
207-
* recipient is told to run is exactly what will be allowed.
208-
*
209-
* Whole-patient shares get no hints on purpose. Listing the reachable types would
210-
* name the withheld ones by omission, which is more than the patient agreed to
211-
* disclose.
203+
* Whole-patient shares get none: naming the reachable types names the withheld
204+
* ones by omission.
212205
*/
213206
export function shareQueryHints(narrowing: { studyInstanceUID?: string }): string[] | undefined {
214207
if (!narrowing.studyInstanceUID) return undefined
215208
return [`ImagingStudy?identifier=urn:oid:${narrowing.studyInstanceUID}`]
216209
}
217210

218211
/**
219-
* True only when NOTHING narrows the share.
220-
*
221-
* @deprecated Conflates the two narrowings it counts: a study-scoped link and a
222-
* de-selected record both report `false`, which reads as a warning for one and as
223-
* the definition of the link for the other. Recipients should use the `query`
224-
* hints for what the share covers and `maxhealth_records_withheld` for what was
225-
* held back. Kept until both viewers ship those.
212+
* True only when NOTHING narrows the share. Sound as an affirmation only: `false`
213+
* covers both a study-scoped link and a de-selected record, which mean opposite
214+
* things to a reader. Act on `true`; use the `query` hints for anything else.
226215
*/
227216
export function isCompleteShare(narrowing: {
228217
selectiveScope?: SelectiveScope

backend/src/routes/api/shl.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,7 @@ export function buildSmartApiAccess(session: {
121121
aud: `${config.baseUrl}/api/shl/fhir`,
122122
// Spec field. Undefined drops out of the JSON, which is the "no hints" case.
123123
query: shareQueryHints({ studyInstanceUID: session.studyInstanceUID }),
124-
// Ours, and named so: nothing in the SHL spec describes a withheld record.
125-
maxhealth_records_withheld: isSelectiveScopeActive(selectiveScope),
126-
// Deprecated — see isCompleteShare. Emitted until both viewers read the above.
124+
// Ours. Only `true` carries meaning — see isCompleteShare.
127125
complete: isCompleteShare({
128126
selectiveScope,
129127
studyInstanceUID: session.studyInstanceUID,

backend/test/shl-access-document.test.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,26 +40,6 @@ describe('buildSmartApiAccess — query hints', () => {
4040
})
4141
})
4242

43-
describe('buildSmartApiAccess — withheld records', () => {
44-
it('reports nothing withheld from a whole-patient share', () => {
45-
expect(parse(buildSmartApiAccess(base)).maxhealth_records_withheld).toBe(false)
46-
})
47-
48-
it('reports records withheld when the patient de-selected some', () => {
49-
const doc = parse(buildSmartApiAccess({
50-
...base,
51-
shareScope: { excludedTypes: ['Condition'], excludedIds: [], excludedObservationCategories: [] },
52-
}))
53-
expect(doc.maxhealth_records_withheld).toBe(true)
54-
})
55-
56-
/** Study scoping is not withholding — it is what the link is for. */
57-
it('reports nothing withheld from a study-scoped share', () => {
58-
const doc = parse(buildSmartApiAccess({ ...base, studyInstanceUID: '1.2.840.113619.2.55.3' }))
59-
expect(doc.maxhealth_records_withheld).toBe(false)
60-
})
61-
})
62-
6343
describe('buildSmartApiAccess', () => {
6444
it('reports a whole-patient share as complete', () => {
6545
expect(parse(buildSmartApiAccess(base)).complete).toBe(true)

0 commit comments

Comments
 (0)