Merge pull request #105 from metosin/SmallFixes

Small fixes
This commit is contained in:
Tommi Reiman 2018-06-25 10:35:05 +03:00 committed by GitHub
commit e952afa8fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 142 additions and 66 deletions

View file

@ -1,8 +1,9 @@
## UNRELEASED ## 0.1.3-SNAPSHOT
## `reitit-core` ## `reitit-core`
* `reitit.coercion/coerce!` coerced all parameters found in match, e.g. injecting in `:query-parameters` into `Match` with coerce those too if `:query` coercion is defined. * `reitit.coercion/coerce!` coerced all parameters found in match, e.g. injecting in `:query-parameters` into `Match` with coerce those too if `:query` coercion is defined.
* if response coercion is not defined for a response status, response is still returned
* `spec-tools.data-spec/maybe` can be used in spec-coercion. * `spec-tools.data-spec/maybe` can be used in spec-coercion.
```clj ```clj
@ -37,6 +38,20 @@
; "/olipa/kerran?iso=p%C3%B6ril%C3%A4inen" ; "/olipa/kerran?iso=p%C3%B6ril%C3%A4inen"
``` ```
### `reitit-spec`
* `[metosin/spec-tools "0.7.1"]` with swagger generation enhancements, see the [CHANGELOG](https://github.com/metosin/spec-tools/blob/master/CHANGELOG.md)
* if response coercion is not defined for a response status, no `:schema` is not emitted.
* updated dependencies:
```clj
[metosin/spec-tools "0.7.1"] is available but we use "0.7.0"
```
### `reitit-schema`
* if response coercion is not defined for a response status, no `:schema` is not emitted.
## 0.1.2 (2018-6-6) ## 0.1.2 (2018-6-6)
### `reitit-core` ### `reitit-core`

View file

@ -81,3 +81,21 @@ There is also a exception throwing version:
(r/match-by-name! router ::user) (r/match-by-name! router ::user)
; ExceptionInfo missing path-params for route /api/user/:id: #{:id} ; ExceptionInfo missing path-params for route /api/user/:id: #{:id}
``` ```
To turn a Match into a path, there is `reitit.core/match->path`:
```clj
(-> router
(r/match-by-name ::user {:id 1})
(r/match->path))
; "/api/user/1"
```
It can take an optional map of query-parameters too:
```clj
(-> router
(r/match-by-name ::user {:id 1})
(r/match->path {:iso "möly"}))
; "/api/user/1?iso=m%C3%B6ly"
```

View file

@ -79,4 +79,4 @@ With custom responses:
(app {:request-method :get, :uri "/pong"}) (app {:request-method :get, :uri "/pong"})
; {:status 406, :body "kosh"} ; {:status 406, :body "kosh"}
``` ```

View file

@ -1,8 +1,8 @@
# Reverse routing with Ring # Reverse routing with Ring
Both the `router` and the `match` are injected into Ring Request (as `::r/router` and `::r/match`) by the `reitit.ring/ring-handler` and with that, available to middleware and endpoints. Both the `router` and the `match` are injected into Ring Request (as `::r/router` and `::r/match`) by the `reitit.ring/ring-handler` and with that, available to middleware and endpoints. To convert a `Match` into a path, one can use `r/match->path`, which optionally takes a map of query-parameters too.
Below is an example how to use the `router` to do reverse routing from a ring handler: Below is an example how to do reverse routing from a ring handler:
```clj ```clj
(require '[reitit.core :as r]) (require '[reitit.core :as r])
@ -11,23 +11,28 @@ Below is an example how to use the `router` to do reverse routing from a ring ha
(def app (def app
(ring/ring-handler (ring/ring-handler
(ring/router (ring/router
[["/users" {:get (fn [{:keys [::r/router]}] [["/users"
{:status 200 {:get (fn [{:keys [::r/router]}]
:body (for [i (range 10)] {:status 200
{:uri (:path (r/match-by-name router ::user {:id i}))})})}] :body (for [i (range 10)]
["/users/:id" {:name ::user {:uri (-> router
:get (constantly {:status 200, :body "user..."})}]]))) (r/match-by-name ::user {:id i})
;; with extra query-params
(r/match->path {:iso "möly"}))})})}]
["/users/:id"
{:name ::user
:get (constantly {:status 200, :body "user..."})}]])))
(app {:request-method :get, :uri "/users"}) (app {:request-method :get, :uri "/users"})
;{:status 200, ; {:status 200,
; :body [{:uri "/users/0"} ; :body ({:uri "/users/0?iso=m%C3%B6ly"}
; {:uri "/users/1"} ; {:uri "/users/1?iso=m%C3%B6ly"}
; {:uri "/users/2"} ; {:uri "/users/2?iso=m%C3%B6ly"}
; {:uri "/users/3"} ; {:uri "/users/3?iso=m%C3%B6ly"}
; {:uri "/users/4"} ; {:uri "/users/4?iso=m%C3%B6ly"}
; {:uri "/users/5"} ; {:uri "/users/5?iso=m%C3%B6ly"}
; {:uri "/users/6"} ; {:uri "/users/6?iso=m%C3%B6ly"}
; {:uri "/users/7"} ; {:uri "/users/7?iso=m%C3%B6ly"}
; {:uri "/users/8"} ; {:uri "/users/8?iso=m%C3%B6ly"}
; {:uri "/users/9"}]} ; {:uri "/users/9?iso=m%C3%B6ly"})}
``` ```

View file

@ -3,4 +3,4 @@
:dependencies [[org.clojure/clojure "1.9.0"] :dependencies [[org.clojure/clojure "1.9.0"]
[ring "1.6.3"] [ring "1.6.3"]
[metosin/muuntaja "0.4.1"] [metosin/muuntaja "0.4.1"]
[metosin/reitit "0.1.2"]]) [metosin/reitit "0.1.3-SNAPSHOT"]])

