Skip to content

Commit 4748ce9

Browse files
author
Dr. Christian Betz
committed
Make muuntaja options transparent
To configure muuntaja (or hand over a configured muuntaja instance), e.g. to add your own format handler, we need to set a muuntaja key on the request.
1 parent 92da11b commit 4748ce9

File tree

3 files changed

+57
-42
lines changed

3 files changed

+57
-42
lines changed

src/hato/client.clj

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
(:require
55
[clojure.string :as str]
66
[hato.middleware :as middleware]
7-
[clojure.java.io :as io])
7+
[clojure.java.io :as io]
8+
[muuntaja.core :as m])
89
(:import
910
(java.net.http
1011
HttpClient$Redirect
@@ -154,7 +155,7 @@
154155
155156
`request` is the map of request options output by `make-request`
156157
`response` is the raw HttpResponse"
157-
[{:keys [request ^HttpResponse response http-client]}]
158+
[{:keys [request ^HttpResponse response http-client muuntaja]}]
158159
{:uri (.toString (.uri response))
159160
:status (.statusCode response)
160161
:body (.body response)
@@ -163,6 +164,7 @@
163164
(into {}))
164165
:version (-> response (.version) (.name) Version->kw)
165166
:http-client http-client
167+
:muuntaja muuntaja
166168
:request (assoc request :http-request (.request response))})
167169

168170
(def cookie-policies
@@ -299,7 +301,7 @@
299301
(.build builder)))
300302

301303
(defn request*
302-
[{:keys [http-client async? as]
304+
[{:keys [http-client async? as muuntaja]
303305
:as req} & [respond raise]]
304306
(let [^HttpClient http-client (if (instance? HttpClient http-client) http-client (build-http-client http-client))
305307
http-request (ring-request->HttpRequest req)
@@ -309,6 +311,7 @@
309311
(response-map
310312
{:request req
311313
:http-client http-client
314+
:muuntaja (m/create muuntaja)
312315
:response resp}))
313316

314317
(-> (.sendAsync http-client http-request bh)
@@ -319,6 +322,7 @@
319322
(response-map
320323
{:request req
321324
:http-client http-client
325+
:muuntaja (m/create muuntaja)
322326
:response resp})))))
323327
(.exceptionally
324328
(reify Function

src/hato/middleware.clj

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@
2222
(java.util.zip
2323
GZIPInputStream InflaterInputStream ZipException Inflater)))
2424

25-
(def muuntaja-instance
26-
(m/create
27-
(-> muuntaja.core/default-options
28-
(assoc-in [:formats "text"] format.text/generic))))
29-
3025
;;;
3126

3227
(defn when-pos [v]
@@ -110,24 +105,24 @@
110105
(client req #(response (exceptions-response req %)) raise))))
111106

112107
;; Multimethods for coercing body type to the :as key
113-
(defmulti coerce-response-body (fn [req _] (:as req)))
108+
(defmulti coerce-response-body (fn [muuntaja-instance req response] (:as req)))
114109

115-
(defmethod coerce-response-body :clojure [_ {:keys [body] :as resp}]
110+
(defmethod coerce-response-body :clojure [_ _ {:keys [body] :as resp}]
116111
(let [^String charset (or (-> resp :content-type-params :charset) "UTF-8")]
117112
(assoc resp :body (edn/read-string (String. ^"[B" body charset)))))
118113

119-
(defmethod coerce-response-body :byte-array [_ resp]
114+
(defmethod coerce-response-body :byte-array [_ _ resp]
120115
resp)
121116

122-
(defmethod coerce-response-body :stream [_ resp]
117+
(defmethod coerce-response-body :stream [_ _ resp]
123118
resp)
124119

125-
(defmethod coerce-response-body :string [_ {:keys [body] :as resp}]
120+
(defmethod coerce-response-body :string [_ _ {:keys [body] :as resp}]
126121
(let [^String charset (or (-> resp :content-type-params :charset) "UTF-8")]
127122
(assoc resp :body (String. ^"[B" body charset))))
128123

129124
(defmethod coerce-response-body :default
130-
[_ resp]
125+
[muuntaja-instance _ resp]
131126
(try
132127
(let [decoded (m/decode-response-body muuntaja-instance resp)]
133128
(assoc resp :body decoded))
@@ -136,27 +131,23 @@
136131
:hato/errors conj e))))
137132

138133
(defn- response-body-coercion
139-
[req {:keys [body] :as resp}]
134+
[muuntaja-instance req {:keys [body] :as resp}]
140135
(if body
141-
(coerce-response-body req resp)
136+
(coerce-response-body muuntaja-instance req resp)
142137
resp))
143138

144139
(defn wrap-response-body-coercion
145140
"Middleware converting a response body from a byte-array to a different object.
146141
Defaults to a String if no :as key is specified, the `coerce-response-body`
147142
multimethod may be extended to add additional coercions."
148-
([client]
149-
(wrap-response-body-coercion
150-
muuntaja.core/instance
151-
client))
152-
([muuntaja-or-options client]
153-
(fn
154-
([req]
155-
(response-body-coercion req (client req)))
156-
([req respond raise]
157-
(client req
158-
#(respond (response-body-coercion req %))
159-
raise)))))
143+
[client]
144+
(fn
145+
([{:keys [muuntaja] :as req}]
146+
(response-body-coercion (m/create muuntaja) req (client req)))
147+
([{:keys [muuntaja] :as req} respond raise]
148+
(client req
149+
#(respond (response-body-coercion (m/create muuntaja) req %))
150+
raise))))
160151

161152
(defn content-type-value [type]
162153
(if (keyword? type)
@@ -356,9 +347,9 @@
356347
(client (method-request req) respond raise))))
357348

358349
(defn- form-params-request
359-
[{:keys [form-params content-type request-method multi-param-style]
360-
:or {content-type :x-www-form-urlencoded}
361-
:as req}]
350+
[muuntaja-instance {:keys [form-params content-type request-method multi-param-style]
351+
:or {content-type :x-www-form-urlencoded}
352+
:as req}]
362353
(if (and form-params (#{:post :put :patch :delete} request-method))
363354
(if (= :x-www-form-urlencoded content-type)
364355
(as-> req $
@@ -380,10 +371,10 @@
380371
"Middleware wrapping the submission or form parameters."
381372
[client]
382373
(fn
383-
([req]
384-
(client (form-params-request req)))
385-
([req respond raise]
386-
(client (form-params-request req) respond raise))))
374+
([{:keys [muuntaja] :as req}]
375+
(client (form-params-request (m/create muuntaja) req)))
376+
([{:keys [muuntaja] :as req} respond raise]
377+
(client (form-params-request (m/create muuntaja) req) respond raise))))
387378

388379
(defn- url-request
389380
[req]
@@ -568,6 +559,24 @@
568559
([req respond raise]
569560
(client (multipart-request req) respond raise))))
570561

562+
(def default-muuntaja-instance
563+
(-> m/default-options
564+
(assoc-in [:formats "text"] format.text/generic)))
565+
566+
(defn muuntaja-instance [{:keys [muuntaja]
567+
:or {muuntaja default-muuntaja-instance}
568+
:as req}]
569+
(assoc req :muuntaja (m/create muuntaja)))
570+
571+
(defn wrap-muuntaja
572+
"Middleware wrapping muuntaja options."
573+
[client]
574+
(fn
575+
([req]
576+
(client (muuntaja-instance req)))
577+
([req respond raise]
578+
(client (muuntaja-instance req) respond raise))))
579+
571580
(def default-middleware
572581
"The default list of middleware hato uses for wrapping requests."
573582
[wrap-request-timing
@@ -588,15 +597,17 @@
588597
wrap-content-type
589598
wrap-form-params
590599
wrap-nested-params
591-
wrap-method])
600+
wrap-method
601+
wrap-muuntaja])
592602

593603
(defn wrap-request
594604
"Returns a batteries-included HTTP request function corresponding to the given
595605
core client. See default-middleware for the middleware wrappers that are used
596-
by default"
606+
by default, which you can enrich with your muuntaja options or instance according
607+
to your needs for different content types."
597608
([request]
598609
(wrap-request request default-middleware))
599610
([request middleware]
600-
(reduce (fn [req m] (m req))
611+
(reduce (fn [req middleware-wrapper] (middleware-wrapper req))
601612
request
602613
middleware)))

test/hato/middleware_test.clj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@
172172

173173
(deftest test-wrap-output-coercion
174174
(testing "coerces is not depending on status"
175-
(are [expected status] (= expected (-> ((wrap-response-body-coercion (wrap-content-type (constantly {:status status :body (.getBytes "{\"a\": 1}")}))) {:as :json}) :body))
175+
(are [expected status] (= expected (-> ((wrap-muuntaja (wrap-response-body-coercion (wrap-content-type (constantly {:status status :body (.getBytes "{\"a\": 1}") :muuntaja default-muuntaja-instance})))) {:as :json}) :body))
176176
{:a 1} 200
177177
{:a 1} 300))
178178

@@ -258,19 +258,19 @@
258258
(is (= "application/x-www-form-urlencoded" ((:headers r) "content-type")))
259259
(is (= "moo=cow+boy%21" (as-string (:body r))))))
260260
(testing "coercing to json"
261-
(let [r ((wrap-content-type (wrap-form-params identity)) {:form-params {:moo "cow boy!"} :request-method :post :content-type :json})]
261+
(let [r ((wrap-muuntaja (wrap-content-type (wrap-form-params identity))) {:form-params {:moo "cow boy!"} :request-method :post :content-type :json})]
262262
(is (= :post (:request-method r)))
263263
(is (= "application/json" ((:headers r) "content-type")))
264264
(is (= "{\"moo\":\"cow boy!\"}" (as-string (:body r))))))
265265

266266
(testing "coercing to edn"
267-
(let [r ((wrap-content-type (wrap-form-params identity)) {:form-params {:moo "cow boy!"} :request-method :post :content-type :edn})]
267+
(let [r ((wrap-muuntaja (wrap-content-type (wrap-form-params identity))) {:form-params {:moo "cow boy!"} :request-method :post :content-type :edn})]
268268
(is (= :post (:request-method r)))
269269
(is (= "application/edn" ((:headers r) "content-type")))
270270
(is (= "{:moo \"cow boy!\"}" (as-string (:body r))))))
271271

272272
(testing "coercing to transit+json"
273-
(let [r ((wrap-content-type (wrap-form-params identity)) {:form-params {:moo "cow boy!"} :request-method :post :content-type :transit+json})]
273+
(let [r ((wrap-muuntaja (wrap-content-type (wrap-form-params identity))) {:form-params {:moo "cow boy!"} :request-method :post :content-type :transit+json})]
274274
(is (= "[\"^ \",\"~:moo\",\"cow boy!\"]" (as-string (:body r)))))))
275275

276276
(deftest test-wrap-method

0 commit comments

Comments
 (0)