Skip to content

Commit

Permalink
Implement Binary Upload (Fixes #2126) - WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
allentiak committed Jan 23, 2025
1 parent 5881330 commit 6827eb5
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 5 deletions.
14 changes: 12 additions & 2 deletions modules/rest-api/src/blaze/rest_api/routes.clj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
{:name :auth-guard
:wrap auth-guard/wrap-auth-guard})

(def ^:private wrap-binary-resource
{:name :resource
:wrap resource/wrap-binary-resource})

(def ^:private wrap-resource
{:name :resource
:wrap resource/wrap-resource})
Expand Down Expand Up @@ -103,6 +107,8 @@
{:fhir.resource/type name}
[""
(cond-> {:name (keyword name "type")}
(= name "Binary")
(assoc :response-type :binary)
(contains? interactions :search-type)
(assoc :get {:interaction "search-type"
:middleware [[wrap-db node db-sync-timeout]
Expand All @@ -111,7 +117,9 @@
:blaze.rest-api.interaction/handler)})
(contains? interactions :create)
(assoc :post {:interaction "create"
:middleware [wrap-resource]
:middleware (if (:response-type :binary)
[wrap-binary-resource]
[wrap-resource])
:handler (-> interactions :create
:blaze.rest-api.interaction/handler)})
(contains? interactions :conditional-delete-type)
Expand Down Expand Up @@ -179,7 +187,9 @@
:blaze.rest-api.interaction/handler)})
(contains? interactions :update)
(assoc :put {:interaction "update"
:middleware [wrap-resource]
:middleware (if (:response-type :binary)
[wrap-binary-resource]
[wrap-resource])
:handler (-> interactions :update
:blaze.rest-api.interaction/handler)})
(contains? interactions :delete)
Expand Down
50 changes: 50 additions & 0 deletions modules/rest-util/src/blaze/middleware/fhir/resource.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
[blaze.anomaly :as ba :refer [if-ok when-ok]]
[blaze.async.comp :as ac]
[blaze.fhir.spec :as fhir-spec]
[blaze.fhir.spec.type :as type]
[clojure.data.xml.jvm.parse :as xml-jvm]
[clojure.data.xml.tree :as xml-tree]
[clojure.java.io :as io]
Expand All @@ -17,6 +18,7 @@
(:import
[com.ctc.wstx.api WstxInputProperties]
[java.io Reader]
[java.util Base64 Base64$Encoder]
[javax.xml.stream XMLInputFactory]))

(set! *warn-on-reflection* true)
Expand Down Expand Up @@ -95,6 +97,28 @@
(assoc request :body resource))
(ba/incorrect "Missing HTTP body.")))

(def ^:private ^Base64$Encoder b64-encoder
(.withoutPadding (Base64/getUrlEncoder)))

(defn- resource-request-binary** [data]
(with-open [_ (prom/timer parse-duration-seconds "binary")]
(.encodeToString b64-encoder data)))

(defn- binary-content-type [body]
(or (-> body :contentType type/value)
"application/octet-stream"))

(defn- resource-request-binary* [{:keys [data contentType] :as body}]
(when data
(assoc body :data (resource-request-binary** data)
:contentType (binary-content-type contentType))))

(defn- resource-request-binary [{:keys [body] :as request}]
(if body
(when-ok [resource (resource-request-binary* body)]
(assoc request :body resource))
(ba/incorrect "Missing HTTP body.")))

(defn- unsupported-media-type-msg [media-type]
(format "Unsupported media type `%s` expect one of `application/fhir+json` or `application/fhir+xml`."
media-type))
Expand Down Expand Up @@ -125,3 +149,29 @@
(if-ok [request (resource-request request)]
(handler request)
ac/completed-future)))

(defn- binary-resource-request [request]
(if-let [content-type (request/content-type request)]
(cond
(json-request? content-type) (resource-request-json request)
(xml-request? content-type) (resource-request-xml request)
:else
(resource-request-binary request))
(if (str/blank? (slurp (:body request)))
(assoc request :body nil)
(ba/incorrect "Content-Type header expected, but is missing."))))

(defn wrap-binary-resource
"Middleware to parse a binary resource from the body according the content-type
header.
Updates the :body key in the request map on successful parsing and conforming
the resource to the internal format.
Returns an OperationOutcome in the internal format, skipping the handler, with
an appropriate error on parsing and conforming errors."
[handler]
(fn [request]
(if-ok [request (binary-resource-request request)]
(handler request)
ac/completed-future)))
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@

(test/use-fixtures :each tu/fixture)

(defn wrap-error [handler]
(defn- wrap-error [handler]
(fn [request]
(-> (handler request)
(ac/exceptionally handler-util/error-response))))

(def resource-handler
(def ^:private resource-handler
"A handler which just returns the :body from the request."
(-> (comp ac/completed-future :body)
wrap-resource
wrap-error))

(defn input-stream
(defn- input-stream
([^String s]
(ByteArrayInputStream. (.getBytes s StandardCharsets/UTF_8)))
([^String s closed?]
Expand Down

0 comments on commit 6827eb5

Please sign in to comment.