View file

@ -3,4 +3,4 @@
:dependencies [[org.clojure/clojure "1.9.0"] :dependencies [[org.clojure/clojure "1.9.0"]
[ring "1.6.3"] [ring "1.6.3"]
[metosin/muuntaja "0.4.1"] [metosin/muuntaja "0.4.1"]
[metosin/reitit "0.1.2"]]) [metosin/reitit "0.1.3-SNAPSHOT"]])

View file

@ -3,5 +3,5 @@
:dependencies [[org.clojure/clojure "1.9.0"] :dependencies [[org.clojure/clojure "1.9.0"]
[ring "1.6.3"] [ring "1.6.3"]
[metosin/muuntaja "0.5.0"] [metosin/muuntaja "0.5.0"]
[metosin/reitit "0.1.2"]] [metosin/reitit "0.1.3-SNAPSHOT"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-core "0.1.2" (defproject metosin/reitit-core "0.1.3-SNAPSHOT"
:description "Snappy data-driven router for Clojure(Script)" :description "Snappy data-driven router for Clojure(Script)"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

View file

@ -116,7 +116,8 @@
(defn coerce-response [coercers request response] (defn 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))
response)))
(defn request-coercers [coercion parameters opts] (defn request-coercers [coercion parameters opts]
(->> (for [[k v] parameters (->> (for [[k v] parameters

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-ring "0.1.2" (defproject metosin/reitit-ring "0.1.3-SNAPSHOT"
:description "Reitit: Ring routing" :description "Reitit: Ring routing"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-schema "0.1.2" (defproject metosin/reitit-schema "0.1.3-SNAPSHOT"
:description "Reitit: Plumatic Schema coercion" :description "Reitit: Plumatic Schema coercion"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

View file

@ -61,9 +61,12 @@
{::swagger/responses {::swagger/responses
(into (into
(empty responses) (empty responses)
(for [[k response] responses (for [[k response] responses]
:let [response (set/rename-keys response {:body :schema})]] [k (as-> response $
[k (update response :schema #(coercion/-compile-model this % nil))]))}))) (set/rename-keys $ {:body :schema})
(if (:schema $)
(update $ :schema #(coercion/-compile-model this % nil))
$))]))})))
(throw (throw
(ex-info (ex-info
(str "Can't produce Schema apidocs for " spesification) (str "Can't produce Schema apidocs for " spesification)

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-spec "0.1.2" (defproject metosin/reitit-spec "0.1.3-SNAPSHOT"
:description "Reitit: clojure.spec coercion" :description "Reitit: clojure.spec coercion"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

View file

@ -29,7 +29,11 @@
:default-encoder stt/any->any})) :default-encoder stt/any->any}))
(def no-op-transformer (def no-op-transformer
st/no-op-transformer) (reify
st/Transformer
(-name [_] ::no-op)
(-encoder [_ _ _])
(-decoder [_ _ _])))
(defprotocol IntoSpec (defprotocol IntoSpec
(into-spec [this name])) (into-spec [this name]))
@ -59,7 +63,10 @@
#?(:clj Object #?(:clj Object
:cljs default) :cljs default)
(into-spec [this _] (into-spec [this _]
(st/create-spec {:spec this}))) (st/create-spec {:spec this}))
nil
(into-spec [this _]))
(defn stringify-pred [pred] (defn stringify-pred [pred]
(str (if (seq? pred) (seq pred) pred))) (str (if (seq? pred) (seq pred) pred)))
@ -93,9 +100,12 @@
{::swagger/responses {::swagger/responses
(into (into
(empty responses) (empty responses)
(for [[k response] responses (for [[k response] responses]
:let [response (set/rename-keys response {:body :schema})]] [k (as-> response $
[k (update response :schema #(coercion/-compile-model this % nil))]))}))) (set/rename-keys $ {:body :schema})
(if (:schema $)
(update $ :schema #(coercion/-compile-model this % nil))
$))]))})))
(throw (throw
(ex-info (ex-info
(str "Can't produce Spec apidocs for " spesification) (str "Can't produce Spec apidocs for " spesification)

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-swagger-ui "0.1.2" (defproject metosin/reitit-swagger-ui "0.1.3-SNAPSHOT"
:description "Reitit: Swagger-ui support" :description "Reitit: Swagger-ui support"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-swagger "0.1.2" (defproject metosin/reitit-swagger "0.1.3-SNAPSHOT"
:description "Reitit: Swagger-support" :description "Reitit: Swagger-support"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit "0.1.2" (defproject metosin/reitit "0.1.3-SNAPSHOT"
:description "Snappy data-driven router for Clojure(Script)" :description "Snappy data-driven router for Clojure(Script)"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-parent "0.1.2" (defproject metosin/reitit-parent "0.1.3-SNAPSHOT"
:description "Snappy data-driven router for Clojure(Script)" :description "Snappy data-driven router for Clojure(Script)"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"
@ -9,17 +9,17 @@
:source-uri "https://github.com/metosin/reitit/{version}/{filepath}#L{line}" :source-uri "https://github.com/metosin/reitit/{version}/{filepath}#L{line}"
:metadata {:doc/format :markdown}} :metadata {:doc/format :markdown}}
:managed-dependencies [[metosin/reitit "0.1.2"] :managed-dependencies [[metosin/reitit "0.1.3-SNAPSHOT"]
[metosin/reitit-core "0.1.2"] [metosin/reitit-core "0.1.3-SNAPSHOT"]
[metosin/reitit-ring "0.1.2"] [metosin/reitit-ring "0.1.3-SNAPSHOT"]
[metosin/reitit-spec "0.1.2"] [metosin/reitit-spec "0.1.3-SNAPSHOT"]
[metosin/reitit-schema "0.1.2"] [metosin/reitit-schema "0.1.3-SNAPSHOT"]
[metosin/reitit-swagger "0.1.2"] [metosin/reitit-swagger "0.1.3-SNAPSHOT"]
[metosin/reitit-swagger-ui "0.1.2"] [metosin/reitit-swagger-ui "0.1.3-SNAPSHOT"]
[meta-merge "1.0.0"] [meta-merge "1.0.0"]
[ring/ring-core "1.6.3"] [ring/ring-core "1.6.3"]
[metosin/spec-tools "0.7.0"] [metosin/spec-tools "0.7.1-SNAPSHOT"]
[metosin/schema-tools "0.10.3"] [metosin/schema-tools "0.10.3"]
[metosin/ring-swagger-ui "2.2.10"] [metosin/ring-swagger-ui "2.2.10"]
[metosin/jsonista "0.2.1"]] [metosin/jsonista "0.2.1"]]

View file

@ -5,9 +5,8 @@
[reitit.ring.coercion :as rrc] [reitit.ring.coercion :as rrc]
[reitit.coercion.spec :as spec] [reitit.coercion.spec :as spec]
[reitit.coercion.schema :as schema] [reitit.coercion.schema :as schema]
#?@(:clj [ #?@(:clj [[muuntaja.middleware]
[muuntaja.middleware] [jsonista.core :as j]]))
[jsonista.core :as j]]))
#?(:clj #?(:clj
(:import (clojure.lang ExceptionInfo) (:import (clojure.lang ExceptionInfo)
(java.io ByteArrayInputStream)))) (java.io ByteArrayInputStream))))
@ -17,8 +16,11 @@
{:keys [c]} :form {:keys [c]} :form
{:keys [d]} :header {:keys [d]} :header
{:keys [e]} :path} :parameters}] {:keys [e]} :path} :parameters}]
{:status 200 (if (= 666 a)
:body {:total (+ a b c d e)}}) {:status 500
:body {:evil true}}
{:status 200
:body {:total (+ a b c d e)}}))
(def valid-request (def valid-request
{:uri "/api/plus/5" {:uri "/api/plus/5"
@ -51,19 +53,23 @@
:form {:c int?} :form {:c int?}
:header {:d int?} :header {:d int?}
:path {:e int?}} :path {:e int?}}
:responses {200 {:body {:total pos-int?}}} :responses {200 {:body {:total pos-int?}}
500 {:description "fail"}}
:handler handler}}]] :handler handler}}]]
{:data {:middleware middleware {:data {:middleware middleware
:coercion spec/coercion}})))] :coercion spec/coercion}})))]
(testing "withut exception handling" (testing "without exception handling"
(let [app (create [rrc/coerce-request-middleware (let [app (create [rrc/coerce-request-middleware
rrc/coerce-response-middleware])] rrc/coerce-response-middleware])]
(testing "all good" (testing "all good"
(is (= {:status 200 (is (= {:status 200
:body {:total 15}} :body {:total 15}}
(app valid-request)))) (app valid-request)))
(is (= {:status 500
:body {:evil true}}
(app (assoc-in valid-request [:query-params "a"] "666")))))
(testing "invalid request" (testing "invalid request"
(is (thrown-with-msg? (is (thrown-with-msg?
@ -106,7 +112,8 @@
:form {:c s/Int} :form {:c s/Int}
:header {:d s/Int} :header {:d s/Int}
:path {:e s/Int}} :path {:e s/Int}}
:responses {200 {:body {:total (s/constrained s/Int pos? 'positive)}}} :responses {200 {:body {:total (s/constrained s/Int pos? 'positive)}}
500 {:description "fail"}}
:handler handler}}]] :handler handler}}]]
{:data {:middleware middleware {:data {:middleware middleware
:coercion schema/coercion}})))] :coercion schema/coercion}})))]
@ -118,7 +125,10 @@
(testing "all good" (testing "all good"
(is (= {:status 200 (is (= {:status 200
:body {:total 15}} :body {:total 15}}
(app valid-request)))) (app valid-request)))
(is (= {:status 500
:body {:evil true}}
(app (assoc-in valid-request [:query-params "a"] "666")))))
(testing "invalid request" (testing "invalid request"
(is (thrown-with-msg? (is (thrown-with-msg?

View file

@ -89,7 +89,9 @@
:form {:c string?} :form {:c string?}
:header {:d string?} :header {:d string?}
:path {:e string?}} :path {:e string?}}
:responses {200 {:body {:total pos-int?}}} :responses {200 {:body {:total pos-int?}}
400 {:description "fail"}
500 {}}
:handler identity}}]] :handler identity}}]]
{:data {:middleware [rrc/coerce-exceptions-middleware {:data {:middleware [rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware rrc/coerce-request-middleware

View file

@ -23,9 +23,12 @@
{:get {:summary "plus" {:get {:summary "plus"
:parameters {:query {:x int?, :y int?} :parameters {:query {:x int?, :y int?}
:path {:z int?}} :path {:z int?}}
:responses {200 {:body {:total int?}}} :swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body {:total int?}}
500 {:description "fail"}}
:handler (fn [{{{:keys [x y]} :query :handler (fn [{{{:keys [x y]} :query
{:keys [z]} :path} :parameters}] {:keys [z]} :path} :parameters}]
{:status 200, :body {:total (+ x y z)}})}}]] {:status 200, :body {:total (+ x y z)}})}}]]
["/schema" {:coercion schema/coercion} ["/schema" {:coercion schema/coercion}
@ -33,9 +36,12 @@
{:get {:summary "plus" {:get {:summary "plus"
:parameters {:query {:x Int, :y Int} :parameters {:query {:x Int, :y Int}
:path {:z Int}} :path {:z Int}}
:responses {200 {:body {:total Int}}} :swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body {:total Int}}
500 {:description "fail"}}
:handler (fn [{{{:keys [x y]} :query :handler (fn [{{{:keys [x y]} :query
{:keys [z]} :path} :parameters}] {:keys [z]} :path} :parameters}]
{:status 200, :body {:total (+ x y z)}})}}]]] {:status 200, :body {:total (+ x y z)}})}}]]]
{:data {:middleware [swagger/swagger-feature {:data {:middleware [swagger/swagger-feature
@ -87,7 +93,10 @@
:properties {"total" {:format "int32" :properties {"total" {:format "int32"
:type "integer"}} :type "integer"}}
:required ["total"] :required ["total"]
:type "object"}}} :type "object"}}
400 {:schema {:type "string"}
:description "kosh"}
500 {:description "fail"}}
:summary "plus"}} :summary "plus"}}
"/api/spec/plus/{z}" {:get {:parameters [{:description "" "/api/spec/plus/{z}" {:get {:parameters [{:description ""
:format "int64" :format "int64"
@ -111,7 +120,10 @@
:schema {:properties {"total" {:format "int64" :schema {:properties {"total" {:format "int64"
:type "integer"}} :type "integer"}}
:required ["total"] :required ["total"]
:type "object"}}} :type "object"}}
400 {:schema {:type "string"}
:description "kosh"}
500 {:description "fail"}}
:summary "plus"}}}} :summary "plus"}}}}
spec))))) spec)))))