mirror of
https://github.com/metosin/reitit.git
synced 2026-02-16 16:05:15 +00:00
Merge branch 'master' into feature/openapi
This commit is contained in:
commit
0648296315
35 changed files with 596 additions and 272 deletions
23
CHANGELOG.md
23
CHANGELOG.md
|
|
@ -12,6 +12,29 @@ We use [Break Versioning][breakver]. The version numbers follow a `<major>.<mino
|
||||||
|
|
||||||
[breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md
|
[breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md
|
||||||
|
|
||||||
|
## UNRELEASED
|
||||||
|
|
||||||
|
* Remove redundant s/and [#552](https://github.com/metosin/reitit/pull/552)
|
||||||
|
* FIX: redirect-trailing-slash-handler strips query-params [#565](https://github.com/metosin/reitit/issues/565)
|
||||||
|
* **BREAKING**: Drop tests for Clojure 1.9, run tests with 1.10 & 1.11
|
||||||
|
* NEW option `:meta-merge` on a router for custom merge strategy on route data
|
||||||
|
* Swagger: support operationId in generated swagger json [#452](https://github.com/metosin/reitit/pull/452) & [#569](https://github.com/metosin/reitit/pull/569)
|
||||||
|
* Update documentation and link to the startrek project [#578](https://github.com/metosin/reitit/pull/578)
|
||||||
|
* Upgrade jackson for CVE-2022-42003 and CVE-2022-42004 [#577](https://github.com/metosin/reitit/pull/577)
|
||||||
|
* Improved coercion errors perf [#576](https://github.com/metosin/reitit/pull/576)
|
||||||
|
* Add example for Reitit + Pedestal + Malli coercion [#572](https://github.com/metosin/reitit/pull/572)
|
||||||
|
* Handle empty seq as empty string in query-string [#566](https://github.com/metosin/reitit/pull/566)
|
||||||
|
* Polish pedestal chains when printing context diffs [#557](https://github.com/metosin/reitit/pull/557)
|
||||||
|
* Updated dependencies:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
[metosin/ring-swagger-ui "4.15.5"] is available but we use "4.3.0"
|
||||||
|
[metosin/jsonista "0.3.7"] is available but we use "0.3.5"
|
||||||
|
[metosin/malli "0.10.1"] is available but we use "0.8.2"
|
||||||
|
[fipp "0.6.26"] is available but we use "0.6.25"
|
||||||
|
[ring/ring-core "1.9.6"] is available but we use "1.9.5"
|
||||||
|
```
|
||||||
|
|
||||||
## 0.5.18 (2022-04-05)
|
## 0.5.18 (2022-04-05)
|
||||||
|
|
||||||
* FIX [#334](https://github.com/metosin/reitit/pull/334) - Frontend: there is no way to catch the exception if coercion fails (via [#549](https://github.com/metosin/reitit/pull/549))
|
* FIX [#334](https://github.com/metosin/reitit/pull/334) - Frontend: there is no way to catch the exception if coercion fails (via [#549](https://github.com/metosin/reitit/pull/549))
|
||||||
|
|
|
||||||
12
README.md
12
README.md
|
|
@ -153,9 +153,13 @@ All examples are in https://github.com/metosin/reitit/tree/master/examples
|
||||||
|
|
||||||
## External resources
|
## External resources
|
||||||
* Simple web application using Ring/Reitit and Integrant: https://github.com/PrestanceDesign/usermanager-reitit-integrant-example
|
* Simple web application using Ring/Reitit and Integrant: https://github.com/PrestanceDesign/usermanager-reitit-integrant-example
|
||||||
* A simple [ClojureScript](https://clojurescript.org/) frontend and Clojure backend using Reitit, [JUXT Clip](https://github.com/juxt/clip), [next.jdbc](https://github.com/seancorfield/next-jdbc) and other bits and bobs...
|
* A simple Clojure backend using Reitit to serve up a RESTful API: [startrek](https://github.com/dharrigan/startrek). Technologies include:
|
||||||
* [startrek](https://git.sr.ht/~dharrigan/startrek)
|
* [Donut System](https://github.com/donut-party/system)
|
||||||
* [startrek-ui](https://git.sr.ht/~dharrigan/startrek-ui)
|
* [next-jdbc](https://github.com/seancorfield/next-jdbc)
|
||||||
|
* [JUXT Clip](https://github.com/juxt/clip)
|
||||||
|
* [Flyway](https://github.com/flyway/flyway)
|
||||||
|
* [HoneySQL](https://github.com/seancorfield/honeysql)
|
||||||
|
* [Babashka](https://babashka.org)
|
||||||
* https://www.learnreitit.com/
|
* https://www.learnreitit.com/
|
||||||
* Lipas, liikuntapalvelut: https://github.com/lipas-liikuntapaikat/lipas
|
* Lipas, liikuntapalvelut: https://github.com/lipas-liikuntapaikat/lipas
|
||||||
* Implementation of the Todo-Backend API spec, using Clojure, Ring/Reitit and next-jdbc: https://github.com/PrestanceDesign/todo-backend-clojure-reitit
|
* Implementation of the Todo-Backend API spec, using Clojure, Ring/Reitit and next-jdbc: https://github.com/PrestanceDesign/todo-backend-clojure-reitit
|
||||||
|
|
@ -180,6 +184,6 @@ Roadmap is mostly written in [issues](https://github.com/metosin/reitit/issues).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright © 2017-2021 [Metosin Oy](http://www.metosin.fi)
|
Copyright © 2017-2023 [Metosin Oy](http://www.metosin.fi)
|
||||||
|
|
||||||
Distributed under the Eclipse Public License, the same as Clojure.
|
Distributed under the Eclipse Public License, the same as Clojure.
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,18 @@
|
||||||
|
|
||||||
Routers can be configured via options. The following options are available for the `reitit.core/router`:
|
Routers can be configured via options. The following options are available for the `reitit.core/router`:
|
||||||
|
|
||||||
| key | description
|
| key | description
|
||||||
|--------------|-------------
|
|---------------|-------------
|
||||||
| `:path` | Base-path for routes
|
| `:path` | Base-path for routes
|
||||||
| `:routes` | Initial resolved routes (default `[]`)
|
| `:routes` | Initial resolved routes (default `[]`)
|
||||||
| `:data` | Initial route data (default `{}`)
|
| `:data` | Initial route data (default `{}`)
|
||||||
| `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this
|
| `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this
|
||||||
| `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon})
|
| `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon})
|
||||||
| `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`)
|
| `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`)
|
||||||
| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil`
|
| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil`
|
||||||
| `:compile` | Function of `route opts => result` to compile a route handler
|
| `:meta-merge` | Function which follows the signature of `meta-merge.core/meta-merge`, useful for when you want to have more control over the meta merging
|
||||||
| `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects
|
| `:compile` | Function of `route opts => result` to compile a route handler
|
||||||
| `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes
|
| `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects
|
||||||
| `:exception` | Function of `Exception => Exception ` to handle creation time exceptions (default `reitit.exception/exception`)
|
| `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes
|
||||||
| `:router` | Function of `routes opts => router` to override the actual router implementation
|
| `:exception` | Function of `Exception => Exception ` to handle creation time exceptions (default `reitit.exception/exception`)
|
||||||
|
| `:router` | Function of `routes opts => router` to override the actual router implementation
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ The following route data keys contribute to the generated swagger specification:
|
||||||
| :tags | optional set of string or keyword tags for an endpoint api docs
|
| :tags | optional set of string or keyword tags for an endpoint api docs
|
||||||
| :summary | optional short string summary of an endpoint
|
| :summary | optional short string summary of an endpoint
|
||||||
| :description | optional long description of an endpoint. Supports http://spec.commonmark.org/
|
| :description | optional long description of an endpoint. Supports http://spec.commonmark.org/
|
||||||
|
| :operationId | optional string specifying the unique ID of an Operation
|
||||||
|
|
||||||
Coercion keys also contribute to the docs:
|
Coercion keys also contribute to the docs:
|
||||||
|
|
||||||
|
|
|
||||||
11
examples/pedestal-malli-swagger/.gitignore
vendored
Normal file
11
examples/pedestal-malli-swagger/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
/target
|
||||||
|
/classes
|
||||||
|
/checkouts
|
||||||
|
pom.xml
|
||||||
|
pom.xml.asc
|
||||||
|
*.jar
|
||||||
|
*.class
|
||||||
|
/.lein-*
|
||||||
|
/.nrepl-port
|
||||||
|
.hgignore
|
||||||
|
.hg/
|
||||||
9
examples/pedestal-malli-swagger/project.clj
Normal file
9
examples/pedestal-malli-swagger/project.clj
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
(defproject pedestal-malli-swagger-example "0.1.0-SNAPSHOT"
|
||||||
|
:description "Reitit-http with pedestal"
|
||||||
|
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||||
|
[io.pedestal/pedestal.service "0.5.5"]
|
||||||
|
[io.pedestal/pedestal.jetty "0.5.5"]
|
||||||
|
[metosin/reitit-malli "0.5.18"]
|
||||||
|
[metosin/reitit-pedestal "0.5.18"]
|
||||||
|
[metosin/reitit "0.5.18"]]
|
||||||
|
:repl-options {:init-ns server})
|
||||||
164
examples/pedestal-malli-swagger/src/server.clj
Normal file
164
examples/pedestal-malli-swagger/src/server.clj
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
(ns example.server
|
||||||
|
(:require [clojure.java.io :as io]
|
||||||
|
[io.pedestal.http.route]
|
||||||
|
[reitit.interceptor]
|
||||||
|
[reitit.dev.pretty :as pretty]
|
||||||
|
[reitit.coercion.malli]
|
||||||
|
[io.pedestal.http]
|
||||||
|
[reitit.ring]
|
||||||
|
[reitit.ring.malli]
|
||||||
|
[reitit.http]
|
||||||
|
[reitit.pedestal]
|
||||||
|
[reitit.swagger :as swagger]
|
||||||
|
[reitit.swagger-ui :as swagger-ui]
|
||||||
|
[reitit.http.coercion :as coercion]
|
||||||
|
[reitit.http.interceptors.parameters :as parameters]
|
||||||
|
[reitit.http.interceptors.muuntaja :as muuntaja]
|
||||||
|
[reitit.http.interceptors.multipart :as multipart]
|
||||||
|
[muuntaja.core]
|
||||||
|
[malli.util :as mu]))
|
||||||
|
|
||||||
|
(defn reitit-routes
|
||||||
|
[_config]
|
||||||
|
[["/swagger.json" {:get {:no-doc true
|
||||||
|
:swagger {:info {:title "my-api"
|
||||||
|
:description "with [malli](https://github.com/metosin/malli) and reitit-ring"}
|
||||||
|
:tags [{:name "files",
|
||||||
|
:description "file api"}
|
||||||
|
{:name "math",
|
||||||
|
:description "math api"}]}
|
||||||
|
:handler (swagger/create-swagger-handler)}}]
|
||||||
|
["/files" {:swagger {:tags ["files"]}}
|
||||||
|
["/upload"
|
||||||
|
{:post {:summary "upload a file"
|
||||||
|
:parameters {:multipart [:map [:file reitit.ring.malli/temp-file-part]]}
|
||||||
|
:responses {200 {:body [:map
|
||||||
|
[:name string?]
|
||||||
|
[:size int?]]}}
|
||||||
|
:handler (fn [{{{{:keys [filename
|
||||||
|
size]} :file}
|
||||||
|
:multipart}
|
||||||
|
:parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:name filename
|
||||||
|
:size size}})}}]
|
||||||
|
["/download" {:get {:summary "downloads a file"
|
||||||
|
:swagger {:produces ["image/png"]}
|
||||||
|
:handler (fn [_]
|
||||||
|
{:status 200
|
||||||
|
:headers {"Content-Type" "image/png"}
|
||||||
|
:body (-> "reitit.png"
|
||||||
|
(io/resource)
|
||||||
|
(io/input-stream))})}}]]
|
||||||
|
["/math" {:swagger {:tags ["math"]}}
|
||||||
|
["/plus"
|
||||||
|
{:get {:summary "plus with malli query parameters"
|
||||||
|
:parameters {:query [:map
|
||||||
|
[:x
|
||||||
|
{:title "X parameter"
|
||||||
|
:description "Description for X parameter"
|
||||||
|
:json-schema/default 42}
|
||||||
|
int?]
|
||||||
|
[:y int?]]}
|
||||||
|
:responses {200 {:body [:map [:total int?]]}}
|
||||||
|
:handler (fn [{{{:keys [x
|
||||||
|
y]}
|
||||||
|
:query}
|
||||||
|
:parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:total (+ x y)}})}
|
||||||
|
:post {:summary "plus with malli body parameters"
|
||||||
|
:parameters {:body [:map
|
||||||
|
[:x
|
||||||
|
{:title "X parameter"
|
||||||
|
:description "Description for X parameter"
|
||||||
|
:json-schema/default 42}
|
||||||
|
int?]
|
||||||
|
[:y int?]]}
|
||||||
|
:responses {200 {:body [:map [:total int?]]}}
|
||||||
|
:handler (fn [{{{:keys [x
|
||||||
|
y]}
|
||||||
|
:body}
|
||||||
|
:parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:total (+ x y)}})}}]]])
|
||||||
|
|
||||||
|
(defn reitit-ring-routes
|
||||||
|
[_config]
|
||||||
|
[(swagger-ui/create-swagger-ui-handler
|
||||||
|
{:path "/"
|
||||||
|
:config {:validatorUrl nil
|
||||||
|
:operationsSorter "alpha"}})
|
||||||
|
(reitit.ring/create-resource-handler)
|
||||||
|
(reitit.ring/create-default-handler)])
|
||||||
|
|
||||||
|
|
||||||
|
(defn reitit-router-config
|
||||||
|
[_config]
|
||||||
|
{:exception pretty/exception
|
||||||
|
:data {:coercion (reitit.coercion.malli/create
|
||||||
|
{:error-keys #{:coercion
|
||||||
|
:in
|
||||||
|
:schema
|
||||||
|
:value
|
||||||
|
:errors
|
||||||
|
:humanized}
|
||||||
|
:compile mu/closed-schema
|
||||||
|
:strip-extra-keys true
|
||||||
|
:default-values true
|
||||||
|
:options nil})
|
||||||
|
:muuntaja muuntaja.core/instance
|
||||||
|
:interceptors [swagger/swagger-feature
|
||||||
|
(parameters/parameters-interceptor)
|
||||||
|
(muuntaja/format-negotiate-interceptor)
|
||||||
|
(muuntaja/format-response-interceptor)
|
||||||
|
(muuntaja/format-request-interceptor)
|
||||||
|
(coercion/coerce-response-interceptor)
|
||||||
|
(coercion/coerce-request-interceptor)
|
||||||
|
(multipart/multipart-interceptor)]}})
|
||||||
|
|
||||||
|
(def config
|
||||||
|
{:env :dev
|
||||||
|
:io.pedestal.http/routes []
|
||||||
|
:io.pedestal.http/type :jetty
|
||||||
|
:io.pedestal.http/port 3000
|
||||||
|
:io.pedestal.http/join? false
|
||||||
|
:io.pedestal.http/secure-headers {:content-security-policy-settings
|
||||||
|
{:default-src "'self'"
|
||||||
|
:style-src "'self' 'unsafe-inline'"
|
||||||
|
:script-src "'self' 'unsafe-inline'"}}
|
||||||
|
::reitit-routes reitit-routes
|
||||||
|
::reitit-ring-routes reitit-ring-routes
|
||||||
|
::reitit-router-config reitit-router-config})
|
||||||
|
|
||||||
|
(defn reitit-http-router
|
||||||
|
[{::keys [reitit-routes
|
||||||
|
reitit-ring-routes
|
||||||
|
reitit-router-config]
|
||||||
|
:as config}]
|
||||||
|
(reitit.pedestal/routing-interceptor
|
||||||
|
(reitit.http/router
|
||||||
|
(reitit-routes config)
|
||||||
|
(reitit-router-config config))
|
||||||
|
(->> config
|
||||||
|
reitit-ring-routes
|
||||||
|
(apply reitit.ring/routes))))
|
||||||
|
|
||||||
|
(defonce server (atom nil))
|
||||||
|
|
||||||
|
(defn start
|
||||||
|
[server
|
||||||
|
config]
|
||||||
|
(when @server
|
||||||
|
(io.pedestal.http/stop @server)
|
||||||
|
(println "server stopped"))
|
||||||
|
(-> config
|
||||||
|
io.pedestal.http/default-interceptors
|
||||||
|
(reitit.pedestal/replace-last-interceptor (reitit-http-router config))
|
||||||
|
io.pedestal.http/dev-interceptors
|
||||||
|
io.pedestal.http/create-server
|
||||||
|
io.pedestal.http/start
|
||||||
|
(->> (reset! server)))
|
||||||
|
(println "server running in port 3000"))
|
||||||
|
|
||||||
|
#_(start server config)
|
||||||
|
|
@ -41,36 +41,44 @@
|
||||||
:header (->ParameterCoercion :headers :string true true)
|
:header (->ParameterCoercion :headers :string true true)
|
||||||
:path (->ParameterCoercion :path-params :string true true)})
|
:path (->ParameterCoercion :path-params :string true true)})
|
||||||
|
|
||||||
(defn ^:no-doc request-coercion-failed! [result coercion value in request]
|
(defn ^:no-doc request-coercion-failed! [result coercion value in request serialize-failed-result]
|
||||||
(throw
|
(throw
|
||||||
(ex-info
|
(ex-info
|
||||||
(str "Request coercion failed: " (pr-str result))
|
(if serialize-failed-result
|
||||||
(merge
|
(str "Request coercion failed: " (pr-str result))
|
||||||
(into {} result)
|
"Request coercion failed")
|
||||||
{:type ::request-coercion
|
(-> {}
|
||||||
:coercion coercion
|
transient
|
||||||
:value value
|
(as-> $ (reduce conj! $ result))
|
||||||
:in [:request in]
|
(assoc! :type ::request-coercion)
|
||||||
:request request}))))
|
(assoc! :coercion coercion)
|
||||||
|
(assoc! :value value)
|
||||||
|
(assoc! :in [:request in])
|
||||||
|
(assoc! :request request)
|
||||||
|
persistent!))))
|
||||||
|
|
||||||
(defn ^:no-doc response-coercion-failed! [result coercion value request response]
|
(defn ^:no-doc response-coercion-failed! [result coercion value request response serialize-failed-result]
|
||||||
(throw
|
(throw
|
||||||
(ex-info
|
(ex-info
|
||||||
(str "Response coercion failed: " (pr-str result))
|
(if serialize-failed-result
|
||||||
(merge
|
(str "Response coercion failed: " (pr-str result))
|
||||||
(into {} result)
|
"Response coercion failed")
|
||||||
{:type ::response-coercion
|
(-> {}
|
||||||
:coercion coercion
|
transient
|
||||||
:value value
|
(as-> $ (reduce conj! $ result))
|
||||||
:in [:response :body]
|
(assoc! :type ::response-coercion)
|
||||||
:request request
|
(assoc! :coercion coercion)
|
||||||
:response response}))))
|
(assoc! :value value)
|
||||||
|
(assoc! :in [:response :body])
|
||||||
|
(assoc! :request request)
|
||||||
|
(assoc! :response response)
|
||||||
|
persistent!))))
|
||||||
|
|
||||||
(defn extract-request-format-default [request]
|
(defn extract-request-format-default [request]
|
||||||
(-> request :muuntaja/request :format))
|
(-> request :muuntaja/request :format))
|
||||||
|
|
||||||
;; TODO: support faster key walking, walk/keywordize-keys is quite slow...
|
;; TODO: support faster key walking, walk/keywordize-keys is quite slow...
|
||||||
(defn request-coercer [coercion type model {::keys [extract-request-format parameter-coercion]
|
(defn request-coercer [coercion type model {::keys [extract-request-format parameter-coercion serialize-failed-result]
|
||||||
:or {extract-request-format extract-request-format-default
|
:or {extract-request-format extract-request-format-default
|
||||||
parameter-coercion default-parameter-coercion}}]
|
parameter-coercion default-parameter-coercion}}]
|
||||||
(if coercion
|
(if coercion
|
||||||
|
|
@ -83,13 +91,13 @@
|
||||||
format (extract-request-format request)
|
format (extract-request-format request)
|
||||||
result (coercer value format)]
|
result (coercer value format)]
|
||||||
(if (error? result)
|
(if (error? result)
|
||||||
(request-coercion-failed! result coercion value in request)
|
(request-coercion-failed! result coercion value in request serialize-failed-result)
|
||||||
result))))))))
|
result))))))))
|
||||||
|
|
||||||
(defn extract-response-format-default [request _]
|
(defn extract-response-format-default [request _]
|
||||||
(-> request :muuntaja/response :format))
|
(-> request :muuntaja/response :format))
|
||||||
|
|
||||||
(defn response-coercer [coercion body {:keys [extract-response-format]
|
(defn response-coercer [coercion body {:keys [extract-response-format serialize-failed-result]
|
||||||
:or {extract-response-format extract-response-format-default}}]
|
:or {extract-response-format extract-response-format-default}}]
|
||||||
(if coercion
|
(if coercion
|
||||||
(if-let [coercer (-response-coercer coercion body)]
|
(if-let [coercer (-response-coercer coercion body)]
|
||||||
|
|
@ -98,7 +106,7 @@
|
||||||
value (:body response)
|
value (:body response)
|
||||||
result (coercer value format)]
|
result (coercer value format)]
|
||||||
(if (error? result)
|
(if (error? result)
|
||||||
(response-coercion-failed! result coercion value request response)
|
(response-coercion-failed! result coercion value request response serialize-failed-result)
|
||||||
result))))))
|
result))))))
|
||||||
|
|
||||||
(defn encode-error [data]
|
(defn encode-error [data]
|
||||||
|
|
|
||||||
|
|
@ -60,17 +60,20 @@
|
||||||
(defn map-data [f routes]
|
(defn map-data [f routes]
|
||||||
(mapv (fn [[p ds]] [p (f p ds)]) routes))
|
(mapv (fn [[p ds]] [p (f p ds)]) routes))
|
||||||
|
|
||||||
(defn merge-data [p x]
|
(defn meta-merge [left right opts]
|
||||||
|
((or (:meta-merge opts) mm/meta-merge) left right))
|
||||||
|
|
||||||
|
(defn merge-data [opts p x]
|
||||||
(reduce
|
(reduce
|
||||||
(fn [acc [k v]]
|
(fn [acc [k v]]
|
||||||
(try
|
(try
|
||||||
(mm/meta-merge acc {k v})
|
(meta-merge acc {k v} opts)
|
||||||
(catch #?(:clj Exception, :cljs js/Error) e
|
(catch #?(:clj Exception, :cljs js/Error) e
|
||||||
(ex/fail! ::merge-data {:path p, :left acc, :right {k v}, :exception e}))))
|
(ex/fail! ::merge-data {:path p, :left acc, :right {k v}, :exception e}))))
|
||||||
{} x))
|
{} x))
|
||||||
|
|
||||||
(defn resolve-routes [raw-routes {:keys [coerce] :as opts}]
|
(defn resolve-routes [raw-routes {:keys [coerce] :as opts}]
|
||||||
(cond->> (->> (walk raw-routes opts) (map-data merge-data))
|
(cond->> (->> (walk raw-routes opts) (map-data #(merge-data opts %1 %2)))
|
||||||
coerce (into [] (keep #(coerce % opts)))))
|
coerce (into [] (keep #(coerce % opts)))))
|
||||||
|
|
||||||
(defn path-conflicting-routes [routes opts]
|
(defn path-conflicting-routes [routes opts]
|
||||||
|
|
@ -249,6 +252,10 @@
|
||||||
(->> params
|
(->> params
|
||||||
(map (fn [[k v]]
|
(map (fn [[k v]]
|
||||||
(if (or (sequential? v) (set? v))
|
(if (or (sequential? v) (set? v))
|
||||||
(str/join "&" (map query-parameter (repeat k) v))
|
(if (seq v)
|
||||||
|
(str/join "&" (map query-parameter (repeat k) v))
|
||||||
|
;; Empty seq results in single & character in the query string.
|
||||||
|
;; Handle as empty string to behave similarly as when the value is nil.
|
||||||
|
(query-parameter k ""))
|
||||||
(query-parameter k v))))
|
(query-parameter k v))))
|
||||||
(str/join "&")))
|
(str/join "&")))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
(ns reitit.interceptor
|
(ns reitit.interceptor
|
||||||
(:require [clojure.pprint :as pprint]
|
(:require [clojure.pprint :as pprint]
|
||||||
[meta-merge.core :refer [meta-merge]]
|
|
||||||
[reitit.core :as r]
|
[reitit.core :as r]
|
||||||
[reitit.exception :as exception]
|
[reitit.exception :as exception]
|
||||||
[reitit.impl :as impl]))
|
[reitit.impl :as impl]))
|
||||||
|
|
@ -156,13 +155,13 @@
|
||||||
([data]
|
([data]
|
||||||
(router data nil))
|
(router data nil))
|
||||||
([data opts]
|
([data opts]
|
||||||
(let [opts (meta-merge {:compile compile-result} opts)]
|
(let [opts (impl/meta-merge {:compile compile-result} opts opts)]
|
||||||
(r/router data opts))))
|
(r/router data opts))))
|
||||||
|
|
||||||
(defn interceptor-handler [router]
|
(defn interceptor-handler [router]
|
||||||
(with-meta
|
(with-meta
|
||||||
(fn [path]
|
(fn [path]
|
||||||
(some->> (r/match-by-path router path)
|
(some->> (r/match-by-path router path)
|
||||||
:result
|
:result
|
||||||
:interceptors))
|
:interceptors))
|
||||||
{::router router}))
|
{::router router}))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
(ns reitit.middleware
|
(ns reitit.middleware
|
||||||
(:require [clojure.pprint :as pprint]
|
(:require [clojure.pprint :as pprint]
|
||||||
[meta-merge.core :refer [meta-merge]]
|
|
||||||
[reitit.core :as r]
|
[reitit.core :as r]
|
||||||
[reitit.exception :as exception]
|
[reitit.exception :as exception]
|
||||||
[reitit.impl :as impl]))
|
[reitit.impl :as impl]))
|
||||||
|
|
@ -139,14 +138,14 @@
|
||||||
([data]
|
([data]
|
||||||
(router data nil))
|
(router data nil))
|
||||||
([data opts]
|
([data opts]
|
||||||
(let [opts (meta-merge {:compile compile-result} opts)]
|
(let [opts (impl/meta-merge {:compile compile-result} opts opts)]
|
||||||
(r/router data opts))))
|
(r/router data opts))))
|
||||||
|
|
||||||
(defn middleware-handler [router]
|
(defn middleware-handler [router]
|
||||||
(with-meta
|
(with-meta
|
||||||
(fn [path]
|
(fn [path]
|
||||||
(some->> path
|
(some->> path
|
||||||
(r/match-by-path router)
|
(r/match-by-path router)
|
||||||
:result
|
:result
|
||||||
:handler))
|
:handler))
|
||||||
{::router router}))
|
{::router router}))
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
(s/nilable
|
(s/nilable
|
||||||
(s/cat :path ::path
|
(s/cat :path ::path
|
||||||
:arg (s/? ::arg)
|
:arg (s/? ::arg)
|
||||||
:childs (s/* (s/and (s/nilable ::raw-routes))))))
|
:childs (s/* (s/nilable ::raw-routes)))))
|
||||||
|
|
||||||
(s/def ::raw-routes
|
(s/def ::raw-routes
|
||||||
(s/or :route ::raw-route
|
(s/or :route ::raw-route
|
||||||
|
|
|
||||||
|
|
@ -176,10 +176,10 @@
|
||||||
(fn [_ [p n]]
|
(fn [_ [p n]]
|
||||||
(if-let [cp (common-prefix p path)]
|
(if-let [cp (common-prefix p path)]
|
||||||
(if (= cp p)
|
(if (= cp p)
|
||||||
;; insert into child node
|
;; insert into child node
|
||||||
(let [n' (-insert n (conj ps (subs path (count p))) fp params data)]
|
(let [n' (-insert n (conj ps (subs path (count p))) fp params data)]
|
||||||
(reduced (assoc-in node [:children p] n')))
|
(reduced (assoc-in node [:children p] n')))
|
||||||
;; split child node
|
;; split child node
|
||||||
(let [rp (subs p (count cp))
|
(let [rp (subs p (count cp))
|
||||||
rp' (subs path (count cp))
|
rp' (subs path (count cp))
|
||||||
n' (-insert (-node {}) ps fp params data)
|
n' (-insert (-node {}) ps fp params data)
|
||||||
|
|
@ -189,7 +189,7 @@
|
||||||
(dissoc p)
|
(dissoc p)
|
||||||
(assoc cp n'')))))))))
|
(assoc cp n'')))))))))
|
||||||
nil (:children node))
|
nil (:children node))
|
||||||
;; new child node
|
;; new child node
|
||||||
(assoc-in node [:children path] (-insert (-node {}) ps fp params data))))]
|
(assoc-in node [:children path] (-insert (-node {}) ps fp params data))))]
|
||||||
(if-let [child (get-in node' [:children ""])]
|
(if-let [child (get-in node' [:children ""])]
|
||||||
;; optimize by removing empty paths
|
;; optimize by removing empty paths
|
||||||
|
|
@ -385,62 +385,62 @@
|
||||||
;;
|
;;
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(->
|
(->
|
||||||
[["/v2/whoami" 1]
|
[["/v2/whoami" 1]
|
||||||
["/v2/users/:user-id/datasets" 2]
|
["/v2/users/:user-id/datasets" 2]
|
||||||
["/v2/public/projects/:project-id/datasets" 3]
|
["/v2/public/projects/:project-id/datasets" 3]
|
||||||
["/v1/public/topics/:topic" 4]
|
["/v1/public/topics/:topic" 4]
|
||||||
["/v1/users/:user-id/orgs/:org-id" 5]
|
["/v1/users/:user-id/orgs/:org-id" 5]
|
||||||
["/v1/search/topics/:term" 6]
|
["/v1/search/topics/:term" 6]
|
||||||
["/v1/users/:user-id/invitations" 7]
|
["/v1/users/:user-id/invitations" 7]
|
||||||
["/v1/users/:user-id/topics" 9]
|
["/v1/users/:user-id/topics" 9]
|
||||||
["/v1/users/:user-id/bookmarks/followers" 10]
|
["/v1/users/:user-id/bookmarks/followers" 10]
|
||||||
["/v2/datasets/:dataset-id" 11]
|
["/v2/datasets/:dataset-id" 11]
|
||||||
["/v1/orgs/:org-id/usage-stats" 12]
|
["/v1/orgs/:org-id/usage-stats" 12]
|
||||||
["/v1/orgs/:org-id/devices/:client-id" 13]
|
["/v1/orgs/:org-id/devices/:client-id" 13]
|
||||||
["/v1/messages/user/:user-id" 14]
|
["/v1/messages/user/:user-id" 14]
|
||||||
["/v1/users/:user-id/devices" 15]
|
["/v1/users/:user-id/devices" 15]
|
||||||
["/v1/public/users/:user-id" 16]
|
["/v1/public/users/:user-id" 16]
|
||||||
["/v1/orgs/:org-id/errors" 17]
|
["/v1/orgs/:org-id/errors" 17]
|
||||||
["/v1/public/orgs/:org-id" 18]
|
["/v1/public/orgs/:org-id" 18]
|
||||||
["/v1/orgs/:org-id/invitations" 19]
|
["/v1/orgs/:org-id/invitations" 19]
|
||||||
["/v1/users/:user-id/device-errors" 22]
|
["/v1/users/:user-id/device-errors" 22]
|
||||||
["/v2/login" 23]
|
["/v2/login" 23]
|
||||||
["/v1/users/:user-id/usage-stats" 24]
|
["/v1/users/:user-id/usage-stats" 24]
|
||||||
["/v2/users/:user-id/devices" 25]
|
["/v2/users/:user-id/devices" 25]
|
||||||
["/v1/users/:user-id/claim-device/:client-id" 26]
|
["/v1/users/:user-id/claim-device/:client-id" 26]
|
||||||
["/v2/public/projects/:project-id" 27]
|
["/v2/public/projects/:project-id" 27]
|
||||||
["/v2/public/datasets/:dataset-id" 28]
|
["/v2/public/datasets/:dataset-id" 28]
|
||||||
["/v2/users/:user-id/topics/bulk" 29]
|
["/v2/users/:user-id/topics/bulk" 29]
|
||||||
["/v1/messages/device/:client-id" 30]
|
["/v1/messages/device/:client-id" 30]
|
||||||
["/v1/users/:user-id/owned-orgs" 31]
|
["/v1/users/:user-id/owned-orgs" 31]
|
||||||
["/v1/topics/:topic" 32]
|
["/v1/topics/:topic" 32]
|
||||||
["/v1/users/:user-id/bookmark/:topic" 33]
|
["/v1/users/:user-id/bookmark/:topic" 33]
|
||||||
["/v1/orgs/:org-id/members/:user-id" 34]
|
["/v1/orgs/:org-id/members/:user-id" 34]
|
||||||
["/v1/users/:user-id/devices/:client-id" 35]
|
["/v1/users/:user-id/devices/:client-id" 35]
|
||||||
["/v1/users/:user-id" 36]
|
["/v1/users/:user-id" 36]
|
||||||
["/v1/orgs/:org-id/devices" 37]
|
["/v1/orgs/:org-id/devices" 37]
|
||||||
["/v1/orgs/:org-id/members" 38]
|
["/v1/orgs/:org-id/members" 38]
|
||||||
["/v2/orgs/:org-id/topics" 40]
|
["/v2/orgs/:org-id/topics" 40]
|
||||||
["/v1/whoami" 41]
|
["/v1/whoami" 41]
|
||||||
["/v1/orgs/:org-id" 42]
|
["/v1/orgs/:org-id" 42]
|
||||||
["/v1/users/:user-id/api-key" 43]
|
["/v1/users/:user-id/api-key" 43]
|
||||||
["/v2/schemas" 44]
|
["/v2/schemas" 44]
|
||||||
["/v2/users/:user-id/topics" 45]
|
["/v2/users/:user-id/topics" 45]
|
||||||
["/v1/orgs/:org-id/confirm-membership/:token" 46]
|
["/v1/orgs/:org-id/confirm-membership/:token" 46]
|
||||||
["/v2/topics/:topic" 47]
|
["/v2/topics/:topic" 47]
|
||||||
["/v1/messages/topic/:topic" 48]
|
["/v1/messages/topic/:topic" 48]
|
||||||
["/v1/users/:user-id/devices/:client-id/reset-password" 49]
|
["/v1/users/:user-id/devices/:client-id/reset-password" 49]
|
||||||
["/v2/topics" 50]
|
["/v2/topics" 50]
|
||||||
["/v1/login" 51]
|
["/v1/login" 51]
|
||||||
["/v1/users/:user-id/orgs" 52]
|
["/v1/users/:user-id/orgs" 52]
|
||||||
["/v2/public/messages/dataset/:dataset-id" 53]
|
["/v2/public/messages/dataset/:dataset-id" 53]
|
||||||
["/v1/topics" 54]
|
["/v1/topics" 54]
|
||||||
["/v1/orgs" 55]
|
["/v1/orgs" 55]
|
||||||
["/v1/users/:user-id/bookmarks" 56]
|
["/v1/users/:user-id/bookmarks" 56]
|
||||||
["/v1/orgs/:org-id/topics" 57]
|
["/v1/orgs/:org-id/topics" 57]
|
||||||
["/command1 {arg1} {arg2}" ::cmd1]
|
["/command1 {arg1} {arg2}" ::cmd1]
|
||||||
["/command2 {arg1} {arg2} {arg3}" ::cmd2]]
|
["/command2 {arg1} {arg2} {arg3}" ::cmd2]]
|
||||||
(insert)
|
(insert)
|
||||||
(compile)
|
(compile)
|
||||||
(pretty)))
|
(pretty)))
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@
|
||||||
[fipp.engine]
|
[fipp.engine]
|
||||||
[fipp.visit]
|
[fipp.visit]
|
||||||
[reitit.exception :as exception]
|
[reitit.exception :as exception]
|
||||||
[spell-spec.expound] ;; expound
|
[spell-spec.expound])) ;; expound
|
||||||
))
|
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; colors
|
;; colors
|
||||||
|
|
@ -46,17 +45,17 @@
|
||||||
:error 196})
|
:error 196})
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(defn- -color [color & text]
|
(defn- -color [color & text]
|
||||||
(str "\033[38;5;" (colors color color) "m" (apply str text) "\u001B[0m"))
|
(str "\033[38;5;" (colors color color) "m" (apply str text) "\u001B[0m"))
|
||||||
|
|
||||||
(doseq [c (range 0 255)]
|
(doseq [c (range 0 255)]
|
||||||
(println (-color c "kikka") "->" c))
|
(println (-color c "kikka") "->" c))
|
||||||
|
|
||||||
(doseq [[n c] colors]
|
(doseq [[n c] colors]
|
||||||
(println (-color c "kikka") "->" c n))
|
(println (-color c "kikka") "->" c n))
|
||||||
|
|
||||||
(doseq [[k v] expound.ansi/sgr-code]
|
(doseq [[k v] expound.ansi/sgr-code]
|
||||||
(println (expound.ansi/sgr "kikka" k) "->" k)))
|
(println (expound.ansi/sgr "kikka" k) "->" k)))
|
||||||
|
|
||||||
(defn- -start [x] (str "\033[38;5;" x "m"))
|
(defn- -start [x] (str "\033[38;5;" x "m"))
|
||||||
(defn- -end [] "\u001B[0m")
|
(defn- -end [] "\u001B[0m")
|
||||||
|
|
@ -220,10 +219,10 @@
|
||||||
(defn exception [e]
|
(defn exception [e]
|
||||||
(let [data (-> e ex-data :data)
|
(let [data (-> e ex-data :data)
|
||||||
message (format-exception (-> e ex-data :type) #?(:clj (.getMessage ^Exception e) :cljs (ex-message e)) data)
|
message (format-exception (-> e ex-data :type) #?(:clj (.getMessage ^Exception e) :cljs (ex-message e)) data)
|
||||||
source #?(:clj (->> e Throwable->map :trace
|
source #?(:clj (->> e Throwable->map :trace
|
||||||
(drop-while #(not= (name (first %)) "reitit.core$router"))
|
(drop-while #(not= (name (first %)) "reitit.core$router"))
|
||||||
(drop-while #(= (name (first %)) "reitit.core$router"))
|
(drop-while #(= (name (first %)) "reitit.core$router"))
|
||||||
next first source-str)
|
next first source-str)
|
||||||
:cljs "unknown")]
|
:cljs "unknown")]
|
||||||
(ex-info (exception-str message source (printer)) (assoc (or data {}) ::exception/cause e))))
|
(ex-info (exception-str message source (printer)) (assoc (or data {}) ::exception/cause e))))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
(ns reitit.http
|
(ns reitit.http
|
||||||
(:require [meta-merge.core :refer [meta-merge]]
|
(:require [reitit.core :as r]
|
||||||
[reitit.core :as r]
|
|
||||||
[reitit.exception :as ex]
|
[reitit.exception :as ex]
|
||||||
|
[reitit.impl :as impl]
|
||||||
[reitit.interceptor :as interceptor]
|
[reitit.interceptor :as interceptor]
|
||||||
[reitit.ring :as ring]))
|
[reitit.ring :as ring]))
|
||||||
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
(->methods true top)
|
(->methods true top)
|
||||||
(reduce-kv
|
(reduce-kv
|
||||||
(fn [acc method data]
|
(fn [acc method data]
|
||||||
(let [data (meta-merge top data)]
|
(let [data (impl/meta-merge top data opts)]
|
||||||
(assoc acc method (->endpoint path data method method))))
|
(assoc acc method (->endpoint path data method method))))
|
||||||
(->methods (:handler top) data)
|
(->methods (:handler top) data)
|
||||||
childs))))
|
childs))))
|
||||||
|
|
@ -138,35 +138,35 @@
|
||||||
enrich-request (ring/create-enrich-request inject-match? inject-router?)
|
enrich-request (ring/create-enrich-request inject-match? inject-router?)
|
||||||
enrich-default-request (ring/create-enrich-default-request inject-router?)]
|
enrich-default-request (ring/create-enrich-default-request inject-router?)]
|
||||||
(with-meta
|
(with-meta
|
||||||
(fn
|
(fn
|
||||||
([request]
|
([request]
|
||||||
(if-let [match (r/match-by-path router (:uri request))]
|
(if-let [match (r/match-by-path router (:uri request))]
|
||||||
(let [method (:request-method request)
|
(let [method (:request-method request)
|
||||||
path-params (:path-params match)
|
path-params (:path-params match)
|
||||||
endpoint (-> match :result method)
|
endpoint (-> match :result method)
|
||||||
interceptors (or (:queue endpoint) (:interceptors endpoint))
|
interceptors (or (:queue endpoint) (:interceptors endpoint))
|
||||||
request (enrich-request request path-params match router)]
|
request (enrich-request request path-params match router)]
|
||||||
(or (interceptor/execute executor interceptors request)
|
(or (interceptor/execute executor interceptors request)
|
||||||
(interceptor/execute executor default-queue request)))
|
(interceptor/execute executor default-queue request)))
|
||||||
(interceptor/execute executor default-queue (enrich-default-request request router))))
|
(interceptor/execute executor default-queue (enrich-default-request request router))))
|
||||||
([request respond raise]
|
([request respond raise]
|
||||||
(let [default #(interceptor/execute executor default-queue % respond raise)]
|
(let [default #(interceptor/execute executor default-queue % respond raise)]
|
||||||
(if-let [match (r/match-by-path router (:uri request))]
|
(if-let [match (r/match-by-path router (:uri request))]
|
||||||
(let [method (:request-method request)
|
(let [method (:request-method request)
|
||||||
path-params (:path-params match)
|
path-params (:path-params match)
|
||||||
endpoint (-> match :result method)
|
endpoint (-> match :result method)
|
||||||
interceptors (or (:queue endpoint) (:interceptors endpoint))
|
interceptors (or (:queue endpoint) (:interceptors endpoint))
|
||||||
request (enrich-request request path-params match router)
|
request (enrich-request request path-params match router)
|
||||||
respond' (fn [response]
|
respond' (fn [response]
|
||||||
(if response
|
(if response
|
||||||
(respond response)
|
(respond response)
|
||||||
(default request)))]
|
(default request)))]
|
||||||
(if interceptors
|
(if interceptors
|
||||||
(interceptor/execute executor interceptors request respond' raise)
|
(interceptor/execute executor interceptors request respond' raise)
|
||||||
(default request)))
|
(default request)))
|
||||||
(default (enrich-default-request request router))))
|
(default (enrich-default-request request router))))
|
||||||
nil))
|
nil))
|
||||||
{::r/router router}))))
|
{::r/router router}))))
|
||||||
|
|
||||||
(defn get-router [handler]
|
(defn get-router [handler]
|
||||||
(-> handler meta ::r/router))
|
(-> handler meta ::r/router))
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,9 @@
|
||||||
|
|
||||||
(defn- polish [ctx]
|
(defn- polish [ctx]
|
||||||
(-> ctx
|
(-> ctx
|
||||||
(dissoc ::original ::previous :stack :queue)
|
(dissoc ::original ::previous :stack :queue
|
||||||
|
:io.pedestal.interceptor.chain/stack
|
||||||
|
:io.pedestal.interceptor.chain/queue)
|
||||||
(update :request dissoc ::r/match ::r/router)))
|
(update :request dissoc ::r/match ::r/router)))
|
||||||
|
|
||||||
(defn- handle [name stage]
|
(defn- handle [name stage]
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@
|
||||||
:response {:default default-transformer-provider
|
:response {:default default-transformer-provider
|
||||||
:formats {"application/json" json-transformer-provider}}}
|
:formats {"application/json" json-transformer-provider}}}
|
||||||
;; set of keys to include in error messages
|
;; set of keys to include in error messages
|
||||||
:error-keys #{:type :coercion :in :schema :value :errors :humanized #_:transformed}
|
:error-keys #{:type :coercion :in #_:schema :value #_:errors :humanized #_:transformed}
|
||||||
;; support lite syntax?
|
;; support lite syntax?
|
||||||
:lite true
|
:lite true
|
||||||
;; schema identity function (default: close all map schemas)
|
;; schema identity function (default: close all map schemas)
|
||||||
|
|
@ -211,7 +211,7 @@
|
||||||
show? (fn [key] (contains? error-keys key))
|
show? (fn [key] (contains? error-keys key))
|
||||||
transformers (walk/prewalk #(if (satisfies? TransformationProvider %) (-transformer % opts) %) transformers)
|
transformers (walk/prewalk #(if (satisfies? TransformationProvider %) (-transformer % opts) %) transformers)
|
||||||
compile (if lite (fn [schema options] (compile (binding [l/*options* options] (l/schema schema)) options))
|
compile (if lite (fn [schema options] (compile (binding [l/*options* options] (l/schema schema)) options))
|
||||||
compile)]
|
compile)]
|
||||||
^{:type ::coercion/coercion}
|
^{:type ::coercion/coercion}
|
||||||
(reify coercion/Coercion
|
(reify coercion/Coercion
|
||||||
(-get-name [_] :malli)
|
(-get-name [_] :malli)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
(ns reitit.ring
|
(ns reitit.ring
|
||||||
(:require [clojure.string :as str]
|
(:require [clojure.string :as str]
|
||||||
[meta-merge.core :refer [meta-merge]]
|
|
||||||
#?@(:clj [[ring.util.mime-type :as mime-type]
|
#?@(:clj [[ring.util.mime-type :as mime-type]
|
||||||
[ring.util.response :as response]])
|
[ring.util.response :as response]])
|
||||||
[reitit.core :as r]
|
[reitit.core :as r]
|
||||||
[reitit.exception :as ex]
|
[reitit.exception :as ex]
|
||||||
[reitit.impl :as impl]
|
[reitit.impl :as impl]
|
||||||
|
|
@ -50,21 +49,21 @@
|
||||||
(->methods true top)
|
(->methods true top)
|
||||||
(reduce-kv
|
(reduce-kv
|
||||||
(fn [acc method data]
|
(fn [acc method data]
|
||||||
(let [data (meta-merge top data)]
|
(let [data (impl/meta-merge top data opts)]
|
||||||
(assoc acc method (->endpoint path data method method))))
|
(assoc acc method (->endpoint path data method method))))
|
||||||
(->methods (:handler top) data)
|
(->methods (:handler top) data)
|
||||||
childs))))
|
childs))))
|
||||||
|
|
||||||
(def default-options-handler
|
(def default-options-handler
|
||||||
(let [handle (fn [request]
|
(let [handler (fn [request]
|
||||||
(let [methods (->> request get-match :result (keep (fn [[k v]] (if v k))))
|
(let [methods (->> request get-match :result (keep (fn [[k v]] (if v k))))
|
||||||
allow (->> methods (map (comp str/upper-case name)) (str/join ","))]
|
allow (->> methods (map (comp str/upper-case name)) (str/join ","))]
|
||||||
{:status 200, :body "", :headers {"Allow" allow}}))]
|
{:status 200, :body "", :headers {"Allow" allow}}))]
|
||||||
(fn
|
(fn
|
||||||
([request]
|
([request]
|
||||||
(handle request))
|
(handler request))
|
||||||
([request respond _]
|
([request respond _]
|
||||||
(respond (handle request))))))
|
(respond (handler request))))))
|
||||||
|
|
||||||
(def default-options-endpoint
|
(def default-options-endpoint
|
||||||
{:no-doc true
|
{:no-doc true
|
||||||
|
|
@ -133,10 +132,10 @@
|
||||||
"
|
"
|
||||||
([] (redirect-trailing-slash-handler {:method :both}))
|
([] (redirect-trailing-slash-handler {:method :both}))
|
||||||
([{:keys [method]}]
|
([{:keys [method]}]
|
||||||
(letfn [(maybe-redirect [request path]
|
(letfn [(maybe-redirect [{:keys [query-string] :as request} path]
|
||||||
(if (and (seq path) (r/match-by-path (::r/router request) path))
|
(if (and (seq path) (r/match-by-path (::r/router request) path))
|
||||||
{:status (if (= (:request-method request) :get) 301 308)
|
{:status (if (= (:request-method request) :get) 301 308)
|
||||||
:headers {"Location" path}
|
:headers {"Location" (if query-string (str path "?" query-string) path)}
|
||||||
:body ""}))
|
:body ""}))
|
||||||
(redirect-handler [request]
|
(redirect-handler [request]
|
||||||
(let [uri (:uri request)]
|
(let [uri (:uri request)]
|
||||||
|
|
@ -317,28 +316,28 @@
|
||||||
enrich-request (create-enrich-request inject-match? inject-router?)
|
enrich-request (create-enrich-request inject-match? inject-router?)
|
||||||
enrich-default-request (create-enrich-default-request inject-router?)]
|
enrich-default-request (create-enrich-default-request inject-router?)]
|
||||||
(with-meta
|
(with-meta
|
||||||
(wrap
|
(wrap
|
||||||
(fn
|
(fn
|
||||||
([request]
|
([request]
|
||||||
(if-let [match (r/match-by-path router (:uri request))]
|
(if-let [match (r/match-by-path router (:uri request))]
|
||||||
(let [method (:request-method request)
|
(let [method (:request-method request)
|
||||||
path-params (:path-params match)
|
path-params (:path-params match)
|
||||||
result (:result match)
|
result (:result match)
|
||||||
handler (-> result method :handler (or default-handler))
|
handler (-> result method :handler (or default-handler))
|
||||||
request (enrich-request request path-params match router)]
|
request (enrich-request request path-params match router)]
|
||||||
(or (handler request) (default-handler request)))
|
(or (handler request) (default-handler request)))
|
||||||
(default-handler (enrich-default-request request router))))
|
(default-handler (enrich-default-request request router))))
|
||||||
([request respond raise]
|
([request respond raise]
|
||||||
(if-let [match (r/match-by-path router (:uri request))]
|
(if-let [match (r/match-by-path router (:uri request))]
|
||||||
(let [method (:request-method request)
|
(let [method (:request-method request)
|
||||||
path-params (:path-params match)
|
path-params (:path-params match)
|
||||||
result (:result match)
|
result (:result match)
|
||||||
handler (-> result method :handler (or default-handler))
|
handler (-> result method :handler (or default-handler))
|
||||||
request (enrich-request request path-params match router)]
|
request (enrich-request request path-params match router)]
|
||||||
((routes handler default-handler) request respond raise))
|
((routes handler default-handler) request respond raise))
|
||||||
(default-handler (enrich-default-request request router) respond raise))
|
(default-handler (enrich-default-request request router) respond raise))
|
||||||
nil)))
|
nil)))
|
||||||
{::r/router router}))))
|
{::r/router router}))))
|
||||||
|
|
||||||
(defn get-router [handler]
|
(defn get-router [handler]
|
||||||
(-> handler meta ::r/router))
|
(-> handler meta ::r/router))
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@
|
||||||
(-get-name [_] :schema)
|
(-get-name [_] :schema)
|
||||||
(-get-options [_] opts)
|
(-get-options [_] opts)
|
||||||
(-get-apidocs [this specification {:keys [parameters responses]}]
|
(-get-apidocs [this specification {:keys [parameters responses]}]
|
||||||
;; TODO: this looks identical to spec, refactor when schema is done.
|
;; TODO: this looks identical to spec, refactor when schema is done.
|
||||||
(case specification
|
(case specification
|
||||||
:swagger (swagger/swagger-spec
|
:swagger (swagger/swagger-spec
|
||||||
(merge
|
(merge
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,13 @@
|
||||||
(s/def ::id (s/or :keyword keyword? :set (s/coll-of keyword? :into #{})))
|
(s/def ::id (s/or :keyword keyword? :set (s/coll-of keyword? :into #{})))
|
||||||
(s/def ::no-doc boolean?)
|
(s/def ::no-doc boolean?)
|
||||||
(s/def ::tags (s/coll-of (s/or :keyword keyword? :string string?) :kind #{}))
|
(s/def ::tags (s/coll-of (s/or :keyword keyword? :string string?) :kind #{}))
|
||||||
|
(s/def ::operationId string?)
|
||||||
(s/def ::summary string?)
|
(s/def ::summary string?)
|
||||||
(s/def ::description string?)
|
(s/def ::description string?)
|
||||||
|
(s/def ::operationId string?)
|
||||||
|
|
||||||
(s/def ::swagger (s/keys :opt-un [::id]))
|
(s/def ::swagger (s/keys :opt-un [::id]))
|
||||||
(s/def ::spec (s/keys :opt-un [::swagger ::no-doc ::tags ::summary ::description]))
|
(s/def ::spec (s/keys :opt-un [::swagger ::no-doc ::tags ::summary ::description ::operationId]))
|
||||||
|
|
||||||
(def swagger-feature
|
(def swagger-feature
|
||||||
"Feature for handling swagger-documentation for routes.
|
"Feature for handling swagger-documentation for routes.
|
||||||
|
|
@ -52,6 +54,7 @@
|
||||||
|
|
||||||
[\"/plus\"
|
[\"/plus\"
|
||||||
{:get {:swagger {:tags \"math\"}
|
{:get {:swagger {:tags \"math\"}
|
||||||
|
:operationId \"addTwoNumbers\"
|
||||||
:summary \"adds numbers together\"
|
:summary \"adds numbers together\"
|
||||||
:description \"takes `x` and `y` query-params and adds them together\"
|
:description \"takes `x` and `y` query-params and adds them together\"
|
||||||
:parameters {:query {:x int?, :y int?}}
|
:parameters {:query {:x int?, :y int?}}
|
||||||
|
|
@ -75,7 +78,7 @@
|
||||||
(let [{:keys [id] :or {id ::default} :as swagger} (-> match :result request-method :data :swagger)
|
(let [{:keys [id] :or {id ::default} :as swagger} (-> match :result request-method :data :swagger)
|
||||||
ids (trie/into-set id)
|
ids (trie/into-set id)
|
||||||
strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions)
|
strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions)
|
||||||
strip-endpoint-keys #(dissoc % :id :parameters :responses :summary :description)
|
strip-endpoint-keys #(dissoc % :id :parameters :responses :summary :description :operationId)
|
||||||
swagger (->> (strip-endpoint-keys swagger)
|
swagger (->> (strip-endpoint-keys swagger)
|
||||||
(merge {:swagger "2.0"
|
(merge {:swagger "2.0"
|
||||||
:x-id ids}))
|
:x-id ids}))
|
||||||
|
|
@ -93,7 +96,7 @@
|
||||||
(apply meta-merge (keep (comp :swagger :data) interceptors))
|
(apply meta-merge (keep (comp :swagger :data) interceptors))
|
||||||
(if coercion
|
(if coercion
|
||||||
(coercion/get-apidocs coercion :swagger data))
|
(coercion/get-apidocs coercion :swagger data))
|
||||||
(select-keys data [:tags :summary :description])
|
(select-keys data [:tags :summary :description :operationId])
|
||||||
(strip-top-level-keys swagger))]))
|
(strip-top-level-keys swagger))]))
|
||||||
transform-path (fn [[p _ c]]
|
transform-path (fn [[p _ c]]
|
||||||
(if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))]
|
(if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))]
|
||||||
|
|
|
||||||
56
project.clj
56
project.clj
|
|
@ -27,24 +27,24 @@
|
||||||
[metosin/reitit-frontend "0.5.18"]
|
[metosin/reitit-frontend "0.5.18"]
|
||||||
[metosin/reitit-sieppari "0.5.18"]
|
[metosin/reitit-sieppari "0.5.18"]
|
||||||
[metosin/reitit-pedestal "0.5.18"]
|
[metosin/reitit-pedestal "0.5.18"]
|
||||||
[metosin/ring-swagger-ui "4.3.0"]
|
[metosin/ring-swagger-ui "4.15.5"]
|
||||||
[metosin/spec-tools "0.10.5"]
|
[metosin/spec-tools "0.10.5"]
|
||||||
[metosin/schema-tools "0.12.3"]
|
[metosin/schema-tools "0.12.3"]
|
||||||
[metosin/muuntaja "0.6.8"]
|
[metosin/muuntaja "0.6.8"]
|
||||||
[metosin/jsonista "0.3.5"]
|
[metosin/jsonista "0.3.7"]
|
||||||
[metosin/sieppari "0.0.0-alpha13"]
|
[metosin/sieppari "0.0.0-alpha13"]
|
||||||
[metosin/malli "0.8.2"]
|
[metosin/malli "0.10.1"]
|
||||||
|
|
||||||
;; https://clojureverse.org/t/depending-on-the-right-versions-of-jackson-libraries/5111
|
;; https://clojureverse.org/t/depending-on-the-right-versions-of-jackson-libraries/5111
|
||||||
[com.fasterxml.jackson.core/jackson-core "2.13.2"]
|
[com.fasterxml.jackson.core/jackson-core "2.14.1"]
|
||||||
[com.fasterxml.jackson.core/jackson-databind "2.13.2.2"]
|
[com.fasterxml.jackson.core/jackson-databind "2.14.1"]
|
||||||
|
|
||||||
[meta-merge "1.0.0"]
|
[meta-merge "1.0.0"]
|
||||||
[fipp "0.6.25" :exclusions [org.clojure/core.rrb-vector]]
|
[fipp "0.6.26" :exclusions [org.clojure/core.rrb-vector]]
|
||||||
[expound "0.9.0"]
|
[expound "0.9.0"]
|
||||||
[lambdaisland/deep-diff "0.0-47"]
|
[lambdaisland/deep-diff "0.0-47"]
|
||||||
[com.bhauman/spell-spec "0.1.2"]
|
[com.bhauman/spell-spec "0.1.2"]
|
||||||
[ring/ring-core "1.9.5"]
|
[ring/ring-core "1.9.6"]
|
||||||
|
|
||||||
[io.pedestal/pedestal.service "0.5.10"]]
|
[io.pedestal/pedestal.service "0.5.10"]]
|
||||||
|
|
||||||
|
|
@ -78,7 +78,7 @@
|
||||||
|
|
||||||
:java-source-paths ["modules/reitit-core/java-src"]
|
:java-source-paths ["modules/reitit-core/java-src"]
|
||||||
|
|
||||||
:dependencies [[org.clojure/clojure "1.10.2"]
|
:dependencies [[org.clojure/clojure "1.11.1"]
|
||||||
[org.clojure/clojurescript "1.10.773"]
|
[org.clojure/clojurescript "1.10.773"]
|
||||||
|
|
||||||
;; modules dependencies
|
;; modules dependencies
|
||||||
|
|
@ -86,55 +86,55 @@
|
||||||
[metosin/spec-tools "0.10.5"]
|
[metosin/spec-tools "0.10.5"]
|
||||||
[metosin/muuntaja "0.6.8"]
|
[metosin/muuntaja "0.6.8"]
|
||||||
[metosin/sieppari "0.0.0-alpha13"]
|
[metosin/sieppari "0.0.0-alpha13"]
|
||||||
[metosin/jsonista "0.3.5"]
|
[metosin/jsonista "0.3.7"]
|
||||||
[metosin/malli "0.8.9"]
|
[metosin/malli "0.10.1"]
|
||||||
[lambdaisland/deep-diff "0.0-47"]
|
[lambdaisland/deep-diff "0.0-47"]
|
||||||
[meta-merge "1.0.0"]
|
[meta-merge "1.0.0"]
|
||||||
[com.bhauman/spell-spec "0.1.2"]
|
[com.bhauman/spell-spec "0.1.2"]
|
||||||
[expound "0.9.0"]
|
[expound "0.9.0"]
|
||||||
[fipp "0.6.25"]
|
[fipp "0.6.26"]
|
||||||
|
|
||||||
[orchestra "2021.01.01-1"]
|
[orchestra "2021.01.01-1"]
|
||||||
|
|
||||||
[ring "1.9.5"]
|
[ring "1.9.6"]
|
||||||
[ikitommi/immutant-web "3.0.0-alpha1"]
|
[ikitommi/immutant-web "3.0.0-alpha1"]
|
||||||
[metosin/ring-http-response "0.9.3"]
|
[metosin/ring-http-response "0.9.3"]
|
||||||
[metosin/ring-swagger-ui "4.3.0"]
|
[metosin/ring-swagger-ui "4.15.5"]
|
||||||
|
|
||||||
[criterium "0.4.6"]
|
[criterium "0.4.6"]
|
||||||
[org.clojure/test.check "1.1.1"]
|
[org.clojure/test.check "1.1.1"]
|
||||||
[org.clojure/tools.namespace "1.2.0"]
|
[org.clojure/tools.namespace "1.3.0"]
|
||||||
[com.gfredericks/test.chuck "0.2.13"]
|
[com.gfredericks/test.chuck "0.2.13"]
|
||||||
|
|
||||||
[io.pedestal/pedestal.service "0.5.10"]
|
[io.pedestal/pedestal.service "0.5.10"]
|
||||||
|
|
||||||
[org.clojure/core.async "1.5.648"]
|
[org.clojure/core.async "1.6.673"]
|
||||||
[manifold "0.2.3"]
|
[manifold "0.3.0"]
|
||||||
[funcool/promesa "6.1.434"]
|
[funcool/promesa "10.0.594"]
|
||||||
|
|
||||||
[com.clojure-goes-fast/clj-async-profiler "0.5.1"]
|
[com.clojure-goes-fast/clj-async-profiler "1.0.3"]
|
||||||
[ring-cors "0.1.13"]
|
[ring-cors "0.1.13"]
|
||||||
|
|
||||||
[com.bhauman/rebel-readline "0.1.4"]]}
|
[com.bhauman/rebel-readline "0.1.4"]]}
|
||||||
:1.9 {:dependencies [[org.clojure/clojure "1.9.0"]]}
|
:1.10 {:dependencies [[org.clojure/clojure "1.10.3"]]}
|
||||||
:perf {:jvm-opts ^:replace ["-server"
|
:perf {:jvm-opts ^:replace ["-server"
|
||||||
"-Xmx4096m"
|
"-Xmx4096m"
|
||||||
"-Dclojure.compiler.direct-linking=true"]
|
"-Dclojure.compiler.direct-linking=true"]
|
||||||
:test-paths ["perf-test/clj"]
|
:test-paths ["perf-test/clj"]
|
||||||
:dependencies [[compojure "1.6.2"]
|
:dependencies [[compojure "1.7.0"]
|
||||||
[ring/ring-defaults "0.3.3"]
|
[ring/ring-defaults "0.3.4"]
|
||||||
[ikitommi/immutant-web "3.0.0-alpha1"]
|
[ikitommi/immutant-web "3.0.0-alpha1"]
|
||||||
[io.pedestal/pedestal.service "0.5.10"]
|
[io.pedestal/pedestal.service "0.5.10"]
|
||||||
[io.pedestal/pedestal.jetty "0.5.10"]
|
[io.pedestal/pedestal.jetty "0.5.10"]
|
||||||
[calfpath "0.8.1"]
|
[calfpath "0.8.1"]
|
||||||
[org.clojure/core.async "1.5.648"]
|
[org.clojure/core.async "1.6.673"]
|
||||||
[manifold "0.2.3"]
|
[manifold "0.3.0"]
|
||||||
[funcool/promesa "6.1.434"]
|
[funcool/promesa "10.0.594"]
|
||||||
[metosin/sieppari]
|
[metosin/sieppari]
|
||||||
[yada "1.2.16"]
|
[yada "1.2.16"]
|
||||||
[aleph "0.4.6"]
|
[aleph "0.6.0"]
|
||||||
[ring/ring-defaults "0.3.3"]
|
[ring/ring-defaults "0.3.4"]
|
||||||
[ataraxy "0.4.2"]
|
[ataraxy "0.4.3"]
|
||||||
[bidi "2.1.6"]
|
[bidi "2.1.6"]
|
||||||
[janus "1.3.2"]]}
|
[janus "1.3.2"]]}
|
||||||
:analyze {:jvm-opts ^:replace ["-server"
|
:analyze {:jvm-opts ^:replace ["-server"
|
||||||
|
|
@ -142,7 +142,7 @@
|
||||||
"-XX:+PrintCompilation"
|
"-XX:+PrintCompilation"
|
||||||
"-XX:+UnlockDiagnosticVMOptions"
|
"-XX:+UnlockDiagnosticVMOptions"
|
||||||
"-XX:+PrintInlining"]}}
|
"-XX:+PrintInlining"]}}
|
||||||
:aliases {"all" ["with-profile" "dev,default:dev,default,1.9"]
|
:aliases {"all" ["with-profile" "dev,default:dev,default,1.10"]
|
||||||
"perf" ["with-profile" "default,dev,perf"]
|
"perf" ["with-profile" "default,dev,perf"]
|
||||||
"test-clj" ["all" "do" ["bat-test"] ["check"]]
|
"test-clj" ["all" "do" ["bat-test"] ["check"]]
|
||||||
"test-browser" ["doo" "chrome-headless" "test"]
|
"test-browser" ["doo" "chrome-headless" "test"]
|
||||||
|
|
|
||||||
|
|
@ -30,4 +30,4 @@
|
||||||
(spit "doc/cljdoc.edn" (with-out-str (pprint/pprint data)))))
|
(spit "doc/cljdoc.edn" (with-out-str (pprint/pprint data)))))
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(reap!))
|
(reap!))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
(ns reitit.exception-test
|
(ns reitit.exception-test
|
||||||
(:require [clojure.spec.alpha :as s]
|
(:require [clojure.spec.alpha :as s]
|
||||||
[clojure.test :refer [are deftest is testing]]
|
[clojure.test :refer [are deftest is]]
|
||||||
[reitit.core :as r]
|
[reitit.core :as r]
|
||||||
[reitit.dev.pretty :as pretty]
|
[reitit.dev.pretty :as pretty]
|
||||||
[reitit.exception :as exception]
|
[reitit.exception :as exception]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(ns reitit.impl-test
|
(ns reitit.impl-test
|
||||||
(:require [clojure.test :refer [are deftest is testing]]
|
(:require [clojure.test :refer [are deftest is]]
|
||||||
[reitit.impl :as impl]))
|
[reitit.impl :as impl]))
|
||||||
|
|
||||||
(deftest strip-nils-test
|
(deftest strip-nils-test
|
||||||
|
|
@ -50,6 +50,8 @@
|
||||||
{"a" "b"} "a=b"
|
{"a" "b"} "a=b"
|
||||||
{:a 1} "a=1"
|
{:a 1} "a=1"
|
||||||
{:a nil} "a="
|
{:a nil} "a="
|
||||||
|
{:a []} "a="
|
||||||
|
{:a '()} "a="
|
||||||
{:a :b :c "d"} "a=b&c=d"
|
{:a :b :c "d"} "a=b&c=d"
|
||||||
{:a "b c"} "a=b+c"
|
{:a "b c"} "a=b+c"
|
||||||
{:a ["b" "c"]} "a=b&a=c"
|
{:a ["b" "c"]} "a=b&a=c"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(ns reitit.interceptor-test
|
(ns reitit.interceptor-test
|
||||||
(:require [clojure.test :refer [are deftest is testing]]
|
(:require [clojure.test :refer [deftest is testing]]
|
||||||
[reitit.core :as r]
|
[reitit.core :as r]
|
||||||
[reitit.interceptor :as interceptor])
|
[reitit.interceptor :as interceptor])
|
||||||
#?(:clj
|
#?(:clj
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(ns reitit.middleware-test
|
(ns reitit.middleware-test
|
||||||
(:require [clojure.test :refer [are deftest is testing]]
|
(:require [clojure.test :refer [deftest is testing]]
|
||||||
[reitit.core :as r]
|
[reitit.core :as r]
|
||||||
[reitit.middleware :as middleware])
|
[reitit.middleware :as middleware])
|
||||||
#?(:clj
|
#?(:clj
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@
|
||||||
(:require [clojure.test :refer [deftest is testing]]
|
(:require [clojure.test :refer [deftest is testing]]
|
||||||
[malli.experimental.lite :as l]
|
[malli.experimental.lite :as l]
|
||||||
#?@(:clj [[muuntaja.middleware]
|
#?@(:clj [[muuntaja.middleware]
|
||||||
[jsonista.core :as j]])
|
[jsonista.core :as j]])
|
||||||
|
[malli.core :as m]
|
||||||
|
[malli.util :as mu]
|
||||||
|
[meta-merge.core :refer [meta-merge]]
|
||||||
[reitit.coercion.malli :as malli]
|
[reitit.coercion.malli :as malli]
|
||||||
[reitit.coercion.schema :as schema]
|
[reitit.coercion.schema :as schema]
|
||||||
[reitit.coercion.spec :as spec]
|
[reitit.coercion.spec :as spec]
|
||||||
|
|
@ -208,6 +211,38 @@
|
||||||
(let [{:keys [status]} (app invalid-request2)]
|
(let [{:keys [status]} (app invalid-request2)]
|
||||||
(is (= 500 status))))))))
|
(is (= 500 status))))))))
|
||||||
|
|
||||||
|
(defn- custom-meta-merge-checking-schema
|
||||||
|
([] {})
|
||||||
|
([left] left)
|
||||||
|
([left right]
|
||||||
|
(cond
|
||||||
|
(and (map? left) (map? right))
|
||||||
|
(merge-with custom-meta-merge-checking-schema left right)
|
||||||
|
|
||||||
|
(and (m/schema? left)
|
||||||
|
(m/schema? right))
|
||||||
|
(mu/merge left right)
|
||||||
|
|
||||||
|
:else
|
||||||
|
(meta-merge left right)))
|
||||||
|
([left right & more]
|
||||||
|
(reduce custom-meta-merge-checking-schema left (cons right more))))
|
||||||
|
|
||||||
|
(defn- custom-meta-merge-checking-parameters
|
||||||
|
([] {})
|
||||||
|
([left] left)
|
||||||
|
([left right]
|
||||||
|
(if (and (map? left) (map? right)
|
||||||
|
(contains? left :parameters)
|
||||||
|
(contains? right :parameters))
|
||||||
|
(-> (merge-with custom-meta-merge-checking-parameters left right)
|
||||||
|
(assoc :parameters (merge-with mu/merge
|
||||||
|
(:parameters left)
|
||||||
|
(:parameters right))))
|
||||||
|
(meta-merge left right)))
|
||||||
|
([left right & more]
|
||||||
|
(reduce custom-meta-merge-checking-parameters left (cons right more))))
|
||||||
|
|
||||||
(deftest malli-coercion-test
|
(deftest malli-coercion-test
|
||||||
(let [create (fn [middleware routes]
|
(let [create (fn [middleware routes]
|
||||||
(ring/ring-handler
|
(ring/ring-handler
|
||||||
|
|
@ -524,7 +559,28 @@
|
||||||
(is (= {:status 200, :body {:total -4}} (call "application/json" [:int {:encode/json -}]))))
|
(is (= {:status 200, :body {:total -4}} (call "application/json" [:int {:encode/json -}]))))
|
||||||
|
|
||||||
(testing "edn encoding (nada)"
|
(testing "edn encoding (nada)"
|
||||||
(is (= {:status 200, :body {:total +4}} (call "application/edn" [:int {:encode/json -}]))))))))
|
(is (= {:status 200, :body {:total +4}} (call "application/edn" [:int {:encode/json -}]))))))
|
||||||
|
|
||||||
|
(testing "using custom meta-merge function"
|
||||||
|
(let [->app (fn [schema-fn meta-merge]
|
||||||
|
(ring/ring-handler
|
||||||
|
(ring/router
|
||||||
|
["/merging-params/:foo" {:parameters {:path (schema-fn [:map [:foo :string]])}}
|
||||||
|
["/:bar" {:parameters {:path (schema-fn [:map [:bar :string]])}
|
||||||
|
:get {:handler (fn [{{{:keys [foo bar]} :path} :parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:total (str "FOO: " foo ", "
|
||||||
|
"BAR: " bar)}})}}]]
|
||||||
|
{:data {:middleware [rrc/coerce-request-middleware
|
||||||
|
rrc/coerce-response-middleware]
|
||||||
|
:coercion malli/coercion}
|
||||||
|
:meta-merge meta-merge})))
|
||||||
|
call (fn [schema-fn meta-merge]
|
||||||
|
((->app schema-fn meta-merge) {:uri "/merging-params/this/that"
|
||||||
|
:request-method :get}))]
|
||||||
|
|
||||||
|
(is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call m/schema custom-meta-merge-checking-schema)))
|
||||||
|
(is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call identity custom-meta-merge-checking-parameters)))))))
|
||||||
|
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(deftest muuntaja-test
|
(deftest muuntaja-test
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@
|
||||||
ExceptionInfo
|
ExceptionInfo
|
||||||
#"Invalid route data"
|
#"Invalid route data"
|
||||||
(ring/router
|
(ring/router
|
||||||
["/api" {:handler identity
|
["/api" {:handler identity
|
||||||
:middleware '()}]
|
:middleware '()}]
|
||||||
{:validate rrs/validate})))))
|
{:validate rrs/validate})))))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
(ns reitit.ring-test
|
(ns reitit.ring-test
|
||||||
(:require [clojure.set :as set]
|
(:require [clojure.set :as set]
|
||||||
[clojure.test :refer [deftest is testing]]
|
[clojure.test :refer [are deftest is testing]]
|
||||||
[reitit.core :as r]
|
[reitit.core :as r]
|
||||||
[reitit.middleware :as middleware]
|
[reitit.middleware :as middleware]
|
||||||
[reitit.ring :as ring]
|
[reitit.ring :as ring]
|
||||||
|
|
@ -312,7 +312,15 @@
|
||||||
|
|
||||||
(testing "does not strip slashes"
|
(testing "does not strip slashes"
|
||||||
(is (= nil (app {:request-method :get, :uri "/slash-less/"})))
|
(is (= nil (app {:request-method :get, :uri "/slash-less/"})))
|
||||||
(is (= nil (app {:request-method :post, :uri "/slash-less/"}))))))
|
(is (= nil (app {:request-method :post, :uri "/slash-less/"}))))
|
||||||
|
|
||||||
|
(testing "retains query-string in location header"
|
||||||
|
(are [method uri]
|
||||||
|
(is (= "/with-slash/?kikka=kukka"
|
||||||
|
(get-in (app {:request-method method :uri uri :query-string "kikka=kukka"})
|
||||||
|
[:headers "Location"])))
|
||||||
|
:get "/with-slash"
|
||||||
|
:post "/with-slash"))))
|
||||||
|
|
||||||
(testing "using :method :strip"
|
(testing "using :method :strip"
|
||||||
(let [app (ring/ring-handler
|
(let [app (ring/ring-handler
|
||||||
|
|
@ -338,7 +346,17 @@
|
||||||
|
|
||||||
(testing "strips multiple slashes"
|
(testing "strips multiple slashes"
|
||||||
(is (= 301 (:status (app {:request-method :get, :uri "/slash-less/////"}))))
|
(is (= 301 (:status (app {:request-method :get, :uri "/slash-less/////"}))))
|
||||||
(is (= 308 (:status (app {:request-method :post, :uri "/slash-less//"})))))))
|
(is (= 308 (:status (app {:request-method :post, :uri "/slash-less//"})))))
|
||||||
|
|
||||||
|
(testing "retains query-string in location header"
|
||||||
|
(are [method uri]
|
||||||
|
(is (= "/slash-less?kikka=kukka"
|
||||||
|
(get-in (app {:request-method method :uri uri :query-string "kikka=kukka"})
|
||||||
|
[:headers "Location"])))
|
||||||
|
:get "/slash-less/"
|
||||||
|
:get "/slash-less//"
|
||||||
|
:post "/slash-less/"
|
||||||
|
:post "/slash-less//"))))
|
||||||
|
|
||||||
(testing "without option (equivalent to using :method :both)"
|
(testing "without option (equivalent to using :method :both)"
|
||||||
(let [app (ring/ring-handler
|
(let [app (ring/ring-handler
|
||||||
|
|
@ -361,7 +379,19 @@
|
||||||
|
|
||||||
(testing "strips multiple slashes"
|
(testing "strips multiple slashes"
|
||||||
(is (= 301 (:status (app {:request-method :get, :uri "/slash-less/////"}))))
|
(is (= 301 (:status (app {:request-method :get, :uri "/slash-less/////"}))))
|
||||||
(is (= 308 (:status (app {:request-method :post, :uri "/slash-less//"})))))))))
|
(is (= 308 (:status (app {:request-method :post, :uri "/slash-less//"})))))
|
||||||
|
|
||||||
|
(testing "retains query-string in location header"
|
||||||
|
(are [method uri expected-location]
|
||||||
|
(is (= expected-location
|
||||||
|
(get-in (app {:request-method method :uri uri :query-string "kikka=kukka"})
|
||||||
|
[:headers "Location"])))
|
||||||
|
:get "/with-slash" "/with-slash/?kikka=kukka"
|
||||||
|
:get "/slash-less/" "/slash-less?kikka=kukka"
|
||||||
|
:get "/slash-less//" "/slash-less?kikka=kukka"
|
||||||
|
:post "/with-slash" "/with-slash/?kikka=kukka"
|
||||||
|
:post "/slash-less/" "/slash-less?kikka=kukka"
|
||||||
|
:post "/slash-less//" "/slash-less?kikka=kukka"))))))
|
||||||
|
|
||||||
(deftest async-ring-test
|
(deftest async-ring-test
|
||||||
(let [promise #(let [value (atom ::nil)]
|
(let [promise #(let [value (atom ::nil)]
|
||||||
|
|
@ -703,6 +733,6 @@
|
||||||
{::trie/trie-compiler compiler})]
|
{::trie/trie-compiler compiler})]
|
||||||
(dotimes [_ 10]
|
(dotimes [_ 10]
|
||||||
(future
|
(future
|
||||||
(dotimes [n 100000]
|
(dotimes [n 100000]
|
||||||
(let [body (:body (app {:request-method :get, :uri (str "/" n)}))]
|
(let [body (:body (app {:request-method :get, :uri (str "/" n)}))]
|
||||||
(is (= body (str n))))))))))))
|
(is (= body (str n))))))))))))
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
(are [data]
|
(are [data]
|
||||||
(is (thrown-with-msg?
|
(is (thrown-with-msg?
|
||||||
ExceptionInfo
|
ExceptionInfo
|
||||||
#"Call to #'reitit.core/router did not conform to spec"
|
#"Call to (#')*reitit.core/router did not conform to spec"
|
||||||
(r/router
|
(r/router
|
||||||
data)))
|
data)))
|
||||||
|
|
||||||
|
|
@ -69,7 +69,7 @@
|
||||||
(are [opts]
|
(are [opts]
|
||||||
(is (thrown-with-msg?
|
(is (thrown-with-msg?
|
||||||
ExceptionInfo
|
ExceptionInfo
|
||||||
#"Call to #'reitit.core/router did not conform to spec"
|
#"Call to (#')*reitit.core/router did not conform to spec"
|
||||||
(r/router
|
(r/router
|
||||||
["/api"] opts)))
|
["/api"] opts)))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,13 @@
|
||||||
["/spec" {:coercion spec/coercion}
|
["/spec" {:coercion spec/coercion}
|
||||||
["/plus/:z"
|
["/plus/:z"
|
||||||
{:patch {:summary "patch"
|
{:patch {:summary "patch"
|
||||||
|
:operationId "Patch"
|
||||||
:handler (constantly {:status 200})}
|
:handler (constantly {:status 200})}
|
||||||
:options {:summary "options"
|
:options {:summary "options"
|
||||||
:middleware [{:data {:swagger {:responses {200 {:description "200"}}}}}]
|
:middleware [{:data {:swagger {:responses {200 {:description "200"}}}}}]
|
||||||
:handler (constantly {:status 200})}
|
:handler (constantly {:status 200})}
|
||||||
:get {:summary "plus"
|
:get {:summary "plus"
|
||||||
|
:operationId "GetPlus"
|
||||||
:parameters {:query {:x int?, :y int?}
|
:parameters {:query {:x int?, :y int?}
|
||||||
:path {:z int?}}
|
:path {:z int?}}
|
||||||
:swagger {:responses {400 {:schema {:type "string"}
|
:swagger {:responses {400 {:schema {:type "string"}
|
||||||
|
|
@ -118,6 +120,7 @@
|
||||||
(app {:request-method :get
|
(app {:request-method :get
|
||||||
:uri "/api/schema/plus/3"
|
:uri "/api/schema/plus/3"
|
||||||
:query-params {:x "2", :y "1"}})))))
|
:query-params {:x "2", :y "1"}})))))
|
||||||
|
|
||||||
(testing "swagger-spec"
|
(testing "swagger-spec"
|
||||||
(let [spec (:body (app {:request-method :get
|
(let [spec (:body (app {:request-method :get
|
||||||
:uri "/api/swagger.json"}))
|
:uri "/api/swagger.json"}))
|
||||||
|
|
@ -126,6 +129,7 @@
|
||||||
:info {:title "my-api"}
|
:info {:title "my-api"}
|
||||||
:paths {"/api/spec/plus/{z}" {:patch {:parameters []
|
:paths {"/api/spec/plus/{z}" {:patch {:parameters []
|
||||||
:summary "patch"
|
:summary "patch"
|
||||||
|
:operationId "Patch"
|
||||||
:responses {:default {:description ""}}}
|
:responses {:default {:description ""}}}
|
||||||
:options {:parameters []
|
:options {:parameters []
|
||||||
:summary "options"
|
:summary "options"
|
||||||
|
|
@ -156,7 +160,8 @@
|
||||||
400 {:schema {:type "string"}
|
400 {:schema {:type "string"}
|
||||||
:description "kosh"}
|
:description "kosh"}
|
||||||
500 {:description "fail"}}
|
500 {:description "fail"}}
|
||||||
:summary "plus"}
|
:summary "plus"
|
||||||
|
:operationId "GetPlus"}
|
||||||
:post {:parameters [{:in "body",
|
:post {:parameters [{:in "body",
|
||||||
:name "body",
|
:name "body",
|
||||||
:description "",
|
:description "",
|
||||||
|
|
@ -201,6 +206,7 @@
|
||||||
:responses {200 {:schema {:type "object"
|
:responses {200 {:schema {:type "object"
|
||||||
:properties {:total {:format "int64"
|
:properties {:total {:format "int64"
|
||||||
:type "integer"}}
|
:type "integer"}}
|
||||||
|
:additionalProperties false
|
||||||
:required [:total]}
|
:required [:total]}
|
||||||
:description ""}
|
:description ""}
|
||||||
400 {:schema {:type "string"}
|
400 {:schema {:type "string"}
|
||||||
|
|
@ -224,6 +230,7 @@
|
||||||
:responses {200 {:description ""
|
:responses {200 {:description ""
|
||||||
:schema {:properties {:total {:format "int64"
|
:schema {:properties {:total {:format "int64"
|
||||||
:type "integer"}}
|
:type "integer"}}
|
||||||
|
:additionalProperties false
|
||||||
:required [:total]
|
:required [:total]
|
||||||
:type "object"}}
|
:type "object"}}
|
||||||
400 {:schema {:type "string"}
|
400 {:schema {:type "string"}
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@
|
||||||
(let [log (atom [])
|
(let [log (atom [])
|
||||||
controller-state (atom [])
|
controller-state (atom [])
|
||||||
controller-1 {:start (fn [_] (swap! log conj :start-1))
|
controller-1 {:start (fn [_] (swap! log conj :start-1))
|
||||||
:stop (fn [_] (swap! log conj :stop-1))}
|
:stop (fn [_] (swap! log conj :stop-1))}
|
||||||
controller-2 {:start (fn [_] (swap! log conj :start-2))
|
controller-2 {:start (fn [_] (swap! log conj :start-2))
|
||||||
:stop (fn [_] (swap! log conj :stop-2))}
|
:stop (fn [_] (swap! log conj :stop-2))}
|
||||||
controller-3 {:start (fn [{:keys [foo]}] (swap! log conj [:start-3 foo]))
|
controller-3 {:start (fn [{:keys [foo]}] (swap! log conj [:start-3 foo]))
|
||||||
:stop (fn [{:keys [foo]}] (swap! log conj [:stop-3 foo]))
|
:stop (fn [{:keys [foo]}] (swap! log conj [:stop-3 foo]))
|
||||||
:identity (fn [match]
|
:identity (fn [match]
|
||||||
{:foo (-> match :parameters :path :foo)})}]
|
{:foo (-> match :parameters :path :foo)})}]
|
||||||
|
|
||||||
|
|
@ -70,9 +70,9 @@
|
||||||
(let [log (atom [])
|
(let [log (atom [])
|
||||||
controller-state (atom [])
|
controller-state (atom [])
|
||||||
static {:start (fn [params] (swap! log conj [:start-static]))
|
static {:start (fn [params] (swap! log conj [:start-static]))
|
||||||
:stop (fn [params] (swap! log conj [:stop-static]))}
|
:stop (fn [params] (swap! log conj [:stop-static]))}
|
||||||
controller {:start (fn [params] (swap! log conj [:start params]))
|
controller {:start (fn [params] (swap! log conj [:start params]))
|
||||||
:stop (fn [params] (swap! log conj [:stop params]))
|
:stop (fn [params] (swap! log conj [:stop params]))
|
||||||
:parameters {:path [:foo]}}]
|
:parameters {:path [:foo]}}]
|
||||||
|
|
||||||
(testing "init"
|
(testing "init"
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,9 @@
|
||||||
(is (= [{:type :warn
|
(is (= [{:type :warn
|
||||||
:message ["missing route" ::asd]}]
|
:message ["missing route" ::asd]}]
|
||||||
(:messages
|
(:messages
|
||||||
(capture-console
|
(capture-console
|
||||||
(fn []
|
(fn []
|
||||||
(rf/match-by-name! router ::asd)))))))))
|
(rf/match-by-name! router ::asd)))))))))
|
||||||
|
|
||||||
(testing "schema coercion"
|
(testing "schema coercion"
|
||||||
(let [router (r/router ["/"
|
(let [router (r/router ["/"
|
||||||
|
|
@ -124,6 +124,6 @@
|
||||||
:required #{:id}
|
:required #{:id}
|
||||||
:path-params {}}]}]
|
:path-params {}}]}]
|
||||||
(:messages
|
(:messages
|
||||||
(capture-console
|
(capture-console
|
||||||
(fn []
|
(fn []
|
||||||
(rf/match-by-name! router ::foo {}))))))))))
|
(rf/match-by-name! router ::foo {}))))))))))
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@
|
||||||
(is (= "#/bar/5?q=x"
|
(is (= "#/bar/5?q=x"
|
||||||
(rfh/href history ::bar {:id 5} {:q "x"})))
|
(rfh/href history ::bar {:id 5} {:q "x"})))
|
||||||
(let [{:keys [value messages]} (capture-console
|
(let [{:keys [value messages]} (capture-console
|
||||||
(fn []
|
(fn []
|
||||||
(rfh/href history ::asd)))]
|
(rfh/href history ::asd)))]
|
||||||
(is (= nil value))
|
(is (= nil value))
|
||||||
(is (= [{:type :warn
|
(is (= [{:type :warn
|
||||||
:message ["missing route" ::asd]}]
|
:message ["missing route" ::asd]}]
|
||||||
|
|
@ -84,8 +84,8 @@
|
||||||
(is (= "/bar/5?q=x"
|
(is (= "/bar/5?q=x"
|
||||||
(rfh/href history ::bar {:id 5} {:q "x"})))
|
(rfh/href history ::bar {:id 5} {:q "x"})))
|
||||||
(let [{:keys [value messages]} (capture-console
|
(let [{:keys [value messages]} (capture-console
|
||||||
(fn []
|
(fn []
|
||||||
(rfh/href history ::asd)))]
|
(rfh/href history ::asd)))]
|
||||||
(is (= nil value))
|
(is (= nil value))
|
||||||
(is (= [{:type :warn
|
(is (= [{:type :warn
|
||||||
:message ["missing route" ::asd]}]
|
:message ["missing route" ::asd]}]
|
||||||
|
|
@ -153,7 +153,7 @@
|
||||||
(done)))))
|
(done)))))
|
||||||
{:use-fragment false})
|
{:use-fragment false})
|
||||||
create-link #(doto
|
create-link #(doto
|
||||||
(js/document.createElement "A")
|
(js/document.createElement "A")
|
||||||
(.setAttribute "href" (rfh/href history ::foo)))
|
(.setAttribute "href" (rfh/href history ::foo)))
|
||||||
document-link (create-link)
|
document-link (create-link)
|
||||||
shadow-link (create-link)]
|
shadow-link (create-link)]
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,6 @@
|
||||||
(set! js/console.warn (partial log :warn))
|
(set! js/console.warn (partial log :warn))
|
||||||
(f)
|
(f)
|
||||||
(finally
|
(finally
|
||||||
(set! js/console.warn original-console-warn)))]
|
(set! js/console.warn original-console-warn)))]
|
||||||
{:value value
|
{:value value
|
||||||
:messages @messages}))
|
:messages @messages}))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue