mirror of
https://github.com/metosin/reitit.git
synced 2025-12-19 09:21:10 +00:00
Merge pull request #52 from metosin/coercion-samples
Cleanup coercion & samples
This commit is contained in:
commit
653743a25f
26 changed files with 419 additions and 93 deletions
|
|
@ -16,12 +16,12 @@ To use `Coercion` with Ring, one needs to do the following:
|
||||||
* `:responses` map, with response status codes as keys (or `:default` for "everything else") with maps with `:schema` and optionally `:description` as values.
|
* `:responses` map, with response status codes as keys (or `:default` for "everything else") with maps with `:schema` and optionally `:description` as values.
|
||||||
2. Set a `Coercion` implementation to route data under `:coercion`
|
2. Set a `Coercion` implementation to route data under `:coercion`
|
||||||
3. Mount request & response coercion middleware to the routes (can be done for all routes as the middleware are only mounted to routes which have the parameters &/ responses defined):
|
3. Mount request & response coercion middleware to the routes (can be done for all routes as the middleware are only mounted to routes which have the parameters &/ responses defined):
|
||||||
* `reitit.ring.coercion/gen-wrap-coerce-parameters`
|
* `reitit.ring.coercion/coerce-request-middleware`
|
||||||
* `reitit.ring.coercion/gen-wrap-coerce-response`
|
* `reitit.ring.coercion/coerce-response-middleware`
|
||||||
|
|
||||||
If the request coercion succeeds, the coerced parameters are injected into request under `:parameters`.
|
If the request coercion succeeds, the coerced parameters are injected into request under `:parameters`.
|
||||||
|
|
||||||
If either request or response coercion fails, an descriptive error is thrown. To turn the exceptions into http responses, one can also mount the `reitit.ring.coercion/gen-wrap-coerce-exceptions` middleware
|
If either request or response coercion fails, an descriptive error is thrown. To turn the exceptions into http responses, one can also mount the `reitit.ring.coercion/coerce-exceptions-middleware` middleware
|
||||||
|
|
||||||
### Example with Schema
|
### Example with Schema
|
||||||
|
|
||||||
|
|
@ -40,9 +40,9 @@ If either request or response coercion fails, an descriptive error is thrown. To
|
||||||
:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||||
{:status 200
|
{:status 200
|
||||||
:body {:total (+ x y)}})}}]]
|
:body {:total (+ x y)}})}}]]
|
||||||
{:data {:middleware [coercion/gen-wrap-coerce-exceptions
|
{:data {:middleware [coercion/coerce-exceptions-middleware
|
||||||
coercion/gen-wrap-coerce-parameters
|
coercion/coerce-request-middleware
|
||||||
coercion/gen-wrap-coerce-response]
|
coercion/coerce-response-middleware]
|
||||||
:coercion schema/coercion}})))
|
:coercion schema/coercion}})))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -89,9 +89,9 @@ Invalid request:
|
||||||
:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||||
{:status 200
|
{:status 200
|
||||||
:body {:total (+ x y)}})}}]]
|
:body {:total (+ x y)}})}}]]
|
||||||
{:data {:middleware [coercion/gen-wrap-coerce-exceptions
|
{:data {:middleware [coercion/coerce-exceptions-middleware
|
||||||
coercion/gen-wrap-coerce-parameters
|
coercion/coerce-request-middleware
|
||||||
coercion/gen-wrap-coerce-response]
|
coercion/coerce-response-middleware]
|
||||||
:coercion spec/coercion}})))
|
:coercion spec/coercion}})))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -152,9 +152,9 @@ Currently, `clojure.spec` [doesn't support runtime transformations via conformin
|
||||||
:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||||
{:status 200
|
{:status 200
|
||||||
:body {:total (+ x y)}})}}]]
|
:body {:total (+ x y)}})}}]]
|
||||||
{:data {:middleware [coercion/gen-wrap-coerce-exceptions
|
{:data {:middleware [coercion/coerce-exceptions-middleware
|
||||||
coercion/gen-wrap-coerce-parameters
|
coercion/coerce-request-middleware
|
||||||
coercion/gen-wrap-coerce-response]
|
coercion/coerce-response-middleware]
|
||||||
:coercion spec/coercion}})))
|
:coercion spec/coercion}})))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -200,9 +200,9 @@ To plug in new validation engine, see the
|
||||||
(defprotocol Coercion
|
(defprotocol Coercion
|
||||||
"Pluggable coercion protocol"
|
"Pluggable coercion protocol"
|
||||||
(get-name [this] "Keyword name for the coercion")
|
(get-name [this] "Keyword name for the coercion")
|
||||||
(compile [this model name] "Compiles a coercion model")
|
|
||||||
(get-apidocs [this model data] "???")
|
(get-apidocs [this model data] "???")
|
||||||
(make-open [this model] "Returns a new map model which doesn't fail on extra keys")
|
(compile-model [this model name] "Compiles a coercion model")
|
||||||
|
(open-model [this model] "Returns a new map model which doesn't fail on extra keys")
|
||||||
(encode-error [this error] "Converts error in to a serializable format")
|
(encode-error [this error] "Converts error in to a serializable format")
|
||||||
(request-coercer [this type model] "Returns a `value format => value` request coercion function")
|
(request-coercer [this type model] "Returns a `value format => value` request coercion function")
|
||||||
(response-coercer [this model] "Returns a `value format => value` response coercion function"))
|
(response-coercer [this model] "Returns a `value format => value` response coercion function"))
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ To demonstrate the two approaches, below are response coercion middleware writte
|
||||||
```clj
|
```clj
|
||||||
(require '[reitit.ring.middleware :as middleware])
|
(require '[reitit.ring.middleware :as middleware])
|
||||||
|
|
||||||
(def gen-wrap-coerce-response
|
(def coerce-response-middleware
|
||||||
"Middleware for pluggable response coercion.
|
"Middleware for pluggable response coercion.
|
||||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||||
and :responses from route data, otherwise does not mount."
|
and :responses from route data, otherwise does not mount."
|
||||||
|
|
|
||||||
11
examples/just-coercion-with-ring/.gitignore
vendored
Normal file
11
examples/just-coercion-with-ring/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
/target
|
||||||
|
/classes
|
||||||
|
/checkouts
|
||||||
|
pom.xml
|
||||||
|
pom.xml.asc
|
||||||
|
*.jar
|
||||||
|
*.class
|
||||||
|
/.lein-*
|
||||||
|
/.nrepl-port
|
||||||
|
.hgignore
|
||||||
|
.hg/
|
||||||
39
examples/just-coercion-with-ring/README.md
Normal file
39
examples/just-coercion-with-ring/README.md
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Just Coercion With Ring
|
||||||
|
|
||||||
|
A Sample project showing how to use the reitit coercion with pure ring.
|
||||||
|
|
||||||
|
* `Middleware` are turned into normal ring middleware via `reitit.middleware/chain`
|
||||||
|
* Endpoint parameters are given to middleware as arguments
|
||||||
|
* Coerced parameters are available from `:parameters`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```clj
|
||||||
|
> lein repl
|
||||||
|
|
||||||
|
(require '[example.server :as server])
|
||||||
|
|
||||||
|
;; the manually coerced version
|
||||||
|
(require '[example.naive :as naive])
|
||||||
|
(server/restart naive/app)
|
||||||
|
|
||||||
|
;; schema-coercion
|
||||||
|
(require '[example.schema :as schema])
|
||||||
|
(server/restart schema/app)
|
||||||
|
|
||||||
|
;; spec-coercion
|
||||||
|
(require '[example.spec :as spec])
|
||||||
|
(server/restart spec/app)
|
||||||
|
|
||||||
|
;; data-spec-coercion
|
||||||
|
(require '[example.dspec :as dspec])
|
||||||
|
(server/restart dspec/app)
|
||||||
|
```
|
||||||
|
|
||||||
|
To test the endpoint:
|
||||||
|
|
||||||
|
http://localhost:3000/?x=1&y=20
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Copyright © 2017 Metosin Oy
|
||||||
6
examples/just-coercion-with-ring/project.clj
Normal file
6
examples/just-coercion-with-ring/project.clj
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
(defproject just-coercion-with-ring "0.1.0-SNAPSHOT"
|
||||||
|
:description "Reitit coercion with vanilla ring"
|
||||||
|
:dependencies [[org.clojure/clojure "1.9.0-RC2"]
|
||||||
|
[ring "1.6.3"]
|
||||||
|
[metosin/muuntaja "0.4.1"]
|
||||||
|
[metosin/reitit "0.1.0-SNAPSHOT"]])
|
||||||
15
examples/just-coercion-with-ring/src/example/dspec.clj
Normal file
15
examples/just-coercion-with-ring/src/example/dspec.clj
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
(ns example.dspec
|
||||||
|
(:require [reitit.ring.coercion :as coercion]
|
||||||
|
[reitit.ring.coercion.spec :as spec-coercion]
|
||||||
|
[example.server :as server]))
|
||||||
|
|
||||||
|
(defn handler [{{{:keys [x y]} :query} :parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:result (+ x y)
|
||||||
|
:source :data-spec}})
|
||||||
|
|
||||||
|
(def app
|
||||||
|
(-> #'handler
|
||||||
|
(server/wrap-coercion
|
||||||
|
{:parameters {:query {:x int?, :y int?}}
|
||||||
|
:coercion spec-coercion/coercion})))
|
||||||
27
examples/just-coercion-with-ring/src/example/naive.clj
Normal file
27
examples/just-coercion-with-ring/src/example/naive.clj
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
(ns example.naive
|
||||||
|
(:require [clojure.spec.alpha :as s]
|
||||||
|
[clojure.walk :as walk]
|
||||||
|
[example.server :as server]))
|
||||||
|
|
||||||
|
(s/def ::x int?)
|
||||||
|
(s/def ::y int?)
|
||||||
|
(s/def ::request (s/keys :req-un [::x ::y]))
|
||||||
|
|
||||||
|
(defn ->long [x]
|
||||||
|
(try
|
||||||
|
(Long/parseLong x)
|
||||||
|
(catch Exception _
|
||||||
|
x)))
|
||||||
|
|
||||||
|
(defn app [request]
|
||||||
|
(println (:query-params request))
|
||||||
|
(let [{:keys [x y] :as params} (-> (:query-params request)
|
||||||
|
(walk/keywordize-keys)
|
||||||
|
(update :x ->long)
|
||||||
|
(update :y ->long))]
|
||||||
|
(if (s/valid? ::request params)
|
||||||
|
{:status 200
|
||||||
|
:body {:result (+ x y)
|
||||||
|
:source :naive}}
|
||||||
|
{:status 400
|
||||||
|
:body "invalid input"})))
|
||||||
15
examples/just-coercion-with-ring/src/example/schema.clj
Normal file
15
examples/just-coercion-with-ring/src/example/schema.clj
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
(ns example.schema
|
||||||
|
(:require [reitit.ring.coercion :as coercion]
|
||||||
|
[reitit.ring.coercion.schema :as schema-coercion]
|
||||||
|
[example.server :as server]))
|
||||||
|
|
||||||
|
(defn handler [{{{:keys [x y]} :query} :parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:result (+ x y)
|
||||||
|
:source :schema}})
|
||||||
|
|
||||||
|
(def app
|
||||||
|
(-> #'handler
|
||||||
|
(server/wrap-coercion
|
||||||
|
{:parameters {:query {:x Long, :y Long}}
|
||||||
|
:coercion schema-coercion/coercion})))
|
||||||
28
examples/just-coercion-with-ring/src/example/server.clj
Normal file
28
examples/just-coercion-with-ring/src/example/server.clj
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
(ns example.server
|
||||||
|
(:require [ring.adapter.jetty :as jetty]
|
||||||
|
[reitit.ring.middleware :as middleware]
|
||||||
|
[reitit.ring.coercion :as coercion]))
|
||||||
|
|
||||||
|
(defonce ^:private server (atom nil))
|
||||||
|
|
||||||
|
;; unlift Middleware Record into vanilla Ring middleware
|
||||||
|
;; NOTE: to support format-based body coercion, an options map needs
|
||||||
|
;; to be set with :extract-request-format and extract-response-format
|
||||||
|
(defn wrap-coercion [handler resource]
|
||||||
|
(middleware/chain
|
||||||
|
[coercion/coerce-request-middleware
|
||||||
|
coercion/coerce-response-middleware
|
||||||
|
coercion/coerce-exceptions-middleware]
|
||||||
|
handler
|
||||||
|
resource))
|
||||||
|
|
||||||
|
(defn restart [handler]
|
||||||
|
(let [app (-> handler
|
||||||
|
(ring.middleware.params/wrap-params)
|
||||||
|
(muuntaja.middleware/wrap-format))]
|
||||||
|
(swap! server (fn [x]
|
||||||
|
(when x (.stop x))
|
||||||
|
(jetty/run-jetty
|
||||||
|
handler
|
||||||
|
{:port 3000, :join? false})))
|
||||||
|
(println "server running in port 3000")))
|
||||||
23
examples/just-coercion-with-ring/src/example/spec.clj
Normal file
23
examples/just-coercion-with-ring/src/example/spec.clj
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
(ns example.spec
|
||||||
|
(:require [clojure.spec.alpha :as s]
|
||||||
|
[spec-tools.spec :as spec]
|
||||||
|
[reitit.ring.coercion :as coercion]
|
||||||
|
[reitit.ring.coercion.spec :as spec-coercion]
|
||||||
|
[example.server :as server]))
|
||||||
|
|
||||||
|
;; wrap into Spec Records to enable runtime conforming
|
||||||
|
(s/def ::x spec/int?)
|
||||||
|
(s/def ::y spec/int?)
|
||||||
|
(s/def ::request (s/keys :req-un [::x ::y]))
|
||||||
|
|
||||||
|
;; read coerced parameters under :parameters
|
||||||
|
(defn handler [{{{:keys [x y]} :query} :parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:result (+ x y)
|
||||||
|
:source :spec}})
|
||||||
|
|
||||||
|
(def app
|
||||||
|
(-> #'handler
|
||||||
|
(server/wrap-coercion
|
||||||
|
{:parameters {:query ::request}
|
||||||
|
:coercion spec-coercion/coercion})))
|
||||||
11
examples/ring-example/.gitignore
vendored
Normal file
11
examples/ring-example/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
/target
|
||||||
|
/classes
|
||||||
|
/checkouts
|
||||||
|
pom.xml
|
||||||
|
pom.xml.asc
|
||||||
|
*.jar
|
||||||
|
*.class
|
||||||
|
/.lein-*
|
||||||
|
/.nrepl-port
|
||||||
|
.hgignore
|
||||||
|
.hg/
|
||||||
33
examples/ring-example/README.md
Normal file
33
examples/ring-example/README.md
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Ring example
|
||||||
|
|
||||||
|
A Sample project with ring.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```clj
|
||||||
|
> lein repl
|
||||||
|
|
||||||
|
(require '[example.server :as server])
|
||||||
|
|
||||||
|
(server/restart)
|
||||||
|
```
|
||||||
|
|
||||||
|
To test the endpoints using [httpie](https://httpie.org/):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Schema
|
||||||
|
http GET :3000/schema/plus x==1 y==20
|
||||||
|
http POST :3000/schema/plus x:=1 y:=20
|
||||||
|
|
||||||
|
# Data-specs
|
||||||
|
http GET :3000/dspec/plus x==1 y==20
|
||||||
|
http POST :3000/dspec/plus x:=1 y:=20
|
||||||
|
|
||||||
|
# Specs
|
||||||
|
http GET :3000/spec/plus x==1 y==20
|
||||||
|
http POST :3000/spec/plus x:=1 y:=20
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Copyright © 2017 Metosin Oy
|
||||||
6
examples/ring-example/project.clj
Normal file
6
examples/ring-example/project.clj
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
(defproject just-coercion-with-ring "0.1.0-SNAPSHOT"
|
||||||
|
:description "Reitit coercion with vanilla ring"
|
||||||
|
:dependencies [[org.clojure/clojure "1.9.0-RC2"]
|
||||||
|
[ring "1.6.3"]
|
||||||
|
[metosin/muuntaja "0.4.1"]
|
||||||
|
[metosin/reitit "0.1.0-SNAPSHOT"]])
|
||||||
19
examples/ring-example/src/example/dspec.clj
Normal file
19
examples/ring-example/src/example/dspec.clj
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
(ns example.dspec
|
||||||
|
(:require [reitit.ring.coercion :as coercion]
|
||||||
|
[reitit.ring.coercion.spec :as spec-coercion]))
|
||||||
|
|
||||||
|
(def routes
|
||||||
|
["/dspec"
|
||||||
|
["/plus" {:name ::plus
|
||||||
|
:coercion spec-coercion/coercion
|
||||||
|
:responses {200 {:schema {:total int?}}}
|
||||||
|
:get {:summary "plus with query-params"
|
||||||
|
:parameters {:query {:x int?, :y int?}}
|
||||||
|
:handler (fn [{{{:keys [x y]} :query} :parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:total (+ x y)}})}
|
||||||
|
:post {:summary "plus with body-params"
|
||||||
|
:parameters {:body {:x int?, :y int?}}
|
||||||
|
:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:total (+ x y)}})}}]])
|
||||||
20
examples/ring-example/src/example/schema.clj
Normal file
20
examples/ring-example/src/example/schema.clj
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
(ns example.schema
|
||||||
|
(:require [schema.core :as s]
|
||||||
|
[reitit.ring.coercion :as coercion]
|
||||||
|
[reitit.ring.coercion.schema :as schema-coercion]))
|
||||||
|
|
||||||
|
(def routes
|
||||||
|
["/schema"
|
||||||
|
["/plus" {:name ::plus
|
||||||
|
:coercion schema-coercion/coercion
|
||||||
|
:responses {200 {:schema {:total s/Int}}}
|
||||||
|
:get {:summary "plus with query-params"
|
||||||
|
:parameters {:query {:x s/Int, :y s/Int}}
|
||||||
|
:handler (fn [{{{:keys [x y]} :query} :parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:total (+ x y)}})}
|
||||||
|
:post {:summary "plus with body-params"
|
||||||
|
:parameters {:body {:x s/Int, :y s/Int}}
|
||||||
|
:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:total (+ x y)}})}}]])
|
||||||
33
examples/ring-example/src/example/server.clj
Normal file
33
examples/ring-example/src/example/server.clj
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
(ns example.server
|
||||||
|
(:require [ring.adapter.jetty :as jetty]
|
||||||
|
[ring.middleware.params]
|
||||||
|
[muuntaja.middleware]
|
||||||
|
[reitit.ring :as ring]
|
||||||
|
[reitit.ring.coercion :as coercion]
|
||||||
|
[example.dspec]
|
||||||
|
[example.schema]
|
||||||
|
[example.spec]))
|
||||||
|
|
||||||
|
(defonce ^:private server (atom nil))
|
||||||
|
|
||||||
|
(def app
|
||||||
|
(ring/ring-handler
|
||||||
|
(ring/router
|
||||||
|
[example.schema/routes
|
||||||
|
example.dspec/routes
|
||||||
|
example.spec/routes]
|
||||||
|
{:data {:middleware [ring.middleware.params/wrap-params
|
||||||
|
muuntaja.middleware/wrap-format
|
||||||
|
coercion/coerce-exceptions-middleware
|
||||||
|
coercion/coerce-request-middleware
|
||||||
|
coercion/coerce-response-middleware]}})))
|
||||||
|
|
||||||
|
(defn restart []
|
||||||
|
(swap! server (fn [x]
|
||||||
|
(when x (.stop x))
|
||||||
|
(jetty/run-jetty
|
||||||
|
app
|
||||||
|
{:port 3000, :join? false})))
|
||||||
|
(println "server running in port 3000"))
|
||||||
|
|
||||||
|
(restart)
|
||||||
26
examples/ring-example/src/example/spec.clj
Normal file
26
examples/ring-example/src/example/spec.clj
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
(ns example.spec
|
||||||
|
(:require [clojure.spec.alpha :as s]
|
||||||
|
[spec-tools.spec :as spec]
|
||||||
|
[reitit.ring.coercion :as coercion]
|
||||||
|
[reitit.ring.coercion.spec :as spec-coercion]))
|
||||||
|
|
||||||
|
;; wrap into Spec Records to enable runtime conforming
|
||||||
|
(s/def ::x spec/int?)
|
||||||
|
(s/def ::y spec/int?)
|
||||||
|
(s/def ::total spec/int?)
|
||||||
|
|
||||||
|
(def routes
|
||||||
|
["/spec"
|
||||||
|
["/plus" {:name ::plus
|
||||||
|
:coercion spec-coercion/coercion
|
||||||
|
:responses {200 {:schema (s/keys :req-un [::total])}}
|
||||||
|
:get {:summary "plus with query-params"
|
||||||
|
:parameters {:query (s/keys :req-un [::x ::y])}
|
||||||
|
:handler (fn [{{{:keys [x y]} :query} :parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:total (+ x y)}})}
|
||||||
|
:post {:summary "plus with body-params"
|
||||||
|
:parameters {:body (s/keys :req-un [::x ::y])}
|
||||||
|
:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:total (+ x y)}})}}]])
|
||||||
|
|
@ -15,16 +15,14 @@
|
||||||
|
|
||||||
(defrecord ParameterCoercion [in style keywordize? open?])
|
(defrecord ParameterCoercion [in style keywordize? open?])
|
||||||
|
|
||||||
(def valid-type? #{::request-coercion ::response-coercion})
|
(def ^:no-doc ring-parameter-coercion
|
||||||
|
|
||||||
(def ring-parameter-coercion
|
|
||||||
{:query (->ParameterCoercion :query-params :string true true)
|
{:query (->ParameterCoercion :query-params :string true true)
|
||||||
:body (->ParameterCoercion :body-params :body false false)
|
:body (->ParameterCoercion :body-params :body false false)
|
||||||
:form (->ParameterCoercion :form-params :string true true)
|
:form (->ParameterCoercion :form-params :string true true)
|
||||||
:header (->ParameterCoercion :header-params :string true true)
|
:header (->ParameterCoercion :header-params :string true true)
|
||||||
:path (->ParameterCoercion :path-params :string true true)})
|
:path (->ParameterCoercion :path-params :string true true)})
|
||||||
|
|
||||||
(defn request-coercion-failed! [result coercion value in request]
|
(defn ^:no-doc request-coercion-failed! [result coercion value in request]
|
||||||
(throw
|
(throw
|
||||||
(ex-info
|
(ex-info
|
||||||
(str "Request coercion failed: " (pr-str result))
|
(str "Request coercion failed: " (pr-str result))
|
||||||
|
|
@ -36,7 +34,7 @@
|
||||||
:in [:request in]
|
:in [:request in]
|
||||||
:request request}))))
|
:request request}))))
|
||||||
|
|
||||||
(defn response-coercion-failed! [result coercion value request response]
|
(defn ^:no-doc response-coercion-failed! [result coercion value request response]
|
||||||
(throw
|
(throw
|
||||||
(ex-info
|
(ex-info
|
||||||
(str "Response coercion failed: " (pr-str result))
|
(str "Response coercion failed: " (pr-str result))
|
||||||
|
|
@ -50,26 +48,22 @@
|
||||||
:response response}))))
|
:response response}))))
|
||||||
|
|
||||||
;; TODO: support faster key walking, walk/keywordize-keys is quite slow...
|
;; TODO: support faster key walking, walk/keywordize-keys is quite slow...
|
||||||
|
(defn ^:no-doc request-coercer [coercion type model {:keys [extract-request-format]
|
||||||
(defn request-coercer [coercion type model]
|
:or {extract-request-format (constantly nil)}}]
|
||||||
(if coercion
|
(if coercion
|
||||||
(let [{:keys [keywordize? open? in style]} (ring-parameter-coercion type)
|
(let [{:keys [keywordize? open? in style]} (ring-parameter-coercion type)
|
||||||
transform (comp (if keywordize? walk/keywordize-keys identity) in)
|
transform (comp (if keywordize? walk/keywordize-keys identity) in)
|
||||||
model (if open? (protocol/make-open coercion model) model)
|
model (if open? (protocol/open-model coercion model) model)
|
||||||
coercer (protocol/request-coercer coercion style model)]
|
coercer (protocol/request-coercer coercion style model)]
|
||||||
(fn [request]
|
(fn [request]
|
||||||
(let [value (transform request)
|
(let [value (transform request)
|
||||||
format (some-> request :muuntaja/request :format)
|
format (extract-request-format request)
|
||||||
result (coercer value format)]
|
result (coercer value format)]
|
||||||
(if (protocol/error? result)
|
(if (protocol/error? result)
|
||||||
(request-coercion-failed! result coercion value in request)
|
(request-coercion-failed! result coercion value in request)
|
||||||
result))))))
|
result))))))
|
||||||
|
|
||||||
#_(defn muuntaja-response-format [request response]
|
(defn ^:no-doc response-coercer [coercion model {:keys [extract-response-format]
|
||||||
(or (-> response :muuntaja/content-type)
|
|
||||||
(some-> request :muuntaja/response :format)))
|
|
||||||
|
|
||||||
(defn response-coercer [coercion model {:keys [extract-response-format]
|
|
||||||
:or {extract-response-format (constantly nil)}}]
|
:or {extract-response-format (constantly nil)}}]
|
||||||
(if coercion
|
(if coercion
|
||||||
(let [coercer (protocol/response-coercer coercion model)]
|
(let [coercer (protocol/response-coercer coercion model)]
|
||||||
|
|
@ -81,28 +75,28 @@
|
||||||
(response-coercion-failed! result coercion value request response)
|
(response-coercion-failed! result coercion value request response)
|
||||||
result))))))
|
result))))))
|
||||||
|
|
||||||
(defn encode-error [data]
|
(defn ^:no-doc encode-error [data]
|
||||||
(-> data
|
(-> data
|
||||||
(dissoc :request :response)
|
(dissoc :request :response)
|
||||||
(update :coercion protocol/get-name)
|
(update :coercion protocol/get-name)
|
||||||
(->> (protocol/encode-error (:coercion data)))))
|
(->> (protocol/encode-error (:coercion data)))))
|
||||||
|
|
||||||
(defn- coerce-request [coercers request]
|
(defn ^:no-doc coerce-request [coercers request]
|
||||||
(reduce-kv
|
(reduce-kv
|
||||||
(fn [acc k coercer]
|
(fn [acc k coercer]
|
||||||
(impl/fast-assoc acc k (coercer request)))
|
(impl/fast-assoc acc k (coercer request)))
|
||||||
{}
|
{}
|
||||||
coercers))
|
coercers))
|
||||||
|
|
||||||
(defn- coerce-response [coercers request response]
|
(defn ^:no-doc coerce-response [coercers request response]
|
||||||
(if response
|
(if response
|
||||||
(if-let [coercer (or (coercers (:status response)) (coercers :default))]
|
(if-let [coercer (or (coercers (:status response)) (coercers :default))]
|
||||||
(impl/fast-assoc response :body (coercer request response)))))
|
(impl/fast-assoc response :body (coercer request response)))))
|
||||||
|
|
||||||
(defn ^:no-doc request-coercers [coercion parameters]
|
(defn ^:no-doc request-coercers [coercion parameters opts]
|
||||||
(->> (for [[k v] parameters
|
(->> (for [[k v] parameters
|
||||||
:when v]
|
:when v]
|
||||||
[k (request-coercer coercion k v)])
|
[k (request-coercer coercion k v opts)])
|
||||||
(into {})))
|
(into {})))
|
||||||
|
|
||||||
(defn ^:no-doc response-coercers [coercion responses opts]
|
(defn ^:no-doc response-coercers [coercion responses opts]
|
||||||
|
|
@ -110,7 +104,7 @@
|
||||||
[status (response-coercer coercion schema opts)])
|
[status (response-coercer coercion schema opts)])
|
||||||
(into {})))
|
(into {})))
|
||||||
|
|
||||||
(defn handle-coercion-exception [e respond raise]
|
(defn ^:no-doc handle-coercion-exception [e respond raise]
|
||||||
(let [data (ex-data e)]
|
(let [data (ex-data e)]
|
||||||
(if-let [status (condp = (:type data)
|
(if-let [status (condp = (:type data)
|
||||||
::request-coercion 400
|
::request-coercion 400
|
||||||
|
|
@ -125,15 +119,15 @@
|
||||||
;; middleware
|
;; middleware
|
||||||
;;
|
;;
|
||||||
|
|
||||||
(def gen-wrap-coerce-parameters
|
(def coerce-request-middleware
|
||||||
"Middleware for pluggable request coercion.
|
"Middleware for pluggable request coercion.
|
||||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||||
and :parameters from route data, otherwise does not mount."
|
and :parameters from route data, otherwise does not mount."
|
||||||
(middleware/create
|
(middleware/create
|
||||||
{:name ::coerce-parameters
|
{:name ::coerce-parameters
|
||||||
:gen-wrap (fn [{:keys [coercion parameters]} _]
|
:gen-wrap (fn [{:keys [coercion parameters]} opts]
|
||||||
(if (and coercion parameters)
|
(if (and coercion parameters)
|
||||||
(let [coercers (request-coercers coercion parameters)]
|
(let [coercers (request-coercers coercion parameters opts)]
|
||||||
(fn [handler]
|
(fn [handler]
|
||||||
(fn
|
(fn
|
||||||
([request]
|
([request]
|
||||||
|
|
@ -143,13 +137,13 @@
|
||||||
(let [coerced (coerce-request coercers request)]
|
(let [coerced (coerce-request coercers request)]
|
||||||
(handler (impl/fast-assoc request :parameters coerced) respond raise))))))))}))
|
(handler (impl/fast-assoc request :parameters coerced) respond raise))))))))}))
|
||||||
|
|
||||||
(def gen-wrap-coerce-response
|
(def coerce-response-middleware
|
||||||
"Middleware for pluggable response coercion.
|
"Middleware for pluggable response coercion.
|
||||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||||
and :responses from route data, otherwise does not mount."
|
and :responses from route data, otherwise does not mount."
|
||||||
(middleware/create
|
(middleware/create
|
||||||
{:name ::coerce-response
|
{:name ::coerce-response
|
||||||
:gen-wrap (fn [{:keys [coercion responses opts]} _]
|
:gen-wrap (fn [{:keys [coercion responses]} opts]
|
||||||
(if (and coercion responses)
|
(if (and coercion responses)
|
||||||
(let [coercers (response-coercers coercion responses opts)]
|
(let [coercers (response-coercers coercion responses opts)]
|
||||||
(fn [handler]
|
(fn [handler]
|
||||||
|
|
@ -159,7 +153,7 @@
|
||||||
([request respond raise]
|
([request respond raise]
|
||||||
(handler request #(respond (coerce-response coercers request %)) raise)))))))}))
|
(handler request #(respond (coerce-response coercers request %)) raise)))))))}))
|
||||||
|
|
||||||
(def gen-wrap-coerce-exceptions
|
(def coerce-exceptions-middleware
|
||||||
"Middleware for handling coercion exceptions.
|
"Middleware for handling coercion exceptions.
|
||||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||||
and :parameters or :responses from route data, otherwise does not mount."
|
and :parameters or :responses from route data, otherwise does not mount."
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
(ns reitit.ring.coercion.protocol
|
(ns reitit.ring.coercion.protocol)
|
||||||
(:refer-clojure :exclude [compile]))
|
|
||||||
|
|
||||||
(defprotocol Coercion
|
(defprotocol Coercion
|
||||||
"Pluggable coercion protocol"
|
"Pluggable coercion protocol"
|
||||||
(get-name [this] "Keyword name for the coercion")
|
(get-name [this] "Keyword name for the coercion")
|
||||||
(compile [this model name] "Compiles a coercion model")
|
|
||||||
(get-apidocs [this model data] "???")
|
(get-apidocs [this model data] "???")
|
||||||
(make-open [this model] "Returns a new map model which doesn't fail on extra keys")
|
(compile-model [this model name] "Compiles a model")
|
||||||
|
(open-model [this model] "Returns a new model which allows extra keys in maps")
|
||||||
(encode-error [this error] "Converts error in to a serializable format")
|
(encode-error [this error] "Converts error in to a serializable format")
|
||||||
(request-coercer [this type model] "Returns a `value format => value` request coercion function")
|
(request-coercer [this type model] "Returns a `value format => value` request coercion function")
|
||||||
(response-coercer [this model] "Returns a `value format => value` response coercion function"))
|
(response-coercer [this model] "Returns a `value format => value` response coercion function"))
|
||||||
|
|
|
||||||
|
|
@ -101,3 +101,11 @@
|
||||||
:result
|
:result
|
||||||
:handler))
|
:handler))
|
||||||
{::router router}))
|
{::router router}))
|
||||||
|
|
||||||
|
(defn chain
|
||||||
|
"Creates a vanilla ring middleware chain out of sequence of
|
||||||
|
IntoMiddleware thingies."
|
||||||
|
([middleware handler data]
|
||||||
|
(chain middleware handler data nil))
|
||||||
|
([middleware handler data opts]
|
||||||
|
(compile-handler (expand middleware data opts) handler)))
|
||||||
|
|
|
||||||
|
|
@ -38,15 +38,14 @@
|
||||||
protocol/Coercion
|
protocol/Coercion
|
||||||
(get-name [_] name)
|
(get-name [_] name)
|
||||||
|
|
||||||
(compile [_ model _]
|
|
||||||
model)
|
|
||||||
|
|
||||||
(get-apidocs [_ _ {:keys [parameters responses] :as info}]
|
(get-apidocs [_ _ {:keys [parameters responses] :as info}]
|
||||||
(cond-> (dissoc info :parameters :responses)
|
(cond-> (dissoc info :parameters :responses)
|
||||||
parameters (assoc ::swagger/parameters parameters)
|
parameters (assoc ::swagger/parameters parameters)
|
||||||
responses (assoc ::swagger/responses responses)))
|
responses (assoc ::swagger/responses responses)))
|
||||||
|
|
||||||
(make-open [_ schema] (st/open-schema schema))
|
(compile-model [_ model _] model)
|
||||||
|
|
||||||
|
(open-model [_ schema] (st/open-schema schema))
|
||||||
|
|
||||||
(encode-error [_ error]
|
(encode-error [_ error]
|
||||||
(-> error
|
(-> error
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,6 @@
|
||||||
(into-spec [this _]
|
(into-spec [this _]
|
||||||
(st/create-spec {:spec this})))
|
(st/create-spec {:spec this})))
|
||||||
|
|
||||||
;; TODO: proper name!
|
|
||||||
(def memoized-into-spec
|
|
||||||
(memoize #(into-spec %1 (gensym "spec"))))
|
|
||||||
|
|
||||||
(defn stringify-pred [pred]
|
(defn stringify-pred [pred]
|
||||||
(str (if (seq? pred) (seq pred) pred)))
|
(str (if (seq? pred) (seq pred) pred)))
|
||||||
|
|
||||||
|
|
@ -61,33 +57,33 @@
|
||||||
protocol/Coercion
|
protocol/Coercion
|
||||||
(get-name [_] name)
|
(get-name [_] name)
|
||||||
|
|
||||||
(compile [_ model _]
|
(get-apidocs [this _ {:keys [parameters responses] :as info}]
|
||||||
(memoized-into-spec model))
|
|
||||||
|
|
||||||
(get-apidocs [_ _ {:keys [parameters responses] :as info}]
|
|
||||||
(cond-> (dissoc info :parameters :responses)
|
(cond-> (dissoc info :parameters :responses)
|
||||||
parameters (assoc
|
parameters (assoc
|
||||||
::swagger/parameters
|
::swagger/parameters
|
||||||
(into
|
(into
|
||||||
(empty parameters)
|
(empty parameters)
|
||||||
(for [[k v] parameters]
|
(for [[k v] parameters]
|
||||||
[k memoized-into-spec])))
|
[k (protocol/compile-model this v nil)])))
|
||||||
responses (assoc
|
responses (assoc
|
||||||
::swagger/responses
|
::swagger/responses
|
||||||
(into
|
(into
|
||||||
(empty responses)
|
(empty responses)
|
||||||
(for [[k response] responses]
|
(for [[k response] responses]
|
||||||
[k (update response :schema memoized-into-spec)])))))
|
[k (update response :schema #(protocol/compile-model this % nil))])))))
|
||||||
|
|
||||||
(make-open [_ spec] spec)
|
(compile-model [_ model _]
|
||||||
|
(into-spec model (or name (gensym "spec"))))
|
||||||
|
|
||||||
|
(open-model [_ spec] spec)
|
||||||
|
|
||||||
(encode-error [_ error]
|
(encode-error [_ error]
|
||||||
(-> error
|
(-> error
|
||||||
(update :spec (comp str s/form))
|
(update :spec (comp str s/form))
|
||||||
(update :problems (partial mapv #(update % :pred stringify-pred)))))
|
(update :problems (partial mapv #(update % :pred stringify-pred)))))
|
||||||
|
|
||||||
(request-coercer [_ type spec]
|
(request-coercer [this type spec]
|
||||||
(let [spec (memoized-into-spec spec)
|
(let [spec (protocol/compile-model this spec nil)
|
||||||
{:keys [formats default]} (conforming type)]
|
{:keys [formats default]} (conforming type)]
|
||||||
(fn [value format]
|
(fn [value format]
|
||||||
(if-let [conforming (or (get formats format) default)]
|
(if-let [conforming (or (get formats format) default)]
|
||||||
|
|
|
||||||
|
|
@ -87,9 +87,9 @@
|
||||||
(defrecord NoOpCoercion []
|
(defrecord NoOpCoercion []
|
||||||
protocol/Coercion
|
protocol/Coercion
|
||||||
(get-name [_] :no-op)
|
(get-name [_] :no-op)
|
||||||
(compile [_ model _] model)
|
|
||||||
(get-apidocs [_ _ {:keys [parameters responses] :as info}])
|
(get-apidocs [_ _ {:keys [parameters responses] :as info}])
|
||||||
(make-open [_ spec] spec)
|
(compile-model [_ model _] model)
|
||||||
|
(open-model [_ spec] spec)
|
||||||
(encode-error [_ error] error)
|
(encode-error [_ error] error)
|
||||||
(request-coercer [_ type spec] (fn [value format] value))
|
(request-coercer [_ type spec] (fn [value format] value))
|
||||||
(response-coercer [this spec] (protocol/request-coercer this :response spec)))
|
(response-coercer [this spec] (protocol/request-coercer this :response spec)))
|
||||||
|
|
@ -107,24 +107,24 @@
|
||||||
app (ring/ring-handler
|
app (ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
routes
|
routes
|
||||||
{:data {:middleware [coercion/wrap-coerce-parameters]
|
{:data {:middleware [coercion/coerce-request-middleware]
|
||||||
:coercion coercion}}))
|
:coercion coercion}}))
|
||||||
app2 (ring/ring-handler
|
app2 (ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
routes
|
routes
|
||||||
{:data {:middleware [coercion/gen-wrap-coerce-parameters]
|
{:data {:middleware [coercion/coerce-request-middleware]
|
||||||
:coercion coercion}}))
|
:coercion coercion}}))
|
||||||
app3 (ring/ring-handler
|
app3 (ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
routes
|
routes
|
||||||
{:data {:middleware [coercion/wrap-coerce-parameters
|
{:data {:middleware [coercion/coerce-request-middleware
|
||||||
coercion/wrap-coerce-response]
|
coercion/wrap-coerce-response]
|
||||||
:coercion coercion}}))
|
:coercion coercion}}))
|
||||||
app4 (ring/ring-handler
|
app4 (ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
routes
|
routes
|
||||||
{:data {:middleware [coercion/gen-wrap-coerce-parameters
|
{:data {:middleware [coercion/coerce-request-middleware
|
||||||
coercion/gen-wrap-coerce-response]
|
coercion/coerce-response-middleware]
|
||||||
:coercion coercion}}))
|
:coercion coercion}}))
|
||||||
req {:request-method :get
|
req {:request-method :get
|
||||||
:uri "/api/ping"
|
:uri "/api/ping"
|
||||||
|
|
@ -138,17 +138,17 @@
|
||||||
;; 175ns (-19%)
|
;; 175ns (-19%)
|
||||||
;; 360ns (-64%)
|
;; 360ns (-64%)
|
||||||
;; 4080ns (-30%)
|
;; 4080ns (-30%)
|
||||||
(bench! "gen-wrap-coerce-parameters" (app2 req))
|
(bench! "coerce-request-middleware" (app2 req))
|
||||||
|
|
||||||
;; 300ns
|
;; 300ns
|
||||||
;; 1740ns
|
;; 1740ns
|
||||||
;; 9400ns
|
;; 9400ns
|
||||||
(bench! "wrap-coerce-parameters & responses" (app3 req))
|
(bench! "wrap-coerce-request & responses" (app3 req))
|
||||||
|
|
||||||
;; 175ns (-42%)
|
;; 175ns (-42%)
|
||||||
;; 384ns (-78%)
|
;; 384ns (-78%)
|
||||||
;; 6100ns (-35%)
|
;; 6100ns (-35%)
|
||||||
(bench! "gen-wrap-coerce-parameters & responses" (app4 req)))))
|
(bench! "coerce-request-middleware & responses" (app4 req)))))
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(do
|
(do
|
||||||
|
|
@ -161,8 +161,8 @@
|
||||||
:get {:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
:get {:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||||
{:status 200
|
{:status 200
|
||||||
:body {:total (+ x y)}})}}]]
|
:body {:total (+ x y)}})}}]]
|
||||||
{:data {:middleware [coercion/gen-wrap-coerce-parameters
|
{:data {:middleware [coercion/coerce-request-middleware
|
||||||
coercion/gen-wrap-coerce-response]
|
coercion/coerce-response-middleware]
|
||||||
:coercion spec/coercion}})))
|
:coercion spec/coercion}})))
|
||||||
|
|
||||||
(app
|
(app
|
||||||
|
|
@ -205,8 +205,8 @@
|
||||||
(let [body (-> request :parameters :body)]
|
(let [body (-> request :parameters :body)]
|
||||||
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
||||||
{:data {:middleware [[mm/wrap-format m]
|
{:data {:middleware [[mm/wrap-format m]
|
||||||
coercion/gen-wrap-coerce-parameters
|
coercion/coerce-request-middleware
|
||||||
coercion/gen-wrap-coerce-response]
|
coercion/coerce-response-middleware]
|
||||||
:coercion schema/coercion}}))
|
:coercion schema/coercion}}))
|
||||||
request {:request-method :post
|
request {:request-method :post
|
||||||
:uri "/plus"
|
:uri "/plus"
|
||||||
|
|
@ -228,8 +228,8 @@
|
||||||
:handler (fn [request]
|
:handler (fn [request]
|
||||||
(let [body (-> request :parameters :body)]
|
(let [body (-> request :parameters :body)]
|
||||||
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
||||||
{:data {:middleware [coercion/gen-wrap-coerce-parameters
|
{:data {:middleware [coercion/coerce-request-middleware
|
||||||
coercion/gen-wrap-coerce-response]
|
coercion/coerce-response-middleware]
|
||||||
:coercion schema/coercion}}))
|
:coercion schema/coercion}}))
|
||||||
request {:request-method :post
|
request {:request-method :post
|
||||||
:uri "/plus"
|
:uri "/plus"
|
||||||
|
|
@ -251,8 +251,8 @@
|
||||||
:handler (fn [request]
|
:handler (fn [request]
|
||||||
(let [body (-> request :parameters :body)]
|
(let [body (-> request :parameters :body)]
|
||||||
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
||||||
{:data {:middleware [coercion/gen-wrap-coerce-parameters
|
{:data {:middleware [coercion/coerce-request-middleware
|
||||||
coercion/gen-wrap-coerce-response]
|
coercion/coerce-response-middleware]
|
||||||
:coercion spec/coercion}}))
|
:coercion spec/coercion}}))
|
||||||
request {:request-method :post
|
request {:request-method :post
|
||||||
:uri "/plus"
|
:uri "/plus"
|
||||||
|
|
@ -279,8 +279,8 @@
|
||||||
:handler (fn [request]
|
:handler (fn [request]
|
||||||
(let [body (-> request :parameters :body)]
|
(let [body (-> request :parameters :body)]
|
||||||
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
||||||
{:data {:middleware [coercion/gen-wrap-coerce-parameters
|
{:data {:middleware [coercion/coerce-request-middleware
|
||||||
coercion/gen-wrap-coerce-response]
|
coercion/coerce-response-middleware]
|
||||||
:coercion spec/coercion}}))
|
:coercion spec/coercion}}))
|
||||||
request {:request-method :post
|
request {:request-method :post
|
||||||
:uri "/plus"
|
:uri "/plus"
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@
|
||||||
[expound "0.3.2"]
|
[expound "0.3.2"]
|
||||||
[orchestra "2017.08.13"]
|
[orchestra "2017.08.13"]
|
||||||
|
|
||||||
|
[ring "1.6.3"]
|
||||||
[metosin/muuntaja "0.4.1"]
|
[metosin/muuntaja "0.4.1"]
|
||||||
[metosin/jsonista "0.1.0-SNAPSHOT"]
|
[metosin/jsonista "0.1.0-SNAPSHOT"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,8 @@
|
||||||
:coercion spec/coercion}})))]
|
:coercion spec/coercion}})))]
|
||||||
|
|
||||||
(testing "withut exception handling"
|
(testing "withut exception handling"
|
||||||
(let [app (create [coercion/gen-wrap-coerce-parameters
|
(let [app (create [coercion/coerce-request-middleware
|
||||||
coercion/gen-wrap-coerce-response])]
|
coercion/coerce-response-middleware])]
|
||||||
|
|
||||||
(testing "all good"
|
(testing "all good"
|
||||||
(is (= {:status 200
|
(is (= {:status 200
|
||||||
|
|
@ -74,9 +74,9 @@
|
||||||
(app invalid-request2))))))
|
(app invalid-request2))))))
|
||||||
|
|
||||||
(testing "with exception handling"
|
(testing "with exception handling"
|
||||||
(let [app (create [coercion/gen-wrap-coerce-exceptions
|
(let [app (create [coercion/coerce-exceptions-middleware
|
||||||
coercion/gen-wrap-coerce-parameters
|
coercion/coerce-request-middleware
|
||||||
coercion/gen-wrap-coerce-response])]
|
coercion/coerce-response-middleware])]
|
||||||
|
|
||||||
(testing "all good"
|
(testing "all good"
|
||||||
(is (= {:status 200
|
(is (= {:status 200
|
||||||
|
|
@ -108,8 +108,8 @@
|
||||||
:coercion schema/coercion}})))]
|
:coercion schema/coercion}})))]
|
||||||
|
|
||||||
(testing "withut exception handling"
|
(testing "withut exception handling"
|
||||||
(let [app (create [coercion/gen-wrap-coerce-parameters
|
(let [app (create [coercion/coerce-request-middleware
|
||||||
coercion/gen-wrap-coerce-response])]
|
coercion/coerce-response-middleware])]
|
||||||
|
|
||||||
(testing "all good"
|
(testing "all good"
|
||||||
(is (= {:status 200
|
(is (= {:status 200
|
||||||
|
|
@ -129,9 +129,9 @@
|
||||||
(app invalid-request2))))
|
(app invalid-request2))))
|
||||||
|
|
||||||
(testing "with exception handling"
|
(testing "with exception handling"
|
||||||
(let [app (create [coercion/gen-wrap-coerce-exceptions
|
(let [app (create [coercion/coerce-exceptions-middleware
|
||||||
coercion/gen-wrap-coerce-parameters
|
coercion/coerce-request-middleware
|
||||||
coercion/gen-wrap-coerce-response])]
|
coercion/coerce-response-middleware])]
|
||||||
|
|
||||||
(testing "all good"
|
(testing "all good"
|
||||||
(is (= {:status 200
|
(is (= {:status 200
|
||||||
|
|
|
||||||
|
|
@ -170,3 +170,20 @@
|
||||||
:middleware
|
:middleware
|
||||||
(map :name))))))))))
|
(map :name))))))))))
|
||||||
|
|
||||||
|
(deftest chain-test
|
||||||
|
(testing "chain can produce middlware chain of any IntoMiddleware"
|
||||||
|
(let [mw (fn [handler value]
|
||||||
|
#(conj (handler (conj % value)) value))
|
||||||
|
handler #(conj % :ok)
|
||||||
|
mw1 {:gen-wrap (constantly #(mw % ::mw1))}
|
||||||
|
mw2 {:gen-wrap (constantly nil)}
|
||||||
|
mw3 {:wrap #(mw % ::mw3)}
|
||||||
|
mw4 #(mw % ::mw4)
|
||||||
|
mw5 {:gen-wrap (fn [{:keys [mount?]} _]
|
||||||
|
(when mount?
|
||||||
|
#(mw % ::mw5)))}
|
||||||
|
chain1 (middleware/chain [mw1 mw2 mw3 mw4 mw5] handler {:mount? true})
|
||||||
|
chain2 (middleware/chain [mw1 mw2 mw3 mw4 mw5] handler {:mount? false})]
|
||||||
|
(is (= [::mw1 ::mw3 ::mw4 ::mw5 :ok ::mw5 ::mw4 ::mw3 ::mw1] (chain1 [])))
|
||||||
|
(is (= [::mw1 ::mw3 ::mw4 :ok ::mw4 ::mw3 ::mw1] (chain2 []))))))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue