Skip to content

Commit f0da3d2

Browse files
authored
Merge pull request #365 from HL7/fix-spec-inconsistencies-358-363
Reconcile cross-operation spec inconsistencies (#358#363)
2 parents b703f5d + 1c041f9 commit f0da3d2

10 files changed

Lines changed: 455 additions & 217 deletions

input/fsh/operations.fsh

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ Description: "Export a view definition. User can provide view definition referen
6363
* parameter[2].scope[1] = #type
6464
* parameter[2].type = #code
6565
* parameter[2].binding.strength = #extensible
66-
* parameter[2].binding.valueSet = Canonical(OutputFormatCodes)
67-
* parameter[2].documentation = "Bulk export output format (for example csv, ndjson, parquet, json). Optional; if omitted, the server returns ndjson by default."
66+
* parameter[2].binding.valueSet = Canonical(ExportOutputFormatCodes)
67+
* parameter[2].documentation = "Bulk export output format (csv, ndjson, parquet, json). Optional; if omitted, the server returns ndjson by default. See Common Operation Behavior (operations-common.html)."
6868

6969
* parameter[3].name = #header
7070
* parameter[3].use = #in
@@ -235,7 +235,7 @@ Description: "Execute a view definition against supplied or server data."
235235
* parameter[0].type = #code
236236
* parameter[0].binding.strength = #extensible
237237
* parameter[0].binding.valueSet = Canonical(OutputFormatCodes)
238-
* parameter[0].documentation = "Output format for the result (for example json, ndjson, csv, parquet). Optional; if omitted, the server returns ndjson by default."
238+
* parameter[0].documentation = "Output format for the result (json, ndjson, csv, parquet, fhir). Use fhir to return results as a FHIR Parameters resource. Optional; if omitted, the server returns ndjson by default. See Common Operation Behavior (operations-common.html)."
239239

240240
* parameter[1].name = #header
241241
* parameter[1].use = #in
@@ -305,7 +305,7 @@ Description: "Execute a view definition against supplied or server data."
305305
* parameter[7].scope[1] = #type
306306
* parameter[7].scope[2] = #instance
307307
* parameter[7].type = #Resource
308-
* parameter[7].documentation = "FHIR resources to transform instead of using server data."
308+
* parameter[7].documentation = "FHIR resources to transform instead of using server data. Repeatable. A Bundle supplied here is unwrapped: the ViewDefinition runs against each Bundle.entry[*].resource rather than against the Bundle itself. See OperationDefinition-ViewDefinitionRun notes (Resource Parameter and Bundle Inputs)."
309309

310310
* parameter[8].name = #_limit
311311
* parameter[8].use = #in
@@ -333,7 +333,7 @@ Description: "Execute a view definition against supplied or server data."
333333
* parameter[10].min = 1
334334
* parameter[10].max = "1"
335335
* parameter[10].type = #Binary
336-
* parameter[10].documentation = "Transformed data encoded in the requested output format."
336+
* parameter[10].documentation = "Transformed data in the requested output format, returned as a raw binary stream in the format's native media type, not a serialized Binary resource envelope. When _format=fhir is requested, the response is a Parameters resource instead. See Common Operation Behavior (operations-common.html)."
337337

338338
Instance: SQLQueryRun
339339
Usage: #definition
@@ -364,8 +364,8 @@ Description: "Execute a SQLQuery Library against ViewDefinition tables."
364364
* parameter[0].scope[2] = #instance
365365
* parameter[0].type = #code
366366
* parameter[0].binding.strength = #extensible
367-
* parameter[0].binding.valueSet = Canonical(SQLQueryRunOutputFormatCodes)
368-
* parameter[0].documentation = "Output format for the result (json, ndjson, csv, parquet, fhir). Use fhir to return results as a FHIR Parameters resource. Optional; if omitted, the server returns ndjson by default."
367+
* parameter[0].binding.valueSet = Canonical(OutputFormatCodes)
368+
* parameter[0].documentation = "Output format for the result (json, ndjson, csv, parquet, fhir). Use fhir to return results as a FHIR Parameters resource. Optional; if omitted, the server returns ndjson by default. See Common Operation Behavior (operations-common.html)."
369369

370370
* parameter[1].name = #header
371371
* parameter[1].use = #in
@@ -430,14 +430,14 @@ Description: "Execute a SQLQuery Library against ViewDefinition tables."
430430
* parameter[7].use = #out
431431
* parameter[7].min = 1
432432
* parameter[7].max = "1"
433-
* parameter[7].type = #Resource
434-
* parameter[7].documentation = "Query results. Returns Binary for flat formats (csv, json, ndjson, parquet) or Parameters for _format=fhir."
433+
* parameter[7].type = #Binary
434+
* parameter[7].documentation = "Query results in the requested output format, returned as a raw binary stream in the format's native media type, not a serialized Binary resource envelope. When _format=fhir is requested, the response is a Parameters resource instead. See Common Operation Behavior (operations-common.html)."
435435

436436
Instance: SQLQueryExport
437437
Usage: #definition
438438
InstanceOf: OperationDefinition
439439
Title: "SQLQuery Export"
440-
Description: "Export SQLQuery Library results asynchronously using the FHIR Asynchronous Interaction Request Pattern."
440+
Description: "Export SQLQuery Library results asynchronously using the FHIR Asynchronous Bulk Data Request Pattern."
441441

442442
* id = "SQLQueryExport"
443443
* url = "http://sql-on-fhir.org/OperationDefinition/$sqlquery-export"
@@ -530,8 +530,8 @@ Description: "Export SQLQuery Library results asynchronously using the FHIR Asyn
530530
* parameter[3].scope[1] = #type
531531
* parameter[3].type = #code
532532
* parameter[3].binding.strength = #extensible
533-
* parameter[3].binding.valueSet = Canonical(OutputFormatCodes)
534-
* parameter[3].documentation = "Output format for the exported files (csv, ndjson, parquet, json)."
533+
* parameter[3].binding.valueSet = Canonical(ExportOutputFormatCodes)
534+
* parameter[3].documentation = "Output format for the exported files (csv, ndjson, parquet, json). See Common Operation Behavior (operations-common.html)."
535535

536536
* parameter[4].name = #header
537537
* parameter[4].use = #in

input/fsh/terminology.fsh

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,15 @@ Description: "Output format codes for SQL on FHIR."
7272

7373
ValueSet: OutputFormatCodes
7474
Title: "Output Format Codes"
75-
Description: "ValueSet of all codes from Output Format Codes codesystem"
75+
Description: "ValueSet of all codes from Output Format Codes codesystem. Used by the synchronous run operations; see Common Operation Behavior."
7676
* ^experimental = false
7777
* codes from system OutputFormatCodes
7878

79-
ValueSet: SQLQueryRunOutputFormatCodes
80-
Title: "SQLQuery Run Output Format Codes"
81-
Description: "Output format codes supported by the $sqlquery-run operation."
79+
ValueSet: ExportOutputFormatCodes
80+
Title: "Export Output Format Codes"
81+
Description: "Output format codes supported by the export operations. Excludes fhir, which applies to the run operations only; see Common Operation Behavior."
8282
* ^experimental = false
8383
* OutputFormatCodes#csv
8484
* OutputFormatCodes#ndjson
8585
* OutputFormatCodes#parquet
8686
* OutputFormatCodes#json
87-
* OutputFormatCodes#fhir

input/pagecontent/OperationDefinition-SQLQueryExport-intro.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Export SQLQuery Library results asynchronously using the FHIR Asynchronous Interaction Request Pattern.
1+
Export SQLQuery Library results asynchronously using the FHIR Asynchronous Bulk Data Request Pattern.
22

33
**Use Cases:**
44

@@ -19,9 +19,8 @@ Export SQLQuery Library results asynchronously using the FHIR Asynchronous Inter
1919

2020
1. Client sends request with `Prefer: respond-async` header
2121
2. Server returns `202 Accepted` with `Content-Location` polling URL
22-
3. Client polls for status until `303 See Other` redirect
23-
4. Client retrieves results from redirect location
24-
5. Client downloads exported files from `output.location` URLs
22+
3. Client polls for status until the poll returns `200 OK` with the manifest in the body
23+
4. Client downloads exported files from the `output.location` URLs in the manifest
2524

2625
This operation combines the query source and parameter binding from
2726
[`$sqlquery-run`](OperationDefinition-SQLQueryRun.html) with the asynchronous

input/pagecontent/OperationDefinition-SQLQueryExport-notes.md

Lines changed: 52 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,22 @@
44

55
#### Asynchronous Pattern
66

7-
This operation follows the FHIR Asynchronous Interaction Request Pattern:
7+
This operation follows the [FHIR Asynchronous Bulk Data Request Pattern](https://www.hl7.org/fhir/async-bulk.html); the completion response is specified once in [Common Operation Behavior — Asynchronous Delivery](operations-common.html#asynchronous-delivery):
88

99
1. Client sends request with `Prefer: respond-async` header and query source parameters
1010
2. Server returns `202 Accepted` with `Content-Location` header pointing to status URL
1111
3. Client polls the status URL for export progress
1212
4. Server responds with `202 Accepted` while export is in progress (MAY include interim results)
13-
5. Upon completion, server returns `303 See Other` with `Location` header pointing to result URL
14-
6. Client GETs the result URL to retrieve final output (identical to synchronous response format)
13+
5. Upon completion, the status poll returns `200 OK` with the manifest `Parameters` resource (`exportId`, `status`, `output`, …) **in the response body**
14+
6. Client downloads the exported files from the `output.location` URLs in the manifest
1515

16-
**Note**: This operation uses Parameters resource format instead of Bundle format to:
16+
**Note**: This operation uses a FHIR `Parameters` resource as the manifest instead of the Bulk Data JSON manifest object to:
1717

1818
- Provide structured status reporting and metadata
1919
- Allow extensible output metadata specific to export operations
2020
- Maintain consistency with other FHIR operations
2121

22-
**Note**: The `303 See Other` redirect pattern cleanly separates status polling from result retrieval, eliminating ambiguity around error handling and request header scope.
22+
**Note**: Completion is signalled by `200 OK` with the manifest in the body of the status-poll response, as in the FHIR Asynchronous Bulk Data Request Pattern. The operation does not use a `303 See Other` redirect to a separate result resource, so standard Bulk Data clients interoperate without special handling.
2323

2424
##### Async Flow Diagram
2525

@@ -43,17 +43,11 @@ sequenceDiagram
4343
rect rgb(240, 255, 240)
4444
Note over C,S: Step 3: Completion
4545
C->>S: GET /status/abc123
46-
S-->>C: 303 See Other<br/>Location: /result/abc123
47-
end
48-
49-
rect rgb(255, 255, 240)
50-
Note over C,S: Step 4: Result
51-
C->>S: GET /result/abc123
52-
S-->>C: 200 OK<br/>Body: Parameters{output: [{name, location}]}
46+
S-->>C: 200 OK<br/>Body: Parameters{status: completed, output: [{name, location}]}
5347
end
5448
5549
rect rgb(255, 248, 240)
56-
Note over C,S: Step 5: Download
50+
Note over C,S: Step 4: Download
5751
C->>S: GET /export/abc123/bp-results.csv
5852
S-->>C: 200 OK<br/>Content-Type: text/csv<br/>Body: patient_id,systolic,...
5953
end
@@ -80,8 +74,6 @@ sequenceDiagram
8074
participant S as Server
8175
8276
C->>S: GET /status/abc123
83-
S-->>C: 303 See Other<br/>Location: /result/abc123
84-
C->>S: GET /result/abc123
8577
S-->>C: 500 Internal Server Error<br/>Body: OperationOutcome{severity: error, diagnostics: ...}
8678
```
8779

@@ -109,19 +101,16 @@ Optional filtering parameters:
109101

110102
##### Status Request
111103

112-
- `Accept` (recommended) - Specifies the format of the status response
113-
114-
##### Result Request
115-
116-
- `Accept` (recommended) - Specifies the format of the final result response
104+
- `Accept` (recommended) - Specifies the format of the status response, including the completion (`200 OK`) response that carries the manifest
117105

118106
##### Header Scope
119107

120-
Request headers sent during status polling apply **only to the status response**, not to the final operation result. This separation:
121-
122-
- Allows different content negotiation for status vs. result responses
123-
- Enables servers to use different formats for interim status (e.g., minimal JSON) vs. final results (e.g., detailed Parameters)
124-
- Eliminates ambiguity about which response the headers apply to
108+
Each status-poll request's headers apply to **that poll's response**. Because
109+
completion is delivered as `200 OK` with the manifest in the body of the
110+
status-poll response (there is no separate result resource), the `Accept` header
111+
sent on the completing poll governs the representation of the manifest. This
112+
allows a client to negotiate a different representation for interim status
113+
responses (e.g. minimal JSON) than for the final manifest if it chooses.
125114

126115
#### Parameters
127116

@@ -203,8 +192,16 @@ Servers SHALL document which reference formats they support in their CapabilityS
203192

204193
##### Format Parameter Clarification
205194

206-
It is RECOMMENDED to support 'json', 'ndjson' and 'csv' formats by default.
207-
Servers may support other formats, but they should be explicitly documented in the CapabilityStatement.
195+
The supported formats (`json`, `ndjson`, `csv`, `parquet`) and the default are
196+
defined in
197+
[Common Operation Behavior](operations-common.html#output-formats) and apply to
198+
this operation. The `fhir` format is available on the run operations only:
199+
200+
- It is RECOMMENDED to support `json`, `ndjson` and `csv` by default; servers MAY
201+
support `parquet`, and SHALL document supported formats in the
202+
CapabilityStatement.
203+
- If `_format` is omitted, the server SHALL produce the export output in `ndjson`
204+
format.
208205

209206
##### Patient Parameter Clarification
210207

@@ -244,7 +241,9 @@ SQLQuery profile for the binding rules and the mapping from
244241

245242
#### Output Parameters
246243

247-
Output parameters appear in the **result response** (after following the `303 See Other` redirect), not in status polling responses.
244+
Output parameters appear in the **completion response** — the `200 OK`
245+
status-poll response that carries the manifest. They are not present in the
246+
`202 Accepted` responses returned while the export is still in progress.
248247

249248
##### Export Identifiers
250249

@@ -340,11 +339,11 @@ The $sqlquery-export operation uses standard HTTP status codes to indicate the o
340339
| Status Code | Description | When to Use |
341340
| ------------------------- | -------------------- | -------------------------------------------------------------------- |
342341
| 202 Accepted | In Progress | Export request accepted or still in progress during polling |
343-
| 303 See Other | Complete | Export complete, follow `Location` header to retrieve results |
342+
| 200 OK | Complete | Export complete; the status-poll response body carries the manifest |
344343
| 400 Bad Request | Client Error | Invalid parameters, unsupported parameters, missing required headers |
345344
| 404 Not Found | Not Found | SQLQuery Library not found, or cancelled export status URL |
346345
| 422 Unprocessable Entity | Business Logic Error | Valid request but query is invalid or cannot be executed |
347-
| 500 Internal Server Error | Server Error | Unexpected server error (at result URL indicates operation failure) |
346+
| 500 Internal Server Error | Server Error | Unexpected server error; on a status poll, indicates operation failure |
348347

349348
{:.table-data}
350349

@@ -465,35 +464,35 @@ Content-Type: application/fhir+json
465464
- **Progress Updates**: Server MAY include `X-Progress` header to indicate completion percentage
466465
- **Retry-After**: Server SHOULD include `Retry-After` header to indicate when to retry
467466
- **Interim Results**: Server MAY include partial/interim results in response body (implementation-defined)
468-
4. **Completion**: When export is ready, server responds with:
469-
- `303 See Other` status code
470-
- `Location` header pointing to the result URL
471-
- Response body is optional (MAY be empty or contain minimal status)
472-
5. **Result Retrieval**: Client GETs the result URL from the `Location` header:
473-
- `200 OK` status code with Parameters resource containing `output` locations
474-
- Response format is identical to what a synchronous call would return
475-
6. **Error Handling**: If export fails:
476-
- Status endpoint still returns `303 See Other` with `Location` header
477-
- Result URL returns appropriate error status code (e.g., `500 Internal Server Error`)
478-
- Result response contains `OperationOutcome` with error details
479-
- This cleanly separates polling errors from operation errors
480-
7. **Cancellation** (Recommended):
467+
4. **Completion**: When the export is ready, the status poll returns:
468+
- `200 OK` status code
469+
- A `Parameters` resource in the body containing `status` = `completed`, the
470+
export metadata, and the `output` entries with their download `location`s
471+
- This is the same manifest a synchronous call would return; there is no
472+
`303 See Other` redirect and no separate result URL
473+
5. **Error Handling**: If the export fails, the status poll returns the relevant
474+
error status code (e.g. `500 Internal Server Error`) with an
475+
`OperationOutcome` body. Polling-transport errors and operation failures are
476+
distinguished by the status code on the poll response itself.
477+
6. **Cancellation** (Recommended):
481478
Servers SHOULD support export cancellation via DELETE request to the status URL:
482479
- Client sends `DELETE` request to the status polling URL
483480
- Server responds with `202 Accepted`
484481
- Subsequent status requests return `404 Not Found`
485482
- Server SHOULD clean up any partial results
486-
8. **Result URL Lifetime**:
487-
Result URLs SHALL remain valid for at least 24 hours after export completion:
488-
- Servers SHOULD support multiple retrievals of the same result
489-
- Servers MAY include an `Expires` header to indicate result URL expiration
483+
7. **Result Lifetime**:
484+
The completed status URL (which returns the manifest) and the
485+
`output.location` download URLs SHALL remain valid for at least 24 hours after
486+
export completion:
487+
- Servers SHOULD support multiple retrievals of the completed manifest
488+
- Servers MAY include an `Expires` header to indicate when the URLs expire
490489
- Clients should retrieve results promptly but can retry within the validity window
491-
9. **Access Control**:
492-
Servers SHALL protect status and result URLs with appropriate access controls:
490+
8. **Access Control**:
491+
Servers SHALL protect status and download URLs with appropriate access controls:
493492
- Same authorization context as the original request, OR
494493
- Non-guessable URLs (e.g., cryptographically random tokens)
495494
- Unauthorized access attempts return `401 Unauthorized` or `403 Forbidden`
496-
10. **File Download**: Client downloads the output from URLs in the `output.location` parameters.
495+
9. **File Download**: Client downloads the output from URLs in the `output.location` parameters.
497496

498497
#### Examples
499498

@@ -704,25 +703,7 @@ Accept: application/fhir+json
704703
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc...
705704
```
706705

707-
Response indicates completion with redirect to result URL:
708-
709-
```http
710-
HTTP/1.1 303 See Other
711-
Location: https://example.com/fhir/export/550e8400-e29b-41d4-a716-446655440000/result
712-
```
713-
714-
**Step 6: Result Retrieval**
715-
716-
Client follows the `Location` header to retrieve the final results:
717-
718-
```http
719-
GET /fhir/export/550e8400-e29b-41d4-a716-446655440000/result HTTP/1.1
720-
Host: example.com
721-
Accept: application/fhir+json
722-
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc...
723-
```
724-
725-
Response contains the export results:
706+
The status poll returns `200 OK` with the manifest in the body; there is no redirect:
726707

727708
```http
728709
HTTP/1.1 200 OK
@@ -777,7 +758,7 @@ Expires: Wed, 04 Mar 2026 14:30:42 GMT
777758
}
778759
```
779760

780-
**Step 7: Download Files**
761+
**Step 6: Download Files**
781762

782763
Client downloads each file:
783764

@@ -910,7 +891,7 @@ Prefer: respond-async
910891
{
911892
"name": "viewReference",
912893
"valueReference": {
913-
"reference": "Binary/UsCoreBloodPressures"
894+
"reference": "ViewDefinition/UsCoreBloodPressures"
914895
}
915896
}
916897
]

0 commit comments

Comments
 (0)