Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/scripts/delete-history.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ echo "▶️create patient"
patient_id=$(create | jq -r '.id')

# update the patient to create a second version
patient "$patient_id" "male" | update "$patient_id"
patient "$patient_id" "male" | update "$patient_id" > /dev/null

# expect the history to contain two entries
history="$(get_instance_history "$patient_id")"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ base="http://localhost:8080/fhir"
name=$1
expected_count=$2

data=$(base64 < "modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/$name.cql" | tr -d '\n')
data=$(base64 -w 0 < "modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/$name.cql")
library_uri=$(uuidgen | tr '[:upper:]' '[:lower:]')
measure_uri=$(uuidgen | tr '[:upper:]' '[:lower:]')

Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/evaluate-measure-util.sh
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ create_measure() {
create_bundle_library_measure() {
local measure_uri="$1"
local name="$2"
local data="$(base64 < "modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/$name.cql" | tr -d '\n')"
local data="$(base64 -w 0 < "modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/$name.cql")"
local library_uri=$(uuidgen | tr '[:upper:]' '[:lower:]')
local library="$(create_library "$library_uri" "$data")"
local measure="$(create_measure "$measure_uri" "$library_uri")"
Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/evaluate-patient-q1-measure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ evaluate-measure() {

base="http://localhost:8080/fhir"
file="modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/q1.cql"
data=$(base64 "$file" | tr -d '\n')
data=$(base64 -w 0 < "$file")
library_uri=$(uuidgen | tr '[:upper:]' '[:lower:]')
measure_uri=$(uuidgen | tr '[:upper:]' '[:lower:]')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ update() {
patient_id=$(create | jq -r '.id')

# update the patient to create a second version
patient "$patient_id" "male" | update "$patient_id"
patient "$patient_id" "male" | update "$patient_id" > /dev/null

first_page="$(curl -sfH "Accept: application/fhir+json" "$base/Patient/$patient_id/_history?_count=1")"
total="$(echo "$first_page" | jq -r .total)"
next_link="$(echo "$first_page" | jq -r '.link[] | select(.relation == "next") | .url')"

# update the patient to create a third version
patient "$patient_id" "female" | update "$patient_id"
patient "$patient_id" "female" | update "$patient_id" > /dev/null

second_page="$(curl -sfH "Accept: application/fhir+json" "$next_link")"

Expand Down
4 changes: 2 additions & 2 deletions .github/scripts/read-binary-content-via-json-found.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ set -euo pipefail
base="http://localhost:8080/fhir"

# 10 KiB of random data, base64 encoded
data="$(openssl rand -base64 10240 | tr -d '\n')"
data="$(openssl rand 10240 | base64 -w 0)"

binary() {
cat <<END
Expand All @@ -27,7 +27,7 @@ echo " - via JSON, with ID: $id_via_json"


# Retrieve the Binary resource, and Base64 encode it so it can be safely handled by Bash (JSON)
base64_encoded_binary_resource_via_json=$(curl -sfH 'Accept: application/pdf' "$base/Binary/$id_via_json" | base64 | tr -d '\n')
base64_encoded_binary_resource_via_json=$(curl -sfH 'Accept: application/pdf' "$base/Binary/$id_via_json" | base64 -w 0)


echo "Binary data retrieved. Verifying content... (JSON version)"
Expand Down
4 changes: 2 additions & 2 deletions .github/scripts/read-binary-content-via-xml-found.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ set -euo pipefail
base="http://localhost:8080/fhir"

# 10 KiB of random data, base64 encoded
data="$(openssl rand -base64 10240 | tr -d '\n')"
data="$(openssl rand 10240 | base64 -w 0)"

binary() {
cat <<END
Expand All @@ -25,7 +25,7 @@ echo "Created Binary resource that contains the Random Data"
echo " - via XML, with ID: $id_via_xml"

# Retrieve the Binary resource, and Base64 encode it so it can be safely handled by Bash (via XML)
base64_encoded_binary_resource_via_xml=$(curl -sfH 'Accept: application/pdf' "$base/Binary/$id_via_xml" | base64 | tr -d '\n')
base64_encoded_binary_resource_via_xml=$(curl -sfH 'Accept: application/pdf' "$base/Binary/$id_via_xml" | base64 -w 0)

echo "Binary data retrieved. Verifying content... (XML version)"

Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/util.sh
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ create() {
}

update() {
curl -XPUT -sfH 'Accept: application/fhir+json' -H "Content-Type: application/fhir+json" -d @- -o /dev/null "$1"
curl -XPUT -sfH 'Accept: application/fhir+json' -H "Content-Type: application/fhir+json" -d @- "$1"
}

transact() {
Expand Down
57 changes: 57 additions & 0 deletions .github/scripts/vread-binary-content-found.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/bin/bash
set -euo pipefail

# This script creates two versions of a binary resource and verifies that the
# binary content of both versions can be read via the vread interaction.

script_dir="$(dirname "$(readlink -f "$0")")"
. "$script_dir/util.sh"

base="http://localhost:8080/fhir"

# 10 KiB of random data per version, base64 encoded
data_v1="$(openssl rand 10240 | base64 -w 0)"
data_v2="$(openssl rand 10240 | base64 -w 0)"

id="$(uuidgen | tr '[:upper:]' '[:lower:]')"

binary() {
cat <<END
{
"resourceType": "Binary",
"id": "$id",
"contentType": "application/pdf",
"data": "$1"
}
END
}

# Create both versions of the Binary resource, keeping their version IDs
version_id_v1=$(binary "$data_v1" | update "$base/Binary/$id" | jq -r '.meta.versionId')
version_id_v2=$(binary "$data_v2" | update "$base/Binary/$id" | jq -r '.meta.versionId')

echo "Created Binary resource with ID $id in versions $version_id_v1 and $version_id_v2"

# Retrieve both versions, Base64 encoding them so they can be safely handled by Bash
base64_encoded_binary_resource_v1=$(curl -sfH 'Accept: application/pdf' "$base/Binary/$id/_history/$version_id_v1" | base64 -w 0)
base64_encoded_binary_resource_v2=$(curl -sfH 'Accept: application/pdf' "$base/Binary/$id/_history/$version_id_v2" | base64 -w 0)

echo "Binary data retrieved. Verifying content..."

if [ "$data_v1" = "$base64_encoded_binary_resource_v1" ]; then
echo "✅ Base64 encoding of both the Original Data and the Retrieved Resource Data match (version $version_id_v1)"
else
echo "🆘 Base64 encoding of both the Original Data and the Retrieved Resource Data are different (version $version_id_v1)"
exit 1
fi

if [ "$data_v2" = "$base64_encoded_binary_resource_v2" ]; then
echo "✅ Base64 encoding of both the Original Data and the Retrieved Resource Data match (version $version_id_v2)"
else
echo "🆘 Base64 encoding of both the Original Data and the Retrieved Resource Data are different (version $version_id_v2)"
exit 1
fi

content_type=$(curl -sf -o /dev/null -w '%{content_type}' -H 'Accept: application/pdf' "$base/Binary/$id/_history/$version_id_v1")

test "Content-Type header of the vread response" "$content_type" "application/pdf"
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1726,6 +1726,9 @@ jobs:
- name: Binary Content Download - found (via XML)
run: .github/scripts/read-binary-content-via-xml-found.sh

- name: Binary Content Download - found (via vread)
run: .github/scripts/vread-binary-content-found.sh

- name: Binary Content Upload (via raw data)
run: .github/scripts/write-binary-content-via-raw-data.sh

Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Rigorous adherence to these patterns is required:
## Documentation

* **Environment Variables:** Every new environment variable introduced via `#blaze/cfg` in `resources/blaze.edn` must be documented in `docs/deployment/environment-variables.md`, following the existing format (heading, description, default value, since badge).
* **API Behaviour:** Before finishing, state whether the change alters what a client can observe on an existing interaction or operation (accepted header, parameter, status code, response media type). If it does, update the corresponding page under `docs/api/`, including the sibling pages sharing that behaviour, and badge new sections with the version of the issue's GitHub milestone (`gh issue view <issue-number> --json milestone`). If it's unclear whether a behaviour change is user-facing, ask rather than guess.

## Release Notes

Expand Down
13 changes: 13 additions & 0 deletions docs/api/interaction/read.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ ETag: W/"23"

The version id in the `ETag` header is identical to `meta.versionId` of the returned resource and can be used in an `If-Match` header of a subsequent [update](update.md) to detect concurrent modifications.

## Binary Resources <Badge type="warning" text="Since 0.31"/>

`Binary` resources can be read either as FHIR resource or in binary form. Blaze decides based on the `Accept` header of the request:

* `application/fhir+json` and `application/fhir+xml` (as well as `*/*` or a missing `Accept` header) return the `Binary` resource itself, with its content Base64 encoded in `Binary.data`,
* every other media type returns the raw content of `Binary.data`.

```sh
curl -H 'Accept: application/pdf' "http://localhost:8080/fhir/Binary/AT4S2E5FQTPTIQPP"
```

The `Content-Type` header of such a binary response is taken from `Binary.contentType`, defaulting to `application/octet-stream` if that property is missing. Blaze doesn't match it against the media types requested in the `Accept` header.

## Handling Errors

| Status Code | Description |
Expand Down
13 changes: 13 additions & 0 deletions docs/api/interaction/vread.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ Last-Modified: Tue, 24 Jun 2025 09:03:22 GMT
ETag: W/"23"
```

## Binary Resources <Badge type="warning" text="Since 1.12.0"/>

As with the [read](read.md) interaction, every version of a `Binary` resource can be retrieved either as FHIR resource or in binary form. Blaze decides based on the `Accept` header of the request:

* `application/fhir+json` and `application/fhir+xml` (as well as `*/*` or a missing `Accept` header) return the `Binary` resource itself, with its content Base64 encoded in `Binary.data`,
* every other media type returns the raw content of `Binary.data`.

```sh
curl -H 'Accept: application/pdf' "http://localhost:8080/fhir/Binary/AT4S2E5FQTPTIQPP/_history/2"
```

The `Content-Type` header of such a binary response is taken from `Binary.contentType` of that version, defaulting to `application/octet-stream` if that property is missing. Blaze doesn't match it against the media types requested in the `Accept` header.

## Handling Errors

| Status Code | Description |
Expand Down
2 changes: 1 addition & 1 deletion evaluate-measure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ base=$1
[[ -z "$base" ]] && usage

subject_type_lower=$(echo $subject_type | tr '[:upper:]' '[:lower:]')
data=$(base64 < "$file" | tr -d '\n')
data=$(base64 -w 0 < "$file")
library_uri=$(uuidgen | tr '[:upper:]' '[:lower:]')
measure_uri=$(uuidgen | tr '[:upper:]' '[:lower:]')

Expand Down
2 changes: 2 additions & 0 deletions modules/rest-api/src/blaze/rest_api/routes.clj
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@
:blaze.rest-api.interaction/handler)}))]
["/{vid}"
(cond-> {:name (keyword name "versioned-instance")}
(= name "Binary")
(assoc :response-type :binary)
(contains? interactions :vread)
(assoc :get {:interaction "vread"
:middleware [[wrap-db node db-sync-timeout]]
Expand Down
46 changes: 46 additions & 0 deletions modules/rest-api/test/blaze/rest_api_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,52 @@
(given (call rest-api {:request-method :delete :uri "/Patient"})
:status := 200)))

(def ^:private binary-handler
(constantly
(ac/completed-future
(ring/response
{:fhir/type :fhir/Binary :id "0"
:contentType #fhir/code"application/zip"
:data #fhir/base64Binary"UEsDBAo="}))))

(def ^:private binary-config
(assoc config
::rest-api/resource-patterns
{:default
{:read #:blaze.rest-api.interaction{:handler binary-handler}
:vread #:blaze.rest-api.interaction{:handler binary-handler}}}))

(deftest binary-test
(testing "read"
(with-system [{:blaze/keys [rest-api]} binary-config]
(testing "with binary accept header"
(given (call rest-api {:request-method :get :uri "/Binary/0"
:headers {"accept" "application/zip"}})
:status := 200
[:headers "Content-Type"] := "application/zip"
[:body vec] := [0x50 0x4B 0x03 0x04 0x0A]))

(testing "with FHIR accept header"
(given (call rest-api {:request-method :get :uri "/Binary/0"
:headers {"accept" "application/fhir+json"}})
:status := 200
[:headers "Content-Type"] := "application/fhir+json;charset=utf-8"))))

(testing "vread"
(with-system [{:blaze/keys [rest-api]} binary-config]
(testing "with binary accept header"
(given (call rest-api {:request-method :get :uri "/Binary/0/_history/1"
:headers {"accept" "application/zip"}})
:status := 200
[:headers "Content-Type"] := "application/zip"
[:body vec] := [0x50 0x4B 0x03 0x04 0x0A]))

(testing "with FHIR accept header"
(given (call rest-api {:request-method :get :uri "/Binary/0/_history/1"
:headers {"accept" "application/fhir+json"}})
:status := 200
[:headers "Content-Type"] := "application/fhir+json;charset=utf-8")))))

(def auth-config
(-> (assoc-in config [:blaze/rest-api :auth-backends] [(ig/ref ::auth-backend)])
(assoc ::auth-backend {})))
Expand Down
Loading