mirror of
https://github.com/metosin/reitit.git
synced 2025-12-25 11:18:25 +00:00
commit
e952afa8fd
21 changed files with 142 additions and 66 deletions
17
CHANGELOG.md
17
CHANGELOG.md
|
|
@ -1,8 +1,9 @@
|
|||
## UNRELEASED
|
||||
## 0.1.3-SNAPSHOT
|
||||
|
||||
## `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.
|
||||
* 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.
|
||||
|
||||
```clj
|
||||
|
|
@ -37,6 +38,20 @@
|
|||
; "/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)
|
||||
|
||||
### `reitit-core`
|
||||
|
|
|
|||
|
|
@ -81,3 +81,21 @@ There is also a exception throwing version:
|
|||
(r/match-by-name! router ::user)
|
||||
; 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"
|
||||
```
|
||||
|
|
|
|||
|
|
@ -79,4 +79,4 @@ With custom responses:
|
|||
|
||||
(app {:request-method :get, :uri "/pong"})
|
||||
; {:status 406, :body "kosh"}
|
||||
```
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# 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
|
||||
(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
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
[["/users" {:get (fn [{:keys [::r/router]}]
|
||||
{:status 200
|
||||
:body (for [i (range 10)]
|
||||
{:uri (:path (r/match-by-name router ::user {:id i}))})})}]
|
||||
["/users/:id" {:name ::user
|
||||
:get (constantly {:status 200, :body "user..."})}]])))
|
||||
[["/users"
|
||||
{:get (fn [{:keys [::r/router]}]
|
||||
{:status 200
|
||||
:body (for [i (range 10)]
|
||||
{:uri (-> router
|
||||
(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"})
|
||||
;{:status 200,
|
||||
; :body [{:uri "/users/0"}
|
||||
; {:uri "/users/1"}
|
||||
; {:uri "/users/2"}
|
||||
; {:uri "/users/3"}
|
||||
; {:uri "/users/4"}
|
||||
; {:uri "/users/5"}
|
||||
; {:uri "/users/6"}
|
||||
; {:uri "/users/7"}
|
||||
; {:uri "/users/8"}
|
||||
; {:uri "/users/9"}]}
|
||||
; {:status 200,
|
||||
; :body ({:uri "/users/0?iso=m%C3%B6ly"}
|
||||
; {:uri "/users/1?iso=m%C3%B6ly"}
|
||||
; {:uri "/users/2?iso=m%C3%B6ly"}
|
||||
; {:uri "/users/3?iso=m%C3%B6ly"}
|
||||
; {:uri "/users/4?iso=m%C3%B6ly"}
|
||||
; {:uri "/users/5?iso=m%C3%B6ly"}
|
||||
; {:uri "/users/6?iso=m%C3%B6ly"}
|
||||
; {:uri "/users/7?iso=m%C3%B6ly"}
|
||||
; {:uri "/users/8?iso=m%C3%B6ly"}
|
||||
; {:uri "/users/9?iso=m%C3%B6ly"})}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
:dependencies [[org.clojure/clojure "1.9.0"]
|
||||
[ring "1.6.3"]
|
||||
[metosin/muuntaja "0.4.1"]
|
||||
[metosin/reitit "0.1.2"]])
|
||||
[metosin/reitit "0.1.3-SNAPSHOT"]])
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
:dependencies [[org.clojure/clojure "1.9.0"]
|
||||
[ring "1.6.3"]
|
||||
[metosin/muuntaja "0.4.1"]
|
||||
[metosin/reitit "0.1.2"]])
|
||||
[metosin/reitit "0.1.3-SNAPSHOT"]])
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
:dependencies [[org.clojure/clojure "1.9.0"]
|
||||
[ring "1.6.3"]
|
||||
[metosin/muuntaja "0.5.0"]
|
||||
[metosin/reitit "0.1.2"]]
|
||||
[metosin/reitit "0.1.3-SNAPSHOT"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -116,7 +116,8 @@
|
|||
(defn coerce-response [coercers request response]
|
||||
(if response
|
||||
(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]
|
||||
(->> (for [[k v] parameters
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-ring "0.1.2"
|
||||
(defproject metosin/reitit-ring "0.1.3-SNAPSHOT"
|
||||
:description "Reitit: Ring routing"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-schema "0.1.2"
|
||||
(defproject metosin/reitit-schema "0.1.3-SNAPSHOT"
|
||||
:description "Reitit: Plumatic Schema coercion"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -61,9 +61,12 @@
|
|||
{::swagger/responses
|
||||
(into
|
||||
(empty responses)
|
||||
(for [[k response] responses
|
||||
:let [response (set/rename-keys response {:body :schema})]]
|
||||
[k (update response :schema #(coercion/-compile-model this % nil))]))})))
|
||||
(for [[k response] responses]
|
||||
[k (as-> response $
|
||||
(set/rename-keys $ {:body :schema})
|
||||
(if (:schema $)
|
||||
(update $ :schema #(coercion/-compile-model this % nil))
|
||||
$))]))})))
|
||||
(throw
|
||||
(ex-info
|
||||
(str "Can't produce Schema apidocs for " spesification)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-spec "0.1.2"
|
||||
(defproject metosin/reitit-spec "0.1.3-SNAPSHOT"
|
||||
:description "Reitit: clojure.spec coercion"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -29,7 +29,11 @@
|
|||
:default-encoder stt/any->any}))
|
||||
|
||||
(def no-op-transformer
|
||||
st/no-op-transformer)
|
||||
(reify
|
||||
st/Transformer
|
||||
(-name [_] ::no-op)
|
||||
(-encoder [_ _ _])
|
||||
(-decoder [_ _ _])))
|
||||
|
||||
(defprotocol IntoSpec
|
||||
(into-spec [this name]))
|
||||
|
|
@ -59,7 +63,10 @@
|
|||
#?(:clj Object
|
||||
:cljs default)
|
||||
(into-spec [this _]
|
||||
(st/create-spec {:spec this})))
|
||||
(st/create-spec {:spec this}))
|
||||
|
||||
nil
|
||||
(into-spec [this _]))
|
||||
|
||||
(defn stringify-pred [pred]
|
||||
(str (if (seq? pred) (seq pred) pred)))
|
||||
|
|
@ -93,9 +100,12 @@
|
|||
{::swagger/responses
|
||||
(into
|
||||
(empty responses)
|
||||
(for [[k response] responses
|
||||
:let [response (set/rename-keys response {:body :schema})]]
|
||||
[k (update response :schema #(coercion/-compile-model this % nil))]))})))
|
||||
(for [[k response] responses]
|
||||
[k (as-> response $
|
||||
(set/rename-keys $ {:body :schema})
|
||||
(if (:schema $)
|
||||
(update $ :schema #(coercion/-compile-model this % nil))
|
||||
$))]))})))
|
||||
(throw
|
||||
(ex-info
|
||||
(str "Can't produce Spec apidocs for " spesification)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-swagger "0.1.2"
|
||||
(defproject metosin/reitit-swagger "0.1.3-SNAPSHOT"
|
||||
:description "Reitit: Swagger-support"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
18
project.clj
18
project.clj
|
|
@ -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)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
@ -9,17 +9,17 @@
|
|||
:source-uri "https://github.com/metosin/reitit/{version}/{filepath}#L{line}"
|
||||
:metadata {:doc/format :markdown}}
|
||||
|
||||
:managed-dependencies [[metosin/reitit "0.1.2"]
|
||||
[metosin/reitit-core "0.1.2"]
|
||||
[metosin/reitit-ring "0.1.2"]
|
||||
[metosin/reitit-spec "0.1.2"]
|
||||
[metosin/reitit-schema "0.1.2"]
|
||||
[metosin/reitit-swagger "0.1.2"]
|
||||
[metosin/reitit-swagger-ui "0.1.2"]
|
||||
:managed-dependencies [[metosin/reitit "0.1.3-SNAPSHOT"]
|
||||
[metosin/reitit-core "0.1.3-SNAPSHOT"]
|
||||
[metosin/reitit-ring "0.1.3-SNAPSHOT"]
|
||||
[metosin/reitit-spec "0.1.3-SNAPSHOT"]
|
||||
[metosin/reitit-schema "0.1.3-SNAPSHOT"]
|
||||
[metosin/reitit-swagger "0.1.3-SNAPSHOT"]
|
||||
[metosin/reitit-swagger-ui "0.1.3-SNAPSHOT"]
|
||||
|
||||
[meta-merge "1.0.0"]
|
||||
[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/ring-swagger-ui "2.2.10"]
|
||||
[metosin/jsonista "0.2.1"]]
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@
|
|||
[reitit.ring.coercion :as rrc]
|
||||
[reitit.coercion.spec :as spec]
|
||||
[reitit.coercion.schema :as schema]
|
||||
#?@(:clj [
|
||||
[muuntaja.middleware]
|
||||
[jsonista.core :as j]]))
|
||||
#?@(:clj [[muuntaja.middleware]
|
||||
[jsonista.core :as j]]))
|
||||
#?(:clj
|
||||
(:import (clojure.lang ExceptionInfo)
|
||||
(java.io ByteArrayInputStream))))
|
||||
|
|
@ -17,8 +16,11 @@
|
|||
{:keys [c]} :form
|
||||
{:keys [d]} :header
|
||||
{:keys [e]} :path} :parameters}]
|
||||
{:status 200
|
||||
:body {:total (+ a b c d e)}})
|
||||
(if (= 666 a)
|
||||
{:status 500
|
||||
:body {:evil true}}
|
||||
{:status 200
|
||||
:body {:total (+ a b c d e)}}))
|
||||
|
||||
(def valid-request
|
||||
{:uri "/api/plus/5"
|
||||
|
|
@ -51,19 +53,23 @@
|
|||
:form {:c int?}
|
||||
:header {:d int?}
|
||||
:path {:e int?}}
|
||||
:responses {200 {:body {:total pos-int?}}}
|
||||
:responses {200 {:body {:total pos-int?}}
|
||||
500 {:description "fail"}}
|
||||
:handler handler}}]]
|
||||
{:data {:middleware middleware
|
||||
:coercion spec/coercion}})))]
|
||||
|
||||
(testing "withut exception handling"
|
||||
(testing "without exception handling"
|
||||
(let [app (create [rrc/coerce-request-middleware
|
||||
rrc/coerce-response-middleware])]
|
||||
|
||||
(testing "all good"
|
||||
(is (= {:status 200
|
||||
: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"
|
||||
(is (thrown-with-msg?
|
||||
|
|
@ -106,7 +112,8 @@
|
|||
:form {:c s/Int}
|
||||
:header {:d 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}}]]
|
||||
{:data {:middleware middleware
|
||||
:coercion schema/coercion}})))]
|
||||
|
|
@ -118,7 +125,10 @@
|
|||
(testing "all good"
|
||||
(is (= {:status 200
|
||||
: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"
|
||||
(is (thrown-with-msg?
|
||||
|
|
|
|||
|
|
@ -89,7 +89,9 @@
|
|||
:form {:c string?}
|
||||
:header {:d string?}
|
||||
:path {:e string?}}
|
||||
:responses {200 {:body {:total pos-int?}}}
|
||||
:responses {200 {:body {:total pos-int?}}
|
||||
400 {:description "fail"}
|
||||
500 {}}
|
||||
:handler identity}}]]
|
||||
{:data {:middleware [rrc/coerce-exceptions-middleware
|
||||
rrc/coerce-request-middleware
|
||||
|
|
|
|||
|
|
@ -23,9 +23,12 @@
|
|||
{:get {:summary "plus"
|
||||
:parameters {:query {:x int?, :y 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
|
||||
{:keys [z]} :path} :parameters}]
|
||||
{:keys [z]} :path} :parameters}]
|
||||
{:status 200, :body {:total (+ x y z)}})}}]]
|
||||
|
||||
["/schema" {:coercion schema/coercion}
|
||||
|
|
@ -33,9 +36,12 @@
|
|||
{:get {:summary "plus"
|
||||
:parameters {:query {:x Int, :y 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
|
||||
{:keys [z]} :path} :parameters}]
|
||||
{:keys [z]} :path} :parameters}]
|
||||
{:status 200, :body {:total (+ x y z)}})}}]]]
|
||||
|
||||
{:data {:middleware [swagger/swagger-feature
|
||||
|
|
@ -87,7 +93,10 @@
|
|||
:properties {"total" {:format "int32"
|
||||
:type "integer"}}
|
||||
:required ["total"]
|
||||
:type "object"}}}
|
||||
:type "object"}}
|
||||
400 {:schema {:type "string"}
|
||||
:description "kosh"}
|
||||
500 {:description "fail"}}
|
||||
:summary "plus"}}
|
||||
"/api/spec/plus/{z}" {:get {:parameters [{:description ""
|
||||
:format "int64"
|
||||
|
|
@ -111,7 +120,10 @@
|
|||
:schema {:properties {"total" {:format "int64"
|
||||
:type "integer"}}
|
||||
:required ["total"]
|
||||
:type "object"}}}
|
||||
:type "object"}}
|
||||
400 {:schema {:type "string"}
|
||||
:description "kosh"}
|
||||
500 {:description "fail"}}
|
||||
:summary "plus"}}}}
|
||||
spec)))))
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue