Merge pull request #76 from metosin/IntoString

Initial take on IntoString
This commit is contained in:
Tommi Reiman 2018-03-22 18:58:06 +02:00 committed by GitHub
commit eae1bc72a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 177 additions and 7 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -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)))

View file

@ -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}))))