mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 00:41:12 +00:00
Merge pull request #76 from metosin/IntoString
Initial take on IntoString
This commit is contained in:
commit
eae1bc72a9
6 changed files with 177 additions and 7 deletions
66
CHANGELOG.md
66
CHANGELOG.md
|
|
@ -1,3 +1,69 @@
|
|||
## 0.1.1-SNAPSHOT
|
||||
|
||||
### `reitit-core`
|
||||
|
||||
* `match-by-path` encodes parameters into strings using (internal) `reitit.impl/IntoString` protocol. Handles all of: strings, numbers, keywords, booleans, objects. Fixes [#75](https://github.com/metosin/reitit/issues/75).
|
||||
|
||||
```clj
|
||||
(require '[reitit.core :as r])
|
||||
|
||||
(r/match-by-name
|
||||
(r/router
|
||||
["/coffee/:type" ::coffee])
|
||||
::coffee
|
||||
{:type :luwak})
|
||||
;#Match{:template "/coffee/:type",
|
||||
; :data {:name :user/coffee},
|
||||
; :result nil,
|
||||
; :path-params {:type "luwak"},
|
||||
; :path "/coffee/luwak"}
|
||||
```
|
||||
|
||||
### `reitit-swagger`
|
||||
|
||||
* New module to produce swagger-docs from routing tree, including `Coercion` definitions. Works with both middleware & interceptors.
|
||||
|
||||
```clj
|
||||
(require '[reitit.ring :as ring])
|
||||
(require '[reitit.swagger :as swagger])
|
||||
(require '[reitit.ring.coercion :as rrc])
|
||||
(require '[reitit.coercion.spec :as spec])
|
||||
(require '[reitit.coercion.schema :as schema])
|
||||
|
||||
(require '[schema.core :refer [Int]])
|
||||
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
["/api"
|
||||
{:swagger {:id ::math}}
|
||||
|
||||
["/swagger.json"
|
||||
{:get {:no-doc true
|
||||
:swagger {:info {:title "my-api"}}
|
||||
:handler swagger/swagger-spec-handler}}]
|
||||
|
||||
["/spec" {:coercion spec/coercion}
|
||||
["/plus"
|
||||
{:get {:summary "plus"
|
||||
:parameters {:query {:x int?, :y int?}}
|
||||
:responses {200 {:body {:total int?}}}
|
||||
:handler (fn [{{{:keys [x y]} :query} :parameters}]
|
||||
{:status 200, :body {:total (+ x y)}})}}]]
|
||||
|
||||
["/schema" {:coercion schema/coercion}
|
||||
["/plus"
|
||||
{:get {:summary "plus"
|
||||
:parameters {:query {:x Int, :y Int}}
|
||||
:responses {200 {:body {:total Int}}}
|
||||
:handler (fn [{{{:keys [x y]} :query} :parameters}]
|
||||
{:status 200, :body {:total (+ x y)}})}}]]]
|
||||
|
||||
{:data {:middleware [rrc/coerce-exceptions-middleware
|
||||
rrc/coerce-request-middleware
|
||||
rrc/coerce-response-middleware
|
||||
swagger/swagger-feature]}}))
|
||||
```
|
||||
|
||||
## 0.1.0 (2018-2-19)
|
||||
|
||||
* First release
|
||||
|
|
|
|||
|
|
@ -64,6 +64,17 @@ With provided path-parameters:
|
|||
; :path-params {:id "1"}}
|
||||
```
|
||||
|
||||
Path-parameters are automatically coerced into strings, with the help of (currently internal) Protocol `reitit.impl/IntoString`. It supports strings, numbers, booleans, keywords and objects:
|
||||
|
||||
```clj
|
||||
(r/match-by-name router ::user {:id 1})
|
||||
; #Match{:template "/api/user/:id"
|
||||
; :data {:name :user/user}
|
||||
; :path "/api/user/1"
|
||||
; :result nil
|
||||
; :path-params {:id "1"}}
|
||||
```
|
||||
|
||||
There is also a exception throwing version:
|
||||
|
||||
```clj
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@
|
|||
(match nil)))
|
||||
(match-by-name [_ name path-params]
|
||||
(if-let [match (impl/fast-get lookup name)]
|
||||
(match path-params)))))))
|
||||
(match (impl/path-params path-params))))))))
|
||||
|
||||
(defn lookup-router
|
||||
"Creates a lookup-router from resolved routes and optional
|
||||
|
|
@ -215,7 +215,7 @@
|
|||
(match nil)))
|
||||
(match-by-name [_ name path-params]
|
||||
(if-let [match (impl/fast-get lookup name)]
|
||||
(match path-params)))))))
|
||||
(match (impl/path-params path-params))))))))
|
||||
|
||||
(defn segment-router
|
||||
"Creates a special prefix-tree style segment router from resolved routes and optional
|
||||
|
|
@ -255,7 +255,7 @@
|
|||
(match nil)))
|
||||
(match-by-name [_ name path-params]
|
||||
(if-let [match (impl/fast-get lookup name)]
|
||||
(match path-params)))))))
|
||||
(match (impl/path-params path-params))))))))
|
||||
|
||||
(defn single-static-path-router
|
||||
"Creates a fast router of 1 static route(s) and optional
|
||||
|
|
@ -290,7 +290,7 @@
|
|||
match))
|
||||
(match-by-name [_ name path-params]
|
||||
(if (= n name)
|
||||
(impl/fast-assoc match :path-params path-params)))))))
|
||||
(impl/fast-assoc match :path-params (impl/path-params path-params))))))))
|
||||
|
||||
(defn mixed-router
|
||||
"Creates two routers: [[lookup-router]] or [[single-static-path-router]] for
|
||||
|
|
|
|||
|
|
@ -13,8 +13,10 @@
|
|||
(ns ^:no-doc reitit.impl
|
||||
(:require [clojure.string :as str]
|
||||
[clojure.set :as set])
|
||||
#?(:clj (:import (java.util.regex Pattern)
|
||||
(java.util HashMap Map))))
|
||||
#?(:clj
|
||||
(:import (java.util.regex Pattern)
|
||||
(java.util HashMap Map)
|
||||
(java.net URLEncoder URLDecoder))))
|
||||
|
||||
(defn wild? [s]
|
||||
(contains? #{\: \*} (first (str s))))
|
||||
|
|
@ -135,7 +137,7 @@
|
|||
(defn throw-on-missing-path-params [template required path-params]
|
||||
(when-not (every? #(contains? path-params %) required)
|
||||
(let [defined (-> path-params keys set)
|
||||
missing (clojure.set/difference required defined)]
|
||||
missing (set/difference required defined)]
|
||||
(throw
|
||||
(ex-info
|
||||
(str "missing path-params for route " template " -> " missing)
|
||||
|
|
@ -155,3 +157,52 @@
|
|||
|
||||
(defn strip-nils [m]
|
||||
(->> m (remove (comp nil? second)) (into {})))
|
||||
|
||||
;;
|
||||
;; Path-parameters, see https://github.com/metosin/reitit/issues/75
|
||||
;;
|
||||
|
||||
(defn url-encode [s]
|
||||
(some-> s
|
||||
#?(:clj (URLEncoder/encode "UTF-8")
|
||||
:cljs (js/encodeURIComponent))
|
||||
#?(:clj (.replace "+" "%20"))))
|
||||
|
||||
(defn url-decode [s]
|
||||
(some-> s #?(:clj (URLDecoder/decode "UTF-8")
|
||||
:cljs (js/decodeURIComponent))))
|
||||
|
||||
(defprotocol IntoString
|
||||
(into-string [_]))
|
||||
|
||||
(extend-protocol IntoString
|
||||
#?(:clj String
|
||||
:cljs string)
|
||||
(into-string [this] this)
|
||||
|
||||
#?(:clj clojure.lang.Keyword
|
||||
:cljs cljs.core.Keyword)
|
||||
(into-string [this]
|
||||
(let [ns (namespace this)]
|
||||
(str ns (if ns "/") (name this))))
|
||||
|
||||
#?(:clj Boolean
|
||||
:cljs boolean)
|
||||
(into-string [this] (str this))
|
||||
|
||||
#?(:clj Number
|
||||
:cljs number)
|
||||
(into-string [this] (str this))
|
||||
|
||||
#?(:clj Object
|
||||
:cljs object)
|
||||
(into-string [this] (str this)))
|
||||
|
||||
(defn path-params
|
||||
"shallow transform of the path-param values into strings"
|
||||
[params]
|
||||
(reduce-kv
|
||||
(fn [m k v]
|
||||
(assoc m k (url-encode (into-string v))))
|
||||
{}
|
||||
params))
|
||||
|
|
|
|||
|
|
@ -29,6 +29,12 @@
|
|||
:path "/api/ipa/large"
|
||||
:path-params {:size "large"}})
|
||||
(r/match-by-name router ::beer {:size "large"})))
|
||||
(is (= (r/map->Match
|
||||
{:template "/api/ipa/:size"
|
||||
:data {:name ::beer}
|
||||
:path "/api/ipa/large"
|
||||
:path-params {:size "large"}})
|
||||
(r/match-by-name router ::beer {:size :large})))
|
||||
(is (= nil (r/match-by-name router "ILLEGAL")))
|
||||
(is (= [::beer] (r/route-names router)))
|
||||
|
||||
|
|
|
|||
|
|
@ -10,3 +10,39 @@
|
|||
|
||||
(deftest strip-nils-test
|
||||
(is (= {:a 1, :c false} (impl/strip-nils {:a 1, :b nil, :c false}))))
|
||||
|
||||
(deftest url-encode-and-decode-test
|
||||
(is (= "reitit.impl-test%2Fkikka" (-> ::kikka
|
||||
impl/into-string
|
||||
impl/url-encode)))
|
||||
(is (= ::kikka (-> ::kikka
|
||||
impl/into-string
|
||||
impl/url-encode
|
||||
impl/url-decode
|
||||
keyword))))
|
||||
|
||||
(deftest path-params-test
|
||||
(is (= {:n "1"
|
||||
:n1 "-1"
|
||||
:n2 "1"
|
||||
:n3 "1"
|
||||
:n4 "1"
|
||||
:n5 "1"
|
||||
:d "2.2"
|
||||
:b "true"
|
||||
:s "kikka"
|
||||
:u "c2541900-17a7-4353-9024-db8ac258ba4e"
|
||||
:k "kikka"
|
||||
:qk "reitit.impl-test%2Fkikka"}
|
||||
(impl/path-params {:n 1
|
||||
:n1 -1
|
||||
:n2 (long 1)
|
||||
:n3 (int 1)
|
||||
:n4 (short 1)
|
||||
:n5 (byte 1)
|
||||
:d 2.2
|
||||
:b true
|
||||
:s "kikka"
|
||||
:u #uuid "c2541900-17a7-4353-9024-db8ac258ba4e"
|
||||
:k :kikka
|
||||
:qk ::kikka}))))
|
||||
|
|
|
|||
Loading…
Reference in a new issue