Merge branch 'master' into into-interceptor-for-multimethods

This commit is contained in:
Tommi Reiman 2023-01-09 17:40:32 +02:00 committed by GitHub
commit f80271fac1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
138 changed files with 2827 additions and 2170 deletions

View file

@ -1,6 +1,6 @@
{;;:skip-comments true {;;:skip-comments true
:lint-as {potemkin/def-derived-map clojure.core/defrecord} :lint-as {potemkin/def-derived-map clojure.core/defrecord}
:linters {:if {:level :off} :linters {:missing-else-branch {:level :off}
:unused-binding {:level :off} :unused-binding {:level :off}
:unused-referred-var {:exclude {clojure.test [deftest testing is are] :unused-referred-var {:exclude {clojure.test [deftest testing is are]
cljs.test [deftest testing is are]}}}} cljs.test [deftest testing is are]}}}}

View file

@ -0,0 +1 @@
{:config-paths ["../../../.clj-kondo"]}

3
.lsp/config.edn Normal file
View file

@ -0,0 +1,3 @@
{:cljfmt {:indents {for-all [[:inner 0]]
are [[:inner 0]]}}
:clean {:ns-inner-blocks-indentation :same-line}}

View file

@ -12,13 +12,50 @@ 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 ## 0.5.18 (2022-04-05)
**[compare](https://github.com/metosin/reitit/compare/0.5.15...master)** * 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))
* Save three seq constructions [#537](https://github.com/metosin/reitit/pull/537)
* update jackson-databind for CVE-2020-36518 [#544](https://github.com/metosin/reitit/pull/544)
* Balance parenthesis in docs [#547](https://github.com/metosin/reitit/pull/547)
## 0.5.17 (2022-03-10)
* FIX match-by-path is broken if there are no non-conflicting wildcard routes [#538](https://github.com/metosin/reitit/issues/538)
## 0.5.16 (2022-02-15)
**[compare](https://github.com/metosin/reitit/compare/0.5.15...0.5.16)**
* Support for [Malli Lite Syntax](https://github.com/metosin/malli#lite) in coercion (enabled by default):
```clj
["/add/:id" {:post {:parameters {:path {:id int?}
:query {:a (l/optional int?)}
:body {:id int?
:data {:id (l/maybe int?)
:orders (l/map-of uuid? {:name string?})}}}
:responses {200 {:body {:total pos-int?}}
500 {:description "fail"}}}}]
```
* Improved Reitit-frontend function docstrings * Improved Reitit-frontend function docstrings
* Allow multimethods to be interpreted as interceptors * Allow multimethods to be interpreted as interceptors
* Updated deps:
```clj
[metosin/ring-swagger-ui "4.3.0"] is available but we use "3.46.0"
[metosin/jsonista "0.3.5"] is available but we use "0.3.3"
[metosin/malli "0.8.2"] is available but we use "0.5.1"
[com.fasterxml.jackson.core/jackson-core "2.13.1"] is available but we use "2.12.4"
[com.fasterxml.jackson.core/jackson-databind "2.13.1"] is available but we use "2.12.4"
[fipp "0.6.25"] is available but we use "0.6.24"
[expound "0.9.0"] is available but we use "0.8.9"
[ring/ring-core "1.9.5"] is available but we use "1.9.4"
```
## 0.5.15 (2021-08-05) ## 0.5.15 (2021-08-05)
**[compare](https://github.com/metosin/reitit/compare/0.5.14...0.5.15)** **[compare](https://github.com/metosin/reitit/compare/0.5.14...0.5.15)**

View file

@ -52,7 +52,7 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians
All main modules bundled: All main modules bundled:
```clj ```clj
[metosin/reitit "0.5.15"] [metosin/reitit "0.5.18"]
``` ```
Optionally, the parts can be required separately. Optionally, the parts can be required separately.
@ -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

View file

@ -40,7 +40,7 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians
All bundled: All bundled:
```clj ```clj
[metosin/reitit "0.5.15"] [metosin/reitit "0.5.18"]
``` ```
Optionally, the parts can be required separately. Optionally, the parts can be required separately.
@ -139,7 +139,7 @@ Routing:
```clj ```clj
(app {:request-method :get, :uri "/api/admin/users"}) (app {:request-method :get, :uri "/api/admin/users"})
; {:status 200, :body "ok", :wrap (:api :admin} ; {:status 200, :body "ok", :wrap (:api :admin)}
(app {:request-method :put, :uri "/api/admin/users"}) (app {:request-method :put, :uri "/api/admin/users"})
; nil ; nil

View file

@ -4,15 +4,16 @@ Routers can be configured via options. The following options are available for t
| 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-fn` | 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

View file

@ -22,7 +22,7 @@ The default exception formatting uses `reitit.exception/exception`. It produces
## Pretty Errors ## Pretty Errors
```clj ```clj
[metosin/reitit-dev "0.5.15"] [metosin/reitit-dev "0.5.18"]
``` ```
For human-readable and developer-friendly exception messages, there is `reitit.dev.pretty/exception` (in the `reitit-dev` module). It is inspired by the lovely errors messages of [ELM](https://elm-lang.org/blog/compiler-errors-for-humans) and [ETA](https://twitter.com/jyothsnasrin/status/1037703436043603968) and uses [fipp](https://github.com/brandonbloom/fipp), [expound](https://github.com/bhb/expound) and [spell-spec](https://github.com/bhauman/spell-spec) for most of heavy lifting. For human-readable and developer-friendly exception messages, there is `reitit.dev.pretty/exception` (in the `reitit-dev` module). It is inspired by the lovely errors messages of [ELM](https://elm-lang.org/blog/compiler-errors-for-humans) and [ETA](https://twitter.com/jyothsnasrin/status/1037703436043603968) and uses [fipp](https://github.com/brandonbloom/fipp), [expound](https://github.com/bhb/expound) and [spell-spec](https://github.com/bhauman/spell-spec) for most of heavy lifting.

View file

@ -2,6 +2,10 @@
[Malli](https://github.com/metosin/malli) is data-driven Schema library for Clojure/Script. [Malli](https://github.com/metosin/malli) is data-driven Schema library for Clojure/Script.
## Default Syntax
By default, [Vector Syntax](https://github.com/metosin/malli#vector-syntax) is used:
```clj ```clj
(require '[reitit.coercion.malli]) (require '[reitit.coercion.malli])
(require '[reitit.coercion :as coercion]) (require '[reitit.coercion :as coercion])
@ -44,6 +48,20 @@ Failing coercion:
; => ExceptionInfo Request coercion failed... ; => ExceptionInfo Request coercion failed...
``` ```
## Lite Syntax
Same using [Lite Syntax](https://github.com/metosin/malli#lite):
```clj
(def router
(r/router
["/:company/users/:user-id" {:name ::user-view
:coercion reitit.coercion.malli/coercion
:parameters {:path {:company string?
:user-id int?}}}]
{:compile coercion/compile-request-coercers}))
```
## Configuring coercion ## Configuring coercion
Using `create` with options to create the coercion instead of `coercion`: Using `create` with options to create the coercion instead of `coercion`:
@ -58,6 +76,8 @@ Using `create` with options to create the coercion instead of `coercion`:
:response {:default reitit.coercion.malli/default-transformer-provider}} :response {:default reitit.coercion.malli/default-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?
:lite true
;; schema identity function (default: close all map schemas) ;; schema identity function (default: close all map schemas)
:compile mu/closed-schema :compile mu/closed-schema
;; validate request & response ;; validate request & response

View file

@ -13,6 +13,13 @@
./scripts/test.sh cljs ./scripts/test.sh cljs
``` ```
## Formatting
```bash
clojure-lsp format
clojure-lsp clean-ns
```
## Documentation ## Documentation
The documentation lives under `doc` and it is hosted on [cljdoc](https://cljdoc.org). See their The documentation lives under `doc` and it is hosted on [cljdoc](https://cljdoc.org). See their

View file

@ -1,7 +1,7 @@
# Default Interceptors # Default Interceptors
```clj ```clj
[metosin/reitit-interceptors "0.5.15"] [metosin/reitit-interceptors "0.5.18"]
``` ```
Just like the [ring default middleware](../ring/default_middleware.md), but for interceptors. Just like the [ring default middleware](../ring/default_middleware.md), but for interceptors.

View file

@ -5,7 +5,7 @@ Reitit has also support for [interceptors](http://pedestal.io/reference/intercep
## Reitit-http ## Reitit-http
```clj ```clj
[metosin/reitit-http "0.5.15"] [metosin/reitit-http "0.5.18"]
``` ```
A module for http-routing using interceptors instead of middleware. Builds on top of the [`reitit-ring`](../ring/ring.md) module having all the same features. A module for http-routing using interceptors instead of middleware. Builds on top of the [`reitit-ring`](../ring/ring.md) module having all the same features.

View file

@ -3,7 +3,7 @@
[Pedestal](http://pedestal.io/) is a backend web framework for Clojure. `reitit-pedestal` provides an alternative routing engine for Pedestal. [Pedestal](http://pedestal.io/) is a backend web framework for Clojure. `reitit-pedestal` provides an alternative routing engine for Pedestal.
```clj ```clj
[metosin/reitit-pedestal "0.5.15"] [metosin/reitit-pedestal "0.5.18"]
``` ```
Why should one use reitit instead of the Pedestal [default routing](http://pedestal.io/reference/routing-quick-reference)? Why should one use reitit instead of the Pedestal [default routing](http://pedestal.io/reference/routing-quick-reference)?
@ -26,8 +26,8 @@ A minimalistic example on how to to swap the default-router with a reitit router
```clj ```clj
; [io.pedestal/pedestal.service "0.5.5"] ; [io.pedestal/pedestal.service "0.5.5"]
; [io.pedestal/pedestal.jetty "0.5.5"] ; [io.pedestal/pedestal.jetty "0.5.5"]
; [metosin/reitit-pedestal "0.5.15"] ; [metosin/reitit-pedestal "0.5.18"]
; [metosin/reitit "0.5.15"] ; [metosin/reitit "0.5.18"]
(require '[io.pedestal.http :as server]) (require '[io.pedestal.http :as server])
(require '[reitit.pedestal :as pedestal]) (require '[reitit.pedestal :as pedestal])

View file

@ -1,7 +1,7 @@
# Sieppari # Sieppari
```clj ```clj
[metosin/reitit-sieppari "0.5.15"] [metosin/reitit-sieppari "0.5.18"]
``` ```
[Sieppari](https://github.com/metosin/sieppari) is a new and fast interceptor implementation for Clojure, with pluggable async supporting [core.async](https://github.com/clojure/core.async), [Manifold](https://github.com/ztellman/manifold) and [Promesa](http://funcool.github.io/promesa/latest). [Sieppari](https://github.com/metosin/sieppari) is a new and fast interceptor implementation for Clojure, with pluggable async supporting [core.async](https://github.com/clojure/core.async), [Manifold](https://github.com/ztellman/manifold) and [Promesa](http://funcool.github.io/promesa/latest).

View file

@ -65,7 +65,7 @@ There is an extra option in http-router (actually, in the underlying interceptor
### Printing Context Diffs ### Printing Context Diffs
```clj ```clj
[metosin/reitit-interceptors "0.5.15"] [metosin/reitit-interceptors "0.5.18"]
``` ```
Using `reitit.http.interceptors.dev/print-context-diffs` transformation, the context diffs between each interceptor are printed out to the console. To use it, add the following router option: Using `reitit.http.interceptors.dev/print-context-diffs` transformation, the context diffs between each interceptor are printed out to the console. To use it, add the following router option:

View file

@ -1,7 +1,7 @@
# Default Middleware # Default Middleware
```clj ```clj
[metosin/reitit-middleware "0.5.15"] [metosin/reitit-middleware "0.5.18"]
``` ```
Any Ring middleware can be used with `reitit-ring`, but using data-driven middleware is preferred as they are easier to manage and in many cases yield better performance. `reitit-middleware` contains a set of common ring middleware, lifted into data-driven middleware. Any Ring middleware can be used with `reitit-ring`, but using data-driven middleware is preferred as they are easier to manage and in many cases yield better performance. `reitit-middleware` contains a set of common ring middleware, lifted into data-driven middleware.

View file

@ -1,7 +1,7 @@
# Exception Handling with Ring # Exception Handling with Ring
```clj ```clj
[metosin/reitit-middleware "0.5.15"] [metosin/reitit-middleware "0.5.18"]
``` ```
Exceptions thrown in router creation can be [handled with custom exception handler](../basics/error_messages.md). By default, exceptions thrown at runtime from a handler or a middleware are not caught by the `reitit.ring/ring-handler`. A good practice is to have a top-level exception handler to log and format errors for clients. Exceptions thrown in router creation can be [handled with custom exception handler](../basics/error_messages.md). By default, exceptions thrown at runtime from a handler or a middleware are not caught by the `reitit.ring/ring-handler`. A good practice is to have a top-level exception handler to log and format errors for clients.

View file

@ -5,7 +5,7 @@
Read more about the [Ring Concepts](https://github.com/ring-clojure/ring/wiki/Concepts). Read more about the [Ring Concepts](https://github.com/ring-clojure/ring/wiki/Concepts).
```clj ```clj
[metosin/reitit-ring "0.5.15"] [metosin/reitit-ring "0.5.18"]
``` ```
## `reitit.ring/ring-router` ## `reitit.ring/ring-router`

View file

@ -1,7 +1,7 @@
# Swagger Support # Swagger Support
``` ```
[metosin/reitit-swagger "0.5.15"] [metosin/reitit-swagger "0.5.18"]
``` ```
Reitit supports [Swagger2](https://swagger.io/) documentation, thanks to [schema-tools](https://github.com/metosin/schema-tools) and [spec-tools](https://github.com/metosin/spec-tools). Documentation is extracted from route definitions, coercion `:parameters` and `:responses` and from a set of new documentation keys. Reitit supports [Swagger2](https://swagger.io/) documentation, thanks to [schema-tools](https://github.com/metosin/schema-tools) and [spec-tools](https://github.com/metosin/spec-tools). Documentation is extracted from route definitions, coercion `:parameters` and `:responses` and from a set of new documentation keys.
@ -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:
@ -44,7 +45,7 @@ If you need to post-process the generated spec, just wrap the handler with a cus
[Swagger-ui](https://github.com/swagger-api/swagger-ui) is a user interface to visualize and interact with the Swagger specification. To make things easy, there is a pre-integrated version of the swagger-ui as a separate module. [Swagger-ui](https://github.com/swagger-api/swagger-ui) is a user interface to visualize and interact with the Swagger specification. To make things easy, there is a pre-integrated version of the swagger-ui as a separate module.
``` ```
[metosin/reitit-swagger-ui "0.5.15"] [metosin/reitit-swagger-ui "0.5.18"]
``` ```
`reitit.swagger-ui/create-swagger-ui-handler` can be used to create a ring-handler to serve the swagger-ui. It accepts the following options: `reitit.swagger-ui/create-swagger-ui-handler` can be used to create a ring-handler to serve the swagger-ui. It accepts the following options:

View file

@ -59,7 +59,7 @@ There is an extra option in ring-router (actually, in the underlying middleware-
### Printing Request Diffs ### Printing Request Diffs
```clj ```clj
[metosin/reitit-middleware "0.5.15"] [metosin/reitit-middleware "0.5.18"]
``` ```
Using `reitit.ring.middleware.dev/print-request-diffs` transformation, the request diffs between each middleware are printed out to the console. To use it, add the following router option: Using `reitit.ring.middleware.dev/print-request-diffs` transformation, the request diffs between each middleware are printed out to the console. To use it, add the following router option:

View file

@ -2,6 +2,6 @@
:description "Reitit Buddy Auth App" :description "Reitit Buddy Auth App"
:dependencies [[org.clojure/clojure "1.10.1"] :dependencies [[org.clojure/clojure "1.10.1"]
[ring/ring-jetty-adapter "1.8.1"] [ring/ring-jetty-adapter "1.8.1"]
[metosin/reitit "0.5.15"] [metosin/reitit "0.5.18"]
[buddy "2.0.0"]] [buddy "2.0.0"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -10,9 +10,9 @@
[ring "1.7.1"] [ring "1.7.1"]
[hiccup "1.0.5"] [hiccup "1.0.5"]
[org.clojure/clojurescript "1.10.439"] [org.clojure/clojurescript "1.10.439"]
[metosin/reitit "0.5.15"] [metosin/reitit "0.5.18"]
[metosin/reitit-schema "0.5.15"] [metosin/reitit-schema "0.5.18"]
[metosin/reitit-frontend "0.5.15"] [metosin/reitit-frontend "0.5.18"]
;; Just for pretty printting the match ;; Just for pretty printting the match
[fipp "0.6.14"]] [fipp "0.6.14"]]

View file

@ -10,9 +10,9 @@
[ring "1.7.1"] [ring "1.7.1"]
[hiccup "1.0.5"] [hiccup "1.0.5"]
[org.clojure/clojurescript "1.10.439"] [org.clojure/clojurescript "1.10.439"]
[metosin/reitit "0.5.15"] [metosin/reitit "0.5.18"]
[metosin/reitit-schema "0.5.15"] [metosin/reitit-schema "0.5.18"]
[metosin/reitit-frontend "0.5.15"] [metosin/reitit-frontend "0.5.18"]
;; Just for pretty printting the match ;; Just for pretty printting the match
[fipp "0.6.14"]] [fipp "0.6.14"]]

View file

@ -10,9 +10,9 @@
[ring "1.7.1"] [ring "1.7.1"]
[hiccup "1.0.5"] [hiccup "1.0.5"]
[org.clojure/clojurescript "1.10.520"] [org.clojure/clojurescript "1.10.520"]
[metosin/reitit "0.5.15"] [metosin/reitit "0.5.18"]
[metosin/reitit-spec "0.5.15"] [metosin/reitit-spec "0.5.18"]
[metosin/reitit-frontend "0.5.15"] [metosin/reitit-frontend "0.5.18"]
;; Just for pretty printting the match ;; Just for pretty printting the match
[fipp "0.6.14"]] [fipp "0.6.14"]]

View file

@ -10,9 +10,9 @@
[ring "1.7.1"] [ring "1.7.1"]
[hiccup "1.0.5"] [hiccup "1.0.5"]
[org.clojure/clojurescript "1.10.520"] [org.clojure/clojurescript "1.10.520"]
[metosin/reitit "0.5.15"] [metosin/reitit "0.5.18"]
[metosin/reitit-spec "0.5.15"] [metosin/reitit-spec "0.5.18"]
[metosin/reitit-frontend "0.5.15"] [metosin/reitit-frontend "0.5.18"]
;; Just for pretty printting the match ;; Just for pretty printting the match
[fipp "0.6.14"]] [fipp "0.6.14"]]

View file

@ -1,7 +1,7 @@
(defproject frontend-re-frame "0.1.0-SNAPSHOT" (defproject frontend-re-frame "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.10.0"]
[org.clojure/clojurescript "1.10.520"] [org.clojure/clojurescript "1.10.520"]
[metosin/reitit "0.5.15"] [metosin/reitit "0.5.18"]
[reagent "0.8.1"] [reagent "0.8.1"]
[re-frame "0.10.6"]] [re-frame "0.10.6"]]

View file

@ -10,9 +10,9 @@
[ring "1.8.1"] [ring "1.8.1"]
[hiccup "1.0.5"] [hiccup "1.0.5"]
[org.clojure/clojurescript "1.10.773"] [org.clojure/clojurescript "1.10.773"]
[metosin/reitit "0.5.15"] [metosin/reitit "0.5.18"]
[metosin/reitit-spec "0.5.15"] [metosin/reitit-spec "0.5.18"]
[metosin/reitit-frontend "0.5.15"] [metosin/reitit-frontend "0.5.18"]
;; Just for pretty printting the match ;; Just for pretty printting the match
[fipp "0.6.23"]] [fipp "0.6.23"]]

View file

@ -3,5 +3,5 @@
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.10.0"]
[ring/ring-jetty-adapter "1.7.1"] [ring/ring-jetty-adapter "1.7.1"]
[aleph "0.4.7-alpha5"] [aleph "0.4.7-alpha5"]
[metosin/reitit "0.5.15"]] [metosin/reitit "0.5.18"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -5,5 +5,5 @@
[funcool/promesa "1.9.0"] [funcool/promesa "1.9.0"]
[manifold "0.1.8"] [manifold "0.1.8"]
[ring/ring-jetty-adapter "1.7.1"] [ring/ring-jetty-adapter "1.7.1"]
[metosin/reitit "0.5.15"]] [metosin/reitit "0.5.18"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -2,4 +2,4 @@
:description "Reitit coercion with vanilla ring" :description "Reitit coercion with vanilla ring"
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.10.0"]
[ring/ring-jetty-adapter "1.7.1"] [ring/ring-jetty-adapter "1.7.1"]
[metosin/reitit "0.5.15"]]) [metosin/reitit "0.5.18"]])

View file

@ -0,0 +1,11 @@
/target
/classes
/checkouts
pom.xml
pom.xml.asc
*.jar
*.class
/.lein-*
/.nrepl-port
.hgignore
.hg/

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

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

View file

@ -3,6 +3,6 @@
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.10.0"]
[io.pedestal/pedestal.service "0.5.5"] [io.pedestal/pedestal.service "0.5.5"]
[io.pedestal/pedestal.jetty "0.5.5"] [io.pedestal/pedestal.jetty "0.5.5"]
[metosin/reitit-pedestal "0.5.15"] [metosin/reitit-pedestal "0.5.18"]
[metosin/reitit "0.5.15"]] [metosin/reitit "0.5.18"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -3,6 +3,6 @@
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.10.0"]
[io.pedestal/pedestal.service "0.5.5"] [io.pedestal/pedestal.service "0.5.5"]
[io.pedestal/pedestal.jetty "0.5.5"] [io.pedestal/pedestal.jetty "0.5.5"]
[metosin/reitit-pedestal "0.5.15"] [metosin/reitit-pedestal "0.5.18"]
[metosin/reitit "0.5.15"]] [metosin/reitit "0.5.18"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -2,5 +2,5 @@
:description "Reitit Ring App" :description "Reitit Ring App"
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.10.0"]
[ring/ring-jetty-adapter "1.7.1"] [ring/ring-jetty-adapter "1.7.1"]
[metosin/reitit "0.5.15"]] [metosin/reitit "0.5.18"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -2,7 +2,7 @@
:description "Reitit Ring App with Integrant" :description "Reitit Ring App with Integrant"
:dependencies [[org.clojure/clojure "1.10.1"] :dependencies [[org.clojure/clojure "1.10.1"]
[ring/ring-jetty-adapter "1.7.1"] [ring/ring-jetty-adapter "1.7.1"]
[metosin/reitit "0.5.15"] [metosin/reitit "0.5.18"]
[integrant "0.7.0"]] [integrant "0.7.0"]]
:main example.server :main example.server
:repl-options {:init-ns user} :repl-options {:init-ns user}

View file

@ -0,0 +1,11 @@
/target
/classes
/checkouts
pom.xml
pom.xml.asc
*.jar
*.class
/.lein-*
/.nrepl-port
.hgignore
.hg/

View file

@ -0,0 +1,23 @@
# reitit-ring, malli, swagger
## Usage
```clj
> lein repl
(start)
```
To test the endpoints using [httpie](https://httpie.org/):
```bash
http GET :3000/math/plus x==1 y==20
http POST :3000/math/plus x:=1 y:=20
http GET :3000/swagger.json
```
<img src="https://raw.githubusercontent.com/metosin/reitit/master/examples/ring-spec-swagger/swagger.png" />
## License
Copyright © 2017-2019 Metosin Oy

View file

@ -0,0 +1,8 @@
(defproject ring-example "0.1.0-SNAPSHOT"
:description "Reitit Ring App with Swagger"
:dependencies [[org.clojure/clojure "1.10.0"]
[metosin/jsonista "0.2.6"]
[ring/ring-jetty-adapter "1.7.1"]
[metosin/reitit "0.5.18"]]
:repl-options {:init-ns example.server}
:profiles {:dev {:dependencies [[ring/ring-mock "0.3.2"]]}})

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 KiB

View file

@ -0,0 +1,123 @@
(ns example.server
(:require [reitit.ring :as ring]
[reitit.coercion.malli]
[reitit.ring.malli]
[reitit.swagger :as swagger]
[reitit.swagger-ui :as swagger-ui]
[reitit.ring.coercion :as coercion]
[reitit.dev.pretty :as pretty]
[reitit.ring.middleware.muuntaja :as muuntaja]
[reitit.ring.middleware.exception :as exception]
[reitit.ring.middleware.multipart :as multipart]
[reitit.ring.middleware.parameters :as parameters]
; [reitit.ring.middleware.dev :as dev]
; [reitit.ring.spec :as spec]
; [spec-tools.spell :as spell]
[ring.adapter.jetty :as jetty]
[muuntaja.core :as m]
[clojure.java.io :as io]
[malli.util :as mu]))
(def app
(ring/ring-handler
(ring/router
[["/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 {:file reitit.ring.malli/temp-file-part}}
:responses {200 {:body {:name :string, :size :int}}}
:handler (fn [{{{:keys [file]} :multipart} :parameters}]
{:status 200
:body {:name (:filename file)
:size (:size file)}})}}]
["/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 {:x [:int {:title "X parameter"
:description "Description for X parameter"
:json-schema/default 42}]
:y :int}}
:responses {200 {:body {:total :int}}}
:handler (fn [{{{:keys [x y]} :query} :parameters}]
{:status 200
:body {:total (+ x y)}})}
:post {:summary "plus with malli body parameters"
:parameters {:body {:x [:int {:title "X parameter"
:description "Description for X parameter"
:json-schema/default 42}]
:y :int}}
:responses {200 {:body {:total :int}}}
:handler (fn [{{{:keys [x y]} :body} :parameters}]
{:status 200
:body {:total (+ x y)}})}}]]]
{;;:reitit.middleware/transform dev/print-request-diffs ;; pretty diffs
;;:validate spec/validate ;; enable spec validation for route data
;;:reitit.spec/wrap spell/closed ;; strict top-level validation
:exception pretty/exception
:data {:coercion (reitit.coercion.malli/create
{;; set of keys to include in error messages
:error-keys #{#_:type :coercion :in :schema :value :errors :humanized #_:transformed}
;; schema identity function (default: close all map schemas)
:compile mu/closed-schema
;; strip-extra-keys (effects only predefined transformers)
:strip-extra-keys true
;; add/set default values
:default-values true
;; malli options
:options nil})
:muuntaja m/instance
:middleware [;; swagger feature
swagger/swagger-feature
;; query-params & form-params
parameters/parameters-middleware
;; content-negotiation
muuntaja/format-negotiate-middleware
;; encoding response body
muuntaja/format-response-middleware
;; exception handling
exception/exception-middleware
;; decoding request body
muuntaja/format-request-middleware
;; coercing response bodys
coercion/coerce-response-middleware
;; coercing request parameters
coercion/coerce-request-middleware
;; multipart
multipart/multipart-middleware]}})
(ring/routes
(swagger-ui/create-swagger-ui-handler
{:path "/"
:config {:validatorUrl nil
:operationsSorter "alpha"}})
(ring/create-default-handler))))
(defn start []
(jetty/run-jetty #'app {:port 3000, :join? false})
(println "server running in port 3000"))
(comment
(start))

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

View file

@ -0,0 +1,38 @@
(ns example.server-test
(:require [clojure.test :refer [deftest testing is]]
[example.server :refer [app]]
[ring.mock.request :refer [request json-body]]))
(deftest example-server
(testing "GET"
(is (= (-> (request :get "/math/plus?x=20&y=3")
app :body slurp)
(-> {:request-method :get :uri "/math/plus" :query-string "x=20&y=3"}
app :body slurp)
(-> {:request-method :get :uri "/math/plus" :query-params {:x 20 :y 3}}
app :body slurp)
"{\"total\":23}")))
(testing "POST"
(is (= (-> (request :post "/math/plus") (json-body {:x 40 :y 2})
app :body slurp)
(-> {:request-method :post :uri "/math/plus" :body-params {:x 40 :y 2}}
app :body slurp)
"{\"total\":42}")))
(testing "Download"
(is (= (-> {:request-method :get :uri "/files/download"}
app :body (#(slurp % :encoding "ascii")) count) ;; binary
(.length (clojure.java.io/file "resources/reitit.png"))
506325)))
(testing "Upload"
(let [file (clojure.java.io/file "resources/reitit.png")
multipart-temp-file-part {:tempfile file
:size (.length file)
:filename (.getName file)
:content-type "image/png;"}]
(is (= (-> {:request-method :post :uri "/files/upload" :multipart-params {:file multipart-temp-file-part}}
app :body slurp)
"{\"name\":\"reitit.png\",\"size\":506325}")))))

View file

@ -3,6 +3,6 @@
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.10.0"]
[metosin/jsonista "0.2.6"] [metosin/jsonista "0.2.6"]
[ring/ring-jetty-adapter "1.7.1"] [ring/ring-jetty-adapter "1.7.1"]
[metosin/reitit "0.5.15"]] [metosin/reitit "0.5.18"]]
:repl-options {:init-ns example.server} :repl-options {:init-ns example.server}
:profiles {:dev {:dependencies [[ring/ring-mock "0.3.2"]]}}) :profiles {:dev {:dependencies [[ring/ring-mock "0.3.2"]]}})

View file

@ -2,6 +2,6 @@
:description "Reitit Ring App with Swagger" :description "Reitit Ring App with Swagger"
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.10.0"]
[ring/ring-jetty-adapter "1.7.1"] [ring/ring-jetty-adapter "1.7.1"]
[metosin/reitit "0.5.15"]] [metosin/reitit "0.5.18"]]
:repl-options {:init-ns example.server} :repl-options {:init-ns example.server}
:profiles {:dev {:dependencies [[ring/ring-mock "0.3.2"]]}}) :profiles {:dev {:dependencies [[ring/ring-mock "0.3.2"]]}})

View file

@ -2,5 +2,5 @@
:description "Reitit Ring App with Swagger" :description "Reitit Ring App with Swagger"
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.10.0"]
[ring/ring-jetty-adapter "1.7.1"] [ring/ring-jetty-adapter "1.7.1"]
[metosin/reitit "0.5.15"]] [metosin/reitit "0.5.18"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -0,0 +1 @@
../../../.clj-kondo/module_config.edn

View file

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

View file

@ -39,38 +39,47 @@
:body (->ParameterCoercion :body-params :body false false) :body (->ParameterCoercion :body-params :body false false)
:form (->ParameterCoercion :form-params :string true true) :form (->ParameterCoercion :form-params :string true true)
: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)
:fragment (->ParameterCoercion :fragment-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
(if serialize-failed-result
(str "Request coercion failed: " (pr-str result)) (str "Request coercion failed: " (pr-str result))
(merge "Request coercion failed")
(into {} result) (-> {}
{:type ::request-coercion transient
:coercion coercion (as-> $ (reduce conj! $ result))
:value value (assoc! :type ::request-coercion)
:in [:request in] (assoc! :coercion coercion)
:request request})))) (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
(if serialize-failed-result
(str "Response coercion failed: " (pr-str result)) (str "Response coercion failed: " (pr-str result))
(merge "Response coercion failed")
(into {} result) (-> {}
{:type ::response-coercion transient
:coercion coercion (as-> $ (reduce conj! $ result))
:value value (assoc! :type ::response-coercion)
:in [:response :body] (assoc! :coercion coercion)
:request request (assoc! :value value)
:response response})))) (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 +92,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 +107,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]
@ -109,9 +118,9 @@
(defn coerce-request [coercers request] (defn coerce-request [coercers request]
(reduce-kv (reduce-kv
(fn [acc k coercer] (fn [acc k coercer]
(impl/fast-assoc acc k (coercer request))) (impl/fast-assoc acc k (coercer request)))
{} coercers)) {} coercers))
(defn coerce-response [coercers request response] (defn coerce-response [coercers request response]
(if response (if response
@ -147,13 +156,13 @@
:multipart :formData}] :multipart :formData}]
(case specification (case specification
:swagger (->> (update :swagger (->> (update
data data
:parameters :parameters
(fn [parameters] (fn [parameters]
(->> parameters (->> parameters
(map (fn [[k v]] [(swagger-parameter k) v])) (map (fn [[k v]] [(swagger-parameter k) v]))
(filter first) (filter first)
(into {})))) (into {}))))
(-get-apidocs coercion specification))))) (-get-apidocs coercion specification)))))
;; ;;

View file

@ -1,6 +1,6 @@
(ns reitit.core (ns reitit.core
(:require [reitit.impl :as impl] (:require [reitit.exception :as exception]
[reitit.exception :as exception] [reitit.impl :as impl]
[reitit.trie :as trie])) [reitit.trie :as trie]))
;; ;;
@ -61,7 +61,7 @@
(if-not (partial-match? match) (if-not (partial-match? match)
match match
(impl/throw-on-missing-path-params (impl/throw-on-missing-path-params
(:template match) (:required match) path-params))))) (:template match) (:required match) path-params)))))
(defn match->path (defn match->path
([match] ([match]
@ -87,15 +87,15 @@
(let [compiler (::trie/trie-compiler opts (trie/compiler)) (let [compiler (::trie/trie-compiler opts (trie/compiler))
names (impl/find-names compiled-routes opts) names (impl/find-names compiled-routes opts)
[pl nl] (reduce [pl nl] (reduce
(fn [[pl nl] [p {:keys [name] :as data} result]] (fn [[pl nl] [p {:keys [name] :as data} result]]
(let [{:keys [path-params] :as route} (impl/parse p opts) (let [{:keys [path-params] :as route} (impl/parse p opts)
f #(if-let [path (impl/path-for route %)] f #(if-let [path (impl/path-for route %)]
(->Match p data result (impl/url-decode-coll %) path) (->Match p data result (impl/url-decode-coll %) path)
(->PartialMatch p data result (impl/url-decode-coll %) path-params))] (->PartialMatch p data result (impl/url-decode-coll %) path-params))]
[(conj pl (-> (trie/insert nil p (->Match p data result nil nil) opts) (trie/compile))) [(conj pl (-> (trie/insert nil p (->Match p data result nil nil) opts) (trie/compile)))
(if name (assoc nl name f) nl)])) (if name (assoc nl name f) nl)]))
[[] {}] [[] {}]
compiled-routes) compiled-routes)
lookup (impl/fast-map nl) lookup (impl/fast-map nl)
matcher (trie/linear-matcher compiler pl true) matcher (trie/linear-matcher compiler pl true)
match-by-path (trie/path-matcher matcher compiler) match-by-path (trie/path-matcher matcher compiler)
@ -103,16 +103,11 @@
^{:type ::router} ^{:type ::router}
(reify (reify
Router Router
(router-name [_] (router-name [_] :linear-router)
:linear-router) (routes [_] routes)
(routes [_] (compiled-routes [_] compiled-routes)
routes) (options [_] opts)
(compiled-routes [_] (route-names [_] names)
compiled-routes)
(options [_]
opts)
(route-names [_]
names)
(match-by-path [_ path] (match-by-path [_ path]
(if-let [match (match-by-path path)] (if-let [match (match-by-path path)]
(-> (:data match) (-> (:data match)
@ -133,33 +128,28 @@
([compiled-routes opts] ([compiled-routes opts]
(when-let [wilds (seq (filter (impl/->wild-route? opts) compiled-routes))] (when-let [wilds (seq (filter (impl/->wild-route? opts) compiled-routes))]
(exception/fail! (exception/fail!
(str "can't create :lookup-router with wildcard routes: " wilds) (str "can't create :lookup-router with wildcard routes: " wilds)
{:wilds wilds {:wilds wilds
:routes compiled-routes})) :routes compiled-routes}))
(let [names (impl/find-names compiled-routes opts) (let [names (impl/find-names compiled-routes opts)
[pl nl] (reduce [pl nl] (reduce
(fn [[pl nl] [p {:keys [name] :as data} result]] (fn [[pl nl] [p {:keys [name] :as data} result]]
[(assoc pl p (->Match p data result {} p)) [(assoc pl p (->Match p data result {} p))
(if name (if name
(assoc nl name #(->Match p data result % p)) (assoc nl name #(->Match p data result % p))
nl)]) nl)])
[{} {}] [{} {}]
compiled-routes) compiled-routes)
data (impl/fast-map pl) data (impl/fast-map pl)
lookup (impl/fast-map nl) lookup (impl/fast-map nl)
routes (impl/uncompile-routes compiled-routes)] routes (impl/uncompile-routes compiled-routes)]
^{:type ::router} ^{:type ::router}
(reify Router (reify Router
(router-name [_] (router-name [_] :lookup-router)
:lookup-router) (routes [_] routes)
(routes [_] (compiled-routes [_] compiled-routes)
routes) (options [_] opts)
(compiled-routes [_] (route-names [_] names)
compiled-routes)
(options [_]
opts)
(route-names [_]
names)
(match-by-path [_ path] (match-by-path [_ path]
(impl/fast-get data path)) (impl/fast-get data path))
(match-by-name [_ name] (match-by-name [_ name]
@ -183,34 +173,29 @@
(let [compiler (::trie/trie-compiler opts (trie/compiler)) (let [compiler (::trie/trie-compiler opts (trie/compiler))
names (impl/find-names compiled-routes opts) names (impl/find-names compiled-routes opts)
[pl nl] (reduce [pl nl] (reduce
(fn [[pl nl] [p {:keys [name] :as data} result]] (fn [[pl nl] [p {:keys [name] :as data} result]]
(let [{:keys [path-params] :as route} (impl/parse p opts) (let [{:keys [path-params] :as route} (impl/parse p opts)
f #(if-let [path (impl/path-for route %)] f #(if-let [path (impl/path-for route %)]
(->Match p data result (impl/url-decode-coll %) path) (->Match p data result (impl/url-decode-coll %) path)
(->PartialMatch p data result (impl/url-decode-coll %) path-params))] (->PartialMatch p data result (impl/url-decode-coll %) path-params))]
[(trie/insert pl p (->Match p data result nil nil) opts) [(trie/insert pl p (->Match p data result nil nil) opts)
(if name (assoc nl name f) nl)])) (if name (assoc nl name f) nl)]))
[nil {}] [nil {}]
compiled-routes) compiled-routes)
matcher (trie/compile pl compiler) matcher (trie/compile pl compiler)
match-by-path (trie/path-matcher matcher compiler) match-by-path (if matcher (trie/path-matcher matcher compiler))
lookup (impl/fast-map nl) lookup (impl/fast-map nl)
routes (impl/uncompile-routes compiled-routes)] routes (impl/uncompile-routes compiled-routes)]
^{:type ::router} ^{:type ::router}
(reify (reify
Router Router
(router-name [_] (router-name [_] :trie-router)
:trie-router) (routes [_] routes)
(routes [_] (compiled-routes [_] compiled-routes)
routes) (options [_] opts)
(compiled-routes [_] (route-names [_] names)
compiled-routes)
(options [_]
opts)
(route-names [_]
names)
(match-by-path [_ path] (match-by-path [_ path]
(if-let [match (match-by-path path)] (if-let [match (and match-by-path (match-by-path path))]
(-> (:data match) (-> (:data match)
(assoc :path-params (:params match)) (assoc :path-params (:params match))
(assoc :path path)))) (assoc :path path))))
@ -229,8 +214,8 @@
([compiled-routes opts] ([compiled-routes opts]
(when (or (not= (count compiled-routes) 1) (some (impl/->wild-route? opts) compiled-routes)) (when (or (not= (count compiled-routes) 1) (some (impl/->wild-route? opts) compiled-routes))
(exception/fail! (exception/fail!
(str ":single-static-path-router requires exactly 1 static route: " compiled-routes) (str ":single-static-path-router requires exactly 1 static route: " compiled-routes)
{:routes compiled-routes})) {:routes compiled-routes}))
(let [[n :as names] (impl/find-names compiled-routes opts) (let [[n :as names] (impl/find-names compiled-routes opts)
[[p data result]] compiled-routes [[p data result]] compiled-routes
p #?(:clj (.intern ^String p) :cljs p) p #?(:clj (.intern ^String p) :cljs p)
@ -238,25 +223,17 @@
routes (impl/uncompile-routes compiled-routes)] routes (impl/uncompile-routes compiled-routes)]
^{:type ::router} ^{:type ::router}
(reify Router (reify Router
(router-name [_] (router-name [_] :single-static-path-router)
:single-static-path-router) (routes [_] routes)
(routes [_] (compiled-routes [_] compiled-routes)
routes) (options [_] opts)
(compiled-routes [_] (route-names [_] names)
compiled-routes)
(options [_]
opts)
(route-names [_]
names)
(match-by-path [_ path] (match-by-path [_ path]
(if (#?(:clj .equals :cljs =) p path) (if (#?(:clj .equals :cljs =) p path) match))
match))
(match-by-name [_ name] (match-by-name [_ name]
(if (= n name) (if (= n name) match))
match))
(match-by-name [_ name path-params] (match-by-name [_ name path-params]
(if (= n name) (if (= n name) (impl/fast-assoc match :path-params (impl/path-params path-params))))))))
(impl/fast-assoc match :path-params (impl/path-params path-params))))))))
(defn mixed-router (defn mixed-router
"Creates two routers: [[lookup-router]] or [[single-static-path-router]] for "Creates two routers: [[lookup-router]] or [[single-static-path-router]] for
@ -274,16 +251,11 @@
routes (impl/uncompile-routes compiled-routes)] routes (impl/uncompile-routes compiled-routes)]
^{:type ::router} ^{:type ::router}
(reify Router (reify Router
(router-name [_] (router-name [_] :mixed-router)
:mixed-router) (routes [_] routes)
(routes [_] (compiled-routes [_] compiled-routes)
routes) (options [_] opts)
(compiled-routes [_] (route-names [_] names)
compiled-routes)
(options [_]
opts)
(route-names [_]
names)
(match-by-path [_ path] (match-by-path [_ path]
(or (match-by-path static-router path) (or (match-by-path static-router path)
(match-by-path wildcard-router path))) (match-by-path wildcard-router path)))
@ -310,16 +282,11 @@
routes (impl/uncompile-routes compiled-routes)] routes (impl/uncompile-routes compiled-routes)]
^{:type ::router} ^{:type ::router}
(reify Router (reify Router
(router-name [_] (router-name [_] :quarantine-router)
:quarantine-router) (routes [_] routes)
(routes [_] (compiled-routes [_] compiled-routes)
routes) (options [_] opts)
(compiled-routes [_] (route-names [_] names)
compiled-routes)
(options [_]
opts)
(route-names [_]
names)
(match-by-path [_ path] (match-by-path [_ path]
(or (match-by-path mixed-router path) (or (match-by-path mixed-router path)
(match-by-path linear-router path))) (match-by-path linear-router path)))

View file

@ -10,8 +10,8 @@
(map (fn [provide] (map (fn [provide]
(when (contains? acc provide) (when (contains? acc provide)
(exception/fail! (exception/fail!
(str "multiple providers for: " provide) (str "multiple providers for: " provide)
{::multiple-providers provide})) {::multiple-providers provide}))
[provide dependent])) [provide dependent]))
(get-provides dependent))) (get-provides dependent)))
{} nodes)) {} nodes))
@ -22,8 +22,8 @@
(if (contains? providers k) (if (contains? providers k)
(get providers k) (get providers k)
(exception/fail! (exception/fail!
(str "provider missing for dependency: " k) (str "provider missing for dependency: " k)
{::missing-provider k}))) {::missing-provider k})))
(defn post-order (defn post-order
"Put `nodes` in post-order. Can also be described as a reverse topological sort. "Put `nodes` in post-order. Can also be described as a reverse topological sort.

View file

@ -31,20 +31,20 @@
path " " (not-empty (select-keys route-data [:conflicting]))))] path " " (not-empty (select-keys route-data [:conflicting]))))]
(apply str "Router contains conflicting route paths:\n\n" (apply str "Router contains conflicting route paths:\n\n"
(mapv (mapv
(fn [[[path route-data] vals]] (fn [[[path route-data] vals]]
(str (resolve-str path route-data) (str (resolve-str path route-data)
"\n" "\n"
(str/join "\n" (mapv (fn [[path route-data]] (str/join "\n" (mapv (fn [[path route-data]]
(resolve-str path route-data)) vals)) (resolve-str path route-data)) vals))
"\n\n")) "\n\n"))
conflicts)))) conflicts))))
(defmethod format-exception :name-conflicts [_ _ conflicts] (defmethod format-exception :name-conflicts [_ _ conflicts]
(apply str "Router contains conflicting route names:\n\n" (apply str "Router contains conflicting route names:\n\n"
(mapv (mapv
(fn [[name vals]] (fn [[name vals]]
(str name "\n-> " (str/join "\n-> " (mapv first vals)) "\n")) (str name "\n-> " (str/join "\n-> " (mapv first vals)) "\n"))
conflicts))) conflicts)))
(defmethod format-exception :reitit.impl/merge-data [_ _ data] (defmethod format-exception :reitit.impl/merge-data [_ _ data]
(str "Error merging route-data\n\n" (pr-str data))) (str "Error merging route-data\n\n" (pr-str data)))

View file

@ -1,13 +1,13 @@
(ns ^:no-doc reitit.impl (ns ^:no-doc reitit.impl
#?(:cljs (:require-macros [reitit.impl])) #?(:cljs (:require-macros [reitit.impl]))
(:require [clojure.string :as str] (:require [clojure.set :as set]
[clojure.set :as set] [clojure.string :as str]
[meta-merge.core :as mm] [meta-merge.core :as mm]
[reitit.trie :as trie] [reitit.exception :as ex]
[reitit.exception :as ex]) [reitit.trie :as trie])
#?(:clj #?(:clj
(:import (java.util HashMap Map) (:import (java.net URLEncoder URLDecoder)
(java.net URLEncoder URLDecoder)))) (java.util HashMap Map))))
(defn parse [path opts] (defn parse [path opts]
(let [path #?(:clj (.intern ^String (trie/normalize path opts)) :cljs (trie/normalize path opts)) (let [path #?(:clj (.intern ^String (trie/normalize path opts)) :cljs (trie/normalize path opts))
@ -28,59 +28,59 @@
Also works on vectors. Maintains key for maps, order for vectors." Also works on vectors. Maintains key for maps, order for vectors."
[f coll] [f coll]
(reduce-kv (reduce-kv
(fn [coll k v] (fn [coll k v]
(if-some [v' (f v)] (if-some [v' (f v)]
(assoc coll k v') (assoc coll k v')
coll)) coll))
coll coll
coll)) coll))
(defn walk [raw-routes {:keys [path data routes expand] (defn walk [raw-routes {:keys [path data routes expand]
:or {data [], routes []} :or {data [], routes []}
:as opts}] :as opts}]
(letfn (letfn
[(walk-many [p m r] [(walk-many [p m r]
(reduce #(into %1 (walk-one p m %2)) [] r)) (reduce #(into %1 (walk-one p m %2)) [] r))
(walk-one [pacc macc routes] (walk-one [pacc macc routes]
(if (vector? (first routes)) (if (vector? (first routes))
(walk-many pacc macc routes) (walk-many pacc macc routes)
(when (string? (first routes)) (when (string? (first routes))
(let [[path & [maybe-arg :as args]] routes (let [[path & [maybe-arg :as args]] routes
[data childs] (if (or (vector? maybe-arg) [data childs] (if (or (vector? maybe-arg)
(and (sequential? maybe-arg) (and (sequential? maybe-arg)
(sequential? (first maybe-arg))) (sequential? (first maybe-arg)))
(nil? maybe-arg)) (nil? maybe-arg))
[{} args] [{} args]
[maybe-arg (rest args)]) [maybe-arg (rest args)])
macc (into macc (expand data opts)) macc (into macc (expand data opts))
child-routes (walk-many (str pacc path) macc (keep identity childs))] child-routes (walk-many (str pacc path) macc (keep identity childs))]
(if (seq childs) (seq child-routes) [[(str pacc path) macc]])))))] (if (seq childs) (seq child-routes) [[(str pacc path) macc]])))))]
(walk-one path (mapv identity data) raw-routes))) (walk-one path (mapv identity data) raw-routes)))
(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 merge-data [{:keys [meta-merge-fn] :as g} p x]
(reduce (reduce
(fn [acc [k v]] (fn [acc [k v]]
(try (try
(mm/meta-merge acc {k v}) ((or meta-merge-fn mm/meta-merge) acc {k v})
(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]
(let [parts-and-routes (mapv (fn [[s :as r]] [(trie/split-path s opts) r]) routes)] (let [parts-and-routes (mapv (fn [[s :as r]] [(trie/split-path s opts) r]) routes)]
(-> (into {} (comp (map-indexed (fn [index [p r]] (-> (into {} (comp (map-indexed (fn [index [p r]]
[r (reduce [r (reduce
(fn [acc [p' r']] (fn [acc [p' r']]
(if (trie/conflicting-parts? p p') (if (trie/conflicting-parts? p p')
(conj acc r') acc)) (conj acc r') acc))
#{} (subvec parts-and-routes (inc index)))])) #{} (subvec parts-and-routes (inc index)))]))
(filter (comp seq second))) parts-and-routes) (filter (comp seq second))) parts-and-routes)
(not-empty)))) (not-empty))))
@ -123,13 +123,13 @@
(defn path-for [route path-params] (defn path-for [route path-params]
(if (:path-params route) (if (:path-params route)
(if-let [parts (reduce (if-let [parts (reduce
(fn [acc part] (fn [acc part]
(if (string? part) (if (string? part)
(conj acc part) (conj acc part)
(if-let [p (get path-params (:value part))] (if-let [p (get path-params (:value part))]
(conj acc p) (conj acc p)
(reduced nil)))) (reduced nil))))
[] (:path-parts route))] [] (:path-parts route))]
(apply str parts)) (apply str parts))
(:path route))) (:path route)))
@ -138,8 +138,8 @@
(let [defined (-> path-params keys set) (let [defined (-> path-params keys set)
missing (set/difference required defined)] missing (set/difference required defined)]
(ex/fail! (ex/fail!
(str "missing path-params for route " template " -> " missing) (str "missing path-params for route " template " -> " missing)
{:path-params path-params, :required required})))) {:path-params path-params, :required required}))))
(defn fast-assoc (defn fast-assoc
#?@(:clj [[^clojure.lang.Associative a k v] (.assoc a k v)] #?@(:clj [[^clojure.lang.Associative a k v] (.assoc a k v)]
@ -178,10 +178,10 @@
(if s (if s
#?(:clj (if (.contains ^String s "%") #?(:clj (if (.contains ^String s "%")
(URLDecoder/decode (URLDecoder/decode
(if (.contains ^String s "+") (if (.contains ^String s "+")
(.replace ^String s "+" "%2B") (.replace ^String s "+" "%2B")
^String s) ^String s)
"UTF-8")) "UTF-8"))
:cljs (js/decodeURIComponent s)))) :cljs (js/decodeURIComponent s))))
(defn url-decode [s] (defn url-decode [s]
@ -249,6 +249,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 "&")))

View file

@ -1,9 +1,9 @@
(ns reitit.interceptor (ns reitit.interceptor
(:require [meta-merge.core :refer [meta-merge]] (:require [clojure.pprint :as pprint]
[clojure.pprint :as pprint] [meta-merge.core :refer [meta-merge]]
[reitit.core :as r] [reitit.core :as r]
[reitit.impl :as impl] [reitit.exception :as exception]
[reitit.exception :as exception])) [reitit.impl :as impl]))
(defprotocol IntoInterceptor (defprotocol IntoInterceptor
(into-interceptor [this data opts])) (into-interceptor [this data opts]))
@ -42,25 +42,25 @@
(if-let [interceptor (if registry (registry this))] (if-let [interceptor (if registry (registry this))]
(into-interceptor interceptor data opts) (into-interceptor interceptor data opts)
(throw (throw
(ex-info (ex-info
(str (str
"Interceptor " this " not found in registry.\n\n" "Interceptor " this " not found in registry.\n\n"
(if (seq registry) (if (seq registry)
(str (str
"Available interceptors in registry:\n" "Available interceptors in registry:\n"
(with-out-str (with-out-str
(pprint/print-table [:id :description] (for [[k v] registry] {:id k :description v})))) (pprint/print-table [:id :description] (for [[k v] registry] {:id k :description v}))))
"see [reitit.interceptor/router] on how to add interceptor to the registry.\n") "\n") "see [reitit.interceptor/router] on how to add interceptor to the registry.\n") "\n")
{:id this {:id this
:registry registry})))) :registry registry}))))
#?(:clj clojure.lang.APersistentVector #?(:clj clojure.lang.APersistentVector
:cljs cljs.core.PersistentVector) :cljs cljs.core.PersistentVector)
(into-interceptor [[f & args :as form] data opts] (into-interceptor [[f & args :as form] data opts]
(when (and (seq args) (not (fn? f))) (when (and (seq args) (not (fn? f)))
(exception/fail! (exception/fail!
(str "Invalid Interceptor form: " form "") (str "Invalid Interceptor form: " form "")
{:form form})) {:form form}))
(into-interceptor (apply f args) data opts)) (into-interceptor (apply f args) data opts))
#?(:clj clojure.lang.Fn #?(:clj clojure.lang.Fn
@ -91,13 +91,13 @@
opts (assoc opts ::compiled (inc ^long compiled))] opts (assoc opts ::compiled (inc ^long compiled))]
(when (>= ^long compiled ^long *max-compile-depth*) (when (>= ^long compiled ^long *max-compile-depth*)
(exception/fail! (exception/fail!
(str "Too deep Interceptor compilation - " compiled) (str "Too deep Interceptor compilation - " compiled)
{:this this, :data data, :opts opts})) {:this this, :data data, :opts opts}))
(if-let [interceptor (into-interceptor (compile data opts) data opts)] (if-let [interceptor (into-interceptor (compile data opts) data opts)]
(map->Interceptor (map->Interceptor
(merge (merge
(dissoc this :compile) (dissoc this :compile)
(impl/strip-nils interceptor))))))) (impl/strip-nils interceptor)))))))
nil nil
(into-interceptor [_ _ _])) (into-interceptor [_ _ _]))
@ -127,9 +127,9 @@
([[_ {:keys [interceptors handler] :as data}] {::keys [queue] :as opts} _] ([[_ {:keys [interceptors handler] :as data}] {::keys [queue] :as opts} _]
(let [chain (chain (into (vec interceptors) [handler]) data opts)] (let [chain (chain (into (vec interceptors) [handler]) data opts)]
(map->Endpoint (map->Endpoint
{:interceptors chain {:interceptors chain
:queue ((or queue identity) chain) :queue ((or queue identity) chain)
:data data})))) :data data}))))
(defn transform-butlast (defn transform-butlast
"Returns a function to that takes a interceptor transformation function and "Returns a function to that takes a interceptor transformation function and
@ -137,8 +137,8 @@
[f] [f]
(fn [interceptors] (fn [interceptors]
(concat (concat
(f (butlast interceptors)) (f (butlast interceptors))
[(last interceptors)]))) [(last interceptors)])))
(defn router (defn router
"Creates a [[reitit.core/Router]] from raw route data and optionally an options map with "Creates a [[reitit.core/Router]] from raw route data and optionally an options map with
@ -160,8 +160,8 @@
:handler get-user}]])" :handler get-user}]])"
([data] ([data]
(router data nil)) (router data nil))
([data opts] ([data {:keys [meta-merge-fn] :as opts}]
(let [opts (meta-merge {:compile compile-result} opts)] (let [opts ((or meta-merge-fn meta-merge) {:compile compile-result} opts)]
(r/router data opts)))) (r/router data opts))))
(defn interceptor-handler [router] (defn interceptor-handler [router]

View file

@ -1,9 +1,9 @@
(ns reitit.middleware (ns reitit.middleware
(:require [meta-merge.core :refer [meta-merge]] (:require [clojure.pprint :as pprint]
[clojure.pprint :as pprint] [meta-merge.core :refer [meta-merge]]
[reitit.core :as r] [reitit.core :as r]
[reitit.impl :as impl] [reitit.exception :as exception]
[reitit.exception :as exception])) [reitit.impl :as impl]))
(defprotocol IntoMiddleware (defprotocol IntoMiddleware
(into-middleware [this data opts])) (into-middleware [this data opts]))
@ -21,17 +21,17 @@
(if-let [middleware (if registry (registry this))] (if-let [middleware (if registry (registry this))]
(into-middleware middleware data opts) (into-middleware middleware data opts)
(throw (throw
(ex-info (ex-info
(str (str
"Middleware " this " not found in registry.\n\n" "Middleware " this " not found in registry.\n\n"
(if (seq registry) (if (seq registry)
(str (str
"Available middleware in registry:\n" "Available middleware in registry:\n"
(with-out-str (with-out-str
(pprint/print-table [:id :description] (for [[k v] registry] {:id k :description v})))) (pprint/print-table [:id :description] (for [[k v] registry] {:id k :description v}))))
"see [reitit.middleware/router] on how to add middleware to the registry.\n") "\n") "see [reitit.middleware/router] on how to add middleware to the registry.\n") "\n")
{:id this {:id this
:registry registry})))) :registry registry}))))
#?(:clj clojure.lang.APersistentVector #?(:clj clojure.lang.APersistentVector
:cljs cljs.core.PersistentVector) :cljs cljs.core.PersistentVector)
@ -43,7 +43,7 @@
:cljs function) :cljs function)
(into-middleware [this _ _] (into-middleware [this _ _]
(map->Middleware (map->Middleware
{:wrap this})) {:wrap this}))
#?(:clj clojure.lang.PersistentArrayMap #?(:clj clojure.lang.PersistentArrayMap
:cljs cljs.core.PersistentArrayMap) :cljs cljs.core.PersistentArrayMap)
@ -63,13 +63,13 @@
opts (assoc opts ::compiled (inc ^long compiled))] opts (assoc opts ::compiled (inc ^long compiled))]
(when (>= ^long compiled ^long *max-compile-depth*) (when (>= ^long compiled ^long *max-compile-depth*)
(exception/fail! (exception/fail!
(str "Too deep Middleware compilation - " compiled) (str "Too deep Middleware compilation - " compiled)
{:this this, :data data, :opts opts})) {:this this, :data data, :opts opts}))
(if-let [middeware (into-middleware (compile data opts) data opts)] (if-let [middeware (into-middleware (compile data opts) data opts)]
(map->Middleware (map->Middleware
(merge (merge
(dissoc this :compile) (dissoc this :compile)
(impl/strip-nils middeware))))))) (impl/strip-nils middeware)))))))
nil nil
(into-middleware [_ _ _])) (into-middleware [_ _ _]))
@ -77,10 +77,10 @@
(defn- ensure-handler! [path data scope] (defn- ensure-handler! [path data scope]
(when-not (:handler data) (when-not (:handler data)
(exception/fail! (exception/fail!
(str "path \"" path "\" doesn't have a :handler defined" (str "path \"" path "\" doesn't have a :handler defined"
(if scope (str " for " scope))) (if scope (str " for " scope)))
(merge {:path path, :data data} (merge {:path path, :data data}
(if scope {:scope scope}))))) (if scope {:scope scope})))))
(defn- expand-and-transform (defn- expand-and-transform
[middleware data {::keys [transform] :or {transform identity} :as opts}] [middleware data {::keys [transform] :or {transform identity} :as opts}]
@ -116,9 +116,9 @@
(ensure-handler! path data scope) (ensure-handler! path data scope)
(let [middleware (expand-and-transform middleware data opts)] (let [middleware (expand-and-transform middleware data opts)]
(map->Endpoint (map->Endpoint
{:handler (compile-handler middleware handler) {:handler (compile-handler middleware handler)
:middleware middleware :middleware middleware
:data data})))) :data data}))))
(defn router (defn router
"Creates a [[reitit.core/Router]] from raw route data and optionally an options map with "Creates a [[reitit.core/Router]] from raw route data and optionally an options map with
@ -138,8 +138,8 @@
:handler get-user}]])" :handler get-user}]])"
([data] ([data]
(router data nil)) (router data nil))
([data opts] ([data {:keys [meta-merge-fn] :as opts}]
(let [opts (meta-merge {:compile compile-result} opts)] (let [opts ((or meta-merge-fn meta-merge) {:compile compile-result} opts)]
(r/router data opts)))) (r/router data opts))))
(defn middleware-handler [router] (defn middleware-handler [router]

View file

@ -1,8 +1,8 @@
(ns reitit.spec (ns reitit.spec
(:require [clojure.spec.alpha :as s] (:require [clojure.spec.alpha :as s]
[clojure.spec.gen.alpha :as gen] [clojure.spec.gen.alpha :as gen]
[reitit.exception :as exception] [reitit.core :as r]
[reitit.core :as r])) [reitit.exception :as exception]))
;; ;;
;; routes ;; routes
@ -16,9 +16,9 @@
(s/def ::raw-route (s/def ::raw-route
(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/and (s/nilable ::raw-routes))))))
(s/def ::raw-routes (s/def ::raw-routes
(s/or :route ::raw-route (s/or :route ::raw-route
@ -60,19 +60,19 @@
(s/def ::opts (s/def ::opts
(s/nilable (s/nilable
(s/keys :opt-un [:reitit.router/path (s/keys :opt-un [:reitit.router/path
:reitit.router/routes :reitit.router/routes
:reitit.router/data :reitit.router/data
:reitit.router/expand :reitit.router/expand
:reitit.router/coerce :reitit.router/coerce
:reitit.router/compile :reitit.router/compile
:reitit.router/conflicts :reitit.router/conflicts
:reitit.router/router]))) :reitit.router/router])))
(s/fdef r/router (s/fdef r/router
:args (s/or :1arity (s/cat :data (s/spec ::raw-routes)) :args (s/or :1arity (s/cat :data (s/spec ::raw-routes))
:2arity (s/cat :data (s/spec ::raw-routes), :opts ::opts)) :2arity (s/cat :data (s/spec ::raw-routes), :opts ::opts))
:ret ::router) :ret ::router)
;; ;;
;; coercion ;; coercion
@ -119,24 +119,25 @@
(defrecord Problem [path scope data spec problems]) (defrecord Problem [path scope data spec problems])
(defn validate-route-data [routes wrap spec] (defn validate-route-data [routes wrap spec]
(let [spec (wrap spec)] (let [spec (wrap spec)
(some->> (for [[p d _] routes] spec-explain (fn [[p d _]]
(when-let [problems (and spec (s/explain-data spec d))] (when-let [problems (and spec (s/explain-data spec d))]
(->Problem p nil d spec problems))) (->Problem p nil d spec problems)))
(keep identity) (seq) (vec)))) errors (into [] (keep spec-explain) routes)]
(when (pos? (count errors)) errors)))
(defn validate [routes {:keys [spec] ::keys [wrap] :or {spec ::default-data, wrap identity}}] (defn validate [routes {:keys [spec] ::keys [wrap] :or {spec ::default-data, wrap identity}}]
(when-let [problems (validate-route-data routes wrap spec)] (when-let [problems (validate-route-data routes wrap spec)]
(exception/fail! (exception/fail!
::invalid-route-data ::invalid-route-data
{:problems problems}))) {:problems problems})))
(defmethod exception/format-exception :reitit.spec/invalid-route-data [_ _ {:keys [problems]}] (defmethod exception/format-exception :reitit.spec/invalid-route-data [_ _ {:keys [problems]}]
(apply str "Invalid route data:\n\n" (apply str "Invalid route data:\n\n"
(mapv (mapv
(fn [{:keys [path scope data spec]}] (fn [{:keys [path scope data spec]}]
(str "-- On route -----------------------\n\n" (str "-- On route -----------------------\n\n"
(pr-str path) (if scope (str " " (pr-str scope))) "\n\n" (pr-str path) (if scope (str " " (pr-str scope))) "\n\n"
(pr-str data) "\n\n" (pr-str data) "\n\n"
(s/explain-str spec data) "\n")) (s/explain-str spec data) "\n"))
problems))) problems)))

View file

@ -2,8 +2,8 @@
(:refer-clojure :exclude [compile]) (:refer-clojure :exclude [compile])
(:require [clojure.string :as str] (:require [clojure.string :as str]
[reitit.exception :as ex]) [reitit.exception :as ex])
#?(:clj (:import [reitit Trie Trie$Match Trie$Matcher] #?(:clj (:import (java.net URLDecoder)
(java.net URLDecoder)))) [reitit Trie Trie$Match Trie$Matcher])))
(defn ^:no-doc into-set [x] (defn ^:no-doc into-set [x]
(cond (cond
@ -90,12 +90,12 @@
(defn join-path [xs] (defn join-path [xs]
(reduce (reduce
(fn [s x] (fn [s x]
(str s (cond (str s (cond
(string? x) x (string? x) x
(instance? Wild x) (str "{" (-> x :value str (subs 1)) "}") (instance? Wild x) (str "{" (-> x :value str (subs 1)) "}")
(instance? CatchAll x) (str "{*" (-> x :value str (subs 1)) "}")))) (instance? CatchAll x) (str "{*" (-> x :value str (subs 1)) "}"))))
"" xs)) "" xs))
(defn normalize [s opts] (defn normalize [s opts]
(-> s (split-path opts) (join-path))) (-> s (split-path opts) (join-path)))
@ -172,25 +172,25 @@
:else :else
(or (or
(reduce (reduce
(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)
n'' (-insert (-node {:children {rp n, rp' n'}}) nil nil nil nil)] n'' (-insert (-node {:children {rp n, rp' n'}}) nil nil nil nil)]
(reduced (update node :children (fn [children] (reduced (update node :children (fn [children]
(-> children (-> children
(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
(-> (merge-with merge (dissoc node' :data) child) (-> (merge-with merge (dissoc node' :data) child)
@ -202,10 +202,10 @@
(if percent? (if percent?
#?(:cljs (js/decodeURIComponent param) #?(:cljs (js/decodeURIComponent param)
:clj (URLDecoder/decode :clj (URLDecoder/decode
(if (.contains ^String param "+") (if (.contains ^String param "+")
(.replace ^String param "+" "%2B") (.replace ^String param "+" "%2B")
param) param)
"UTF-8")) "UTF-8"))
param))) param)))
;; ;;
@ -313,13 +313,13 @@
(def record-parameters (def record-parameters
"Memoized function to transform parameters into runtime generated Record." "Memoized function to transform parameters into runtime generated Record."
(memoize (memoize
(fn [keys] (fn [keys]
(if (some qualified-keyword? keys) (if (some qualified-keyword? keys)
(map-parameters keys) (map-parameters keys)
(let [sym (gensym "PathParams") (let [sym (gensym "PathParams")
ctor (symbol (str "map->" sym))] ctor (symbol (str "map->" sym))]
(binding [*ns* (find-ns 'user)] (binding [*ns* (find-ns 'user)]
(eval `(do (defrecord ~sym ~(mapv (comp symbol name) keys)) (~ctor {})))))))))) (eval `(do (defrecord ~sym ~(mapv (comp symbol name) keys)) (~ctor {}))))))))))
(defn insert (defn insert
"Returns a trie with routes added to it." "Returns a trie with routes added to it."
@ -327,9 +327,9 @@
(insert nil routes)) (insert nil routes))
([node routes] ([node routes]
(reduce (reduce
(fn [acc [p d]] (fn [acc [p d]]
(insert acc p d)) (insert acc p d))
node routes)) node routes))
([node path data] ([node path data]
(insert node path data nil)) (insert node path data nil))
([node path data {::keys [parameters] :or {parameters map-parameters} :as opts}] ([node path data {::keys [parameters] :or {parameters map-parameters} :as opts}]
@ -355,17 +355,16 @@
(cond-> data (conj (data-matcher compiler params data))) (cond-> data (conj (data-matcher compiler params data)))
(into (for [[p c] children] (static-matcher compiler p (compile c compiler (conj cp p))))) (into (for [[p c] children] (static-matcher compiler p (compile c compiler (conj cp p)))))
(into (into
(for [[p c] wilds] (for [[p c] wilds]
(let [pv (:value p) (let [pv (:value p)
ends (ends c)] ends (ends c)]
(if (next ends) (if (next ends)
(ex/fail! ::multiple-terminators {:terminators ends, :path (join-path (conj cp p))}) (ex/fail! ::multiple-terminators {:terminators ends, :path (join-path (conj cp p))})
(wild-matcher compiler pv (ffirst ends) (compile c compiler (conj cp pv))))))) (wild-matcher compiler pv (ffirst ends) (compile c compiler (conj cp pv)))))))
(into (for [[p c] catch-all] (catch-all-matcher compiler (:value p) params (:data c)))))] (into (for [[p c] catch-all] (catch-all-matcher compiler (:value p) params (:data c)))))]
(cond (cond
(> (count matchers) 1) (linear-matcher compiler matchers false) (> (count matchers) 1) (linear-matcher compiler matchers false)
(= (count matchers) 1) (first matchers) (= (count matchers) 1) (first matchers)))))
:else (data-matcher compiler {} nil)))))
(defn pretty (defn pretty
"Returns a simplified EDN structure of a compiled trie for printing purposes." "Returns a simplified EDN structure of a compiled trie for printing purposes."
@ -387,61 +386,61 @@
(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)))

View file

@ -0,0 +1 @@
../../../.clj-kondo/module_config.edn

View file

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

View file

@ -1,18 +1,16 @@
(ns reitit.dev.pretty (ns reitit.dev.pretty
(:require [clojure.string :as str] (:require [arrangement.core] ;; spell-spec
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[reitit.exception :as exception] [clojure.string :as str]
[arrangement.core] [expound.alpha] ;; fipp
;; spell-spec
[spell-spec.expound]
;; expound
[expound.ansi] [expound.ansi]
[expound.alpha]
;; fipp
[fipp.visit]
[fipp.edn] [fipp.edn]
[fipp.ednize] [fipp.ednize]
[fipp.engine])) [fipp.engine]
[fipp.visit]
[reitit.exception :as exception]
[spell-spec.expound] ;; expound
))
;; ;;
;; colors ;; colors
@ -152,13 +150,13 @@
(printer nil)) (printer nil))
([options] ([options]
(map->EdnPrinter (map->EdnPrinter
(merge (merge
{:width 80 {:width 80
:symbols {} :symbols {}
:print-length *print-length* :print-length *print-length*
:print-level *print-level* :print-level *print-level*
:print-meta *print-meta*} :print-meta *print-meta*}
options)))) options))))
(defn pprint (defn pprint
([x] (pprint x {})) ([x] (pprint x {}))
@ -209,13 +207,13 @@
(defn exception-str [message source printer] (defn exception-str [message source printer]
(with-out-str (with-out-str
(print-doc (print-doc
[:group [:group
(title "Router creation failed" source printer) (title "Router creation failed" source printer)
[:break] [:break] [:break] [:break]
message message
[:break] [:break]
(footer printer)] (footer printer)]
printer))) printer)))
(defmulti format-exception (fn [type _ _] type)) (defmulti format-exception (fn [type _ _] type))
@ -231,11 +229,11 @@
(defn de-expound-colors [^String s mappings] (defn de-expound-colors [^String s mappings]
(let [s' (reduce (let [s' (reduce
(fn [s [from to]] (fn [s [from to]]
(.replace ^String s (.replace ^String s
^String (expound.ansi/esc [from]) ^String (expound.ansi/esc [from])
^String (-start (colors to)))) ^String (-start (colors to))))
s mappings)] s mappings)]
(.replace ^String s' (.replace ^String s'
^String (expound.ansi/esc [:none]) ^String (expound.ansi/esc [:none])
(str (expound.ansi/esc [:none]) (-start (colors :text)))))) (str (expound.ansi/esc [:none]) (-start (colors :text))))))
@ -254,9 +252,9 @@
(def expound-printer (def expound-printer
(expound.alpha/custom-printer (expound.alpha/custom-printer
{:theme :figwheel-theme {:theme :figwheel-theme
:show-valid-values? false :show-valid-values? false
:print-specs? false})) :print-specs? false}))
;; ;;
;; Formatters ;; Formatters
@ -276,18 +274,18 @@
" ") " ")
(edn (not-empty (select-keys route-data [:conflicting])))])] (edn (not-empty (select-keys route-data [:conflicting])))])]
(into (into
[:group] [:group]
(mapv (mapv
(fn [[[path route-data] vals]] (fn [[[path route-data] vals]]
[:group [:group
(path-report path route-data) (path-report path route-data)
(into (into
[:group] [:group]
(map (map
(fn [[path route-data]] (path-report path route-data)) (fn [[path route-data]] (path-report path route-data))
vals)) vals))
[:break]]) [:break]])
conflicts))) conflicts)))
[:span (text "Either fix the conflicting paths or disable the conflict resolution") [:span (text "Either fix the conflicting paths or disable the conflict resolution")
[:break] (text "by setting route data for conflicting route: ") [:break] [:break] [:break] (text "by setting route data for conflicting route: ") [:break] [:break]
(edn {:conflicting true} {:margin 3}) (edn {:conflicting true} {:margin 3})
@ -302,19 +300,19 @@
(text "Router contains conflicting route names:") (text "Router contains conflicting route names:")
[:break] [:break] [:break] [:break]
(into (into
[:group] [:group]
(mapv (mapv
(fn [[name vals]] (fn [[name vals]]
[:group [:group
[:span (text name)] [:span (text name)]
[:break] [:break]
(into (into
[:group] [:group]
(map (map
(fn [p] [:span (color :grey "-> " p) [:break]]) (fn [p] [:span (color :grey "-> " p) [:break]])
(mapv first vals))) (mapv first vals)))
[:break]]) [:break]])
conflicts)) conflicts))
(color :white "https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-conflicts") (color :white "https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-conflicts")
[:break]]) [:break]])
@ -323,22 +321,22 @@
(text "Invalid route data:") (text "Invalid route data:")
[:break] [:break] [:break] [:break]
(into (into
[:group] [:group]
(map (map
(fn [{:keys [data path spec scope]}] (fn [{:keys [data path spec scope]}]
[:group [:group
[:span (color :grey "-- On route -----------------------")] [:span (color :grey "-- On route -----------------------")]
[:break] [:break]
[:break] [:break]
(text path) (if scope [:span " " (text scope)]) (text path) (if scope [:span " " (text scope)])
[:break] [:break]
[:break] [:break]
(-> (s/explain-data spec data) (-> (s/explain-data spec data)
(expound-printer) (expound-printer)
(with-out-str) (with-out-str)
(fippify)) (fippify))
[:break]]) [:break]])
problems)) problems))
(color :white "https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-data-validation") (color :white "https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-data-validation")
[:break]]) [:break]])

View file

@ -0,0 +1 @@
../../../.clj-kondo/module_config.edn

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-frontend "0.5.15" (defproject metosin/reitit-frontend "0.5.18"
:description "Reitit: Clojurescript frontend routing core" :description "Reitit: Clojurescript frontend routing core"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

View file

@ -1,5 +1,6 @@
(ns reitit.frontend (ns reitit.frontend
(:require [clojure.set :as set] (:require [clojure.set :as set]
[clojure.string :as str]
[reitit.coercion :as coercion] [reitit.coercion :as coercion]
[reitit.core :as r]) [reitit.core :as r])
(:import goog.Uri (:import goog.Uri
@ -20,20 +21,45 @@
(map (juxt keyword #(query-param q %))) (map (juxt keyword #(query-param q %)))
(into {})))) (into {}))))
(defn fragment-params
"Given goog.Uri, read fragment parameters into Clojure map."
[^Uri uri]
(let [fp (.getFragment uri)]
(if-not (seq fp)
{}
(into {}
(comp
(map #(str/split % #"="))
(map (fn [[k v]]
[(keyword k) v])))
(str/split fp #"&")))))
(defn match-by-path (defn match-by-path
"Given routing tree and current path, return match with possibly "Given routing tree and current path, return match with possibly
coerced parameters. Return nil if no match found." coerced parameters. Return nil if no match found.
[router path]
(let [uri (.parse Uri path)] :on-coercion-error - a sideeffecting fn of `match exception -> nil`"
(if-let [match (r/match-by-path router (.getPath uri))] ([router path] (match-by-path router path nil))
(let [q (query-params uri) ([router path {:keys [on-coercion-error]}]
match (assoc match :query-params q) (let [uri (.parse Uri path)
;; Return uncoerced values if coercion is not enabled - so coerce! (if on-coercion-error
;; that tha parameters are always accessible from same property. (fn [match]
parameters (or (coercion/coerce! match) (try (coercion/coerce! match)
{:path (:path-params match) (catch js/Error e
:query q})] (on-coercion-error match e)
(assoc match :parameters parameters))))) (throw e))))
coercion/coerce!)]
(if-let [match (r/match-by-path router (.getPath uri))]
(let [q (query-params uri)
fp (fragment-params uri)
match (assoc match :query-params q :fragment-params fp)
;; Return uncoerced values if coercion is not enabled - so
;; that tha parameters are always accessible from same property.
parameters (or (coerce! match)
{:path (:path-params match)
:query q
:fragment fp})]
(assoc match :parameters parameters))))))
(defn match-by-name (defn match-by-name
"Given a router, route name and optionally path-parameters, "Given a router, route name and optionally path-parameters,
@ -64,11 +90,11 @@
(let [defined (-> path-params keys set) (let [defined (-> path-params keys set)
missing (set/difference (:required match) defined)] missing (set/difference (:required match) defined)]
(js/console.warn (js/console.warn
"missing path-params for route" name "missing path-params for route" name
{:template (:template match) {:template (:template match)
:missing missing :missing missing
:path-params path-params :path-params path-params
:required (:required match)}) :required (:required match)})
nil)) nil))
match) match)
(do (js/console.warn "missing route" name) (do (js/console.warn "missing route" name)

View file

@ -38,7 +38,7 @@
(when (nil? @history) (when (nil? @history)
(reset! history this)) (reset! history this))
(on-navigate m this)) (on-navigate m this))
opts)) opts))
(defn (defn
^{:see-also ["reitit.frontend.history/href"]} ^{:see-also ["reitit.frontend.history/href"]}

View file

@ -1,9 +1,9 @@
(ns reitit.frontend.history (ns reitit.frontend.history
"Provides integration to hash-change or HTML5 History "Provides integration to hash-change or HTML5 History
events." events."
(:require [reitit.core :as reitit] (:require [goog.events :as gevents]
[reitit.frontend :as rf] [reitit.core :as reitit]
[goog.events :as gevents]) [reitit.frontend :as rf])
(:import goog.Uri)) (:import goog.Uri))
(defprotocol History (defprotocol History
@ -40,7 +40,7 @@
nil) nil)
(-on-navigate [this path] (-on-navigate [this path]
(reset! last-fragment path) (reset! last-fragment path)
(on-navigate (rf/match-by-path router path) this)) (on-navigate (rf/match-by-path router path this) this))
(-get-path [this] (-get-path [this]
;; Remove # ;; Remove #
;; "" or "#" should be same as "#/" ;; "" or "#" should be same as "#/"
@ -125,7 +125,7 @@
(-on-navigate this (-get-path this)) (-on-navigate this (-get-path this))
this)) this))
(-on-navigate [this path] (-on-navigate [this path]
(on-navigate (rf/match-by-path router path) this)) (on-navigate (rf/match-by-path router path this) this))
(-stop [this] (-stop [this]
(gevents/unlistenByKey listen-key) (gevents/unlistenByKey listen-key)
(gevents/unlistenByKey click-listen-key) (gevents/unlistenByKey click-listen-key)

View file

@ -0,0 +1 @@
../../../.clj-kondo/module_config.edn

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-http "0.5.15" (defproject metosin/reitit-http "0.5.18"
:description "Reitit: HTTP routing with interceptors" :description "Reitit: HTTP routing with interceptors"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

View file

@ -1,24 +1,24 @@
(ns reitit.http (ns reitit.http
(:require [meta-merge.core :refer [meta-merge]] (:require [meta-merge.core :refer [meta-merge]]
[reitit.interceptor :as interceptor] [reitit.core :as r]
[reitit.exception :as ex] [reitit.exception :as ex]
[reitit.ring :as ring] [reitit.interceptor :as interceptor]
[reitit.core :as r])) [reitit.ring :as ring]))
(defrecord Endpoint [data interceptors queue handler path method]) (defrecord Endpoint [data interceptors queue handler path method])
(defn coerce-handler [[path data] {:keys [expand] :as opts}] (defn coerce-handler [[path data] {:keys [expand] :as opts}]
[path (reduce [path (reduce
(fn [acc method] (fn [acc method]
(if (contains? acc method) (if (contains? acc method)
(update acc method expand opts) (update acc method expand opts)
acc)) data ring/http-methods)]) acc)) data ring/http-methods)])
(defn compile-result [[path data] {:keys [::default-options-endpoint expand] :as opts}] (defn compile-result [[path data] {:keys [::default-options-endpoint expand meta-merge-fn] :as opts}]
(let [[top childs] (ring/group-keys data) (let [[top childs] (ring/group-keys data)
childs (cond-> childs childs (cond-> childs
(and (not (:options childs)) (not (:handler top)) default-options-endpoint) (and (not (:options childs)) (not (:handler top)) default-options-endpoint)
(assoc :options (expand default-options-endpoint opts))) (assoc :options (expand default-options-endpoint opts)))
compile (fn [[path data] opts scope] compile (fn [[path data] opts scope]
(interceptor/compile-result [path data] opts scope)) (interceptor/compile-result [path data] opts scope))
->endpoint (fn [p d m s] ->endpoint (fn [p d m s]
@ -29,19 +29,19 @@
(assoc :method m)))) (assoc :method m))))
->methods (fn [any? data] ->methods (fn [any? data]
(reduce (reduce
(fn [acc method] (fn [acc method]
(cond-> acc (cond-> acc
any? (assoc method (->endpoint path data method nil)))) any? (assoc method (->endpoint path data method nil))))
(ring/map->Methods {}) (ring/map->Methods {})
ring/http-methods))] ring/http-methods))]
(if-not (seq childs) (if-not (seq childs)
(->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 ((or meta-merge-fn meta-merge) top data)]
(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))))
(defn router (defn router
"Creates a [[reitit.core/Router]] from raw route data and optionally an options map with "Creates a [[reitit.core/Router]] from raw route data and optionally an options map with
@ -133,7 +133,7 @@
(assoc ::interceptor/queue (partial interceptor/queue executor)) (assoc ::interceptor/queue (partial interceptor/queue executor))
(dissoc :data) ; data is already merged into routes (dissoc :data) ; data is already merged into routes
(cond-> (seq interceptors) (cond-> (seq interceptors)
(update-in [:data :interceptors] (partial into (vec interceptors))))) (update-in [:data :interceptors] (partial into (vec interceptors)))))
router (reitit.http/router (r/routes router) router-opts) ;; will re-compile the interceptors router (reitit.http/router (r/routes router) router-opts) ;; will re-compile the interceptors
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?)]

View file

@ -1,7 +1,7 @@
(ns reitit.http.coercion (ns reitit.http.coercion
(:require [reitit.coercion :as coercion] (:require [reitit.coercion :as coercion]
[reitit.spec :as rs] [reitit.impl :as impl]
[reitit.impl :as impl])) [reitit.spec :as rs]))
(defn coerce-request-interceptor (defn coerce-request-interceptor
"Interceptor for pluggable request coercion. "Interceptor for pluggable request coercion.

View file

@ -1,8 +1,8 @@
(ns reitit.http.spec (ns reitit.http.spec
(:require [clojure.spec.alpha :as s] (:require [clojure.spec.alpha :as s]
[reitit.ring.spec :as rrs]
[reitit.interceptor :as interceptor]
[reitit.exception :as exception] [reitit.exception :as exception]
[reitit.interceptor :as interceptor]
[reitit.ring.spec :as rrs]
[reitit.spec :as rs])) [reitit.spec :as rs]))
;; ;;
@ -22,5 +22,5 @@
[routes {:keys [spec ::rs/wrap] :or {spec ::data, wrap identity}}] [routes {:keys [spec ::rs/wrap] :or {spec ::data, wrap identity}}]
(when-let [problems (rrs/validate-route-data routes :interceptors wrap spec)] (when-let [problems (rrs/validate-route-data routes :interceptors wrap spec)]
(exception/fail! (exception/fail!
::rs/invalid-route-data ::rs/invalid-route-data
{:problems problems}))) {:problems problems})))

View file

@ -0,0 +1 @@
../../../.clj-kondo/module_config.edn

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-interceptors "0.5.15" (defproject metosin/reitit-interceptors "0.5.18"
:description "Reitit, common interceptors bundled" :description "Reitit, common interceptors bundled"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

View file

@ -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]
@ -36,16 +38,16 @@
[stages {:keys [enter leave error name] :as interceptor}] [stages {:keys [enter leave error name] :as interceptor}]
(if (->> (select-keys interceptor stages) (vals) (keep identity) (seq)) (if (->> (select-keys interceptor stages) (vals) (keep identity) (seq))
(cond-> {:name ::diff} (cond-> {:name ::diff}
(and enter (stages :enter)) (assoc :enter (handle name :enter)) (and enter (stages :enter)) (assoc :enter (handle name :enter))
(and leave (stages :leave)) (assoc :leave (handle name :leave)) (and leave (stages :leave)) (assoc :leave (handle name :leave))
(and error (stages :error)) (assoc :error (handle name :error))))) (and error (stages :error)) (assoc :error (handle name :error)))))
(defn print-context-diffs (defn print-context-diffs
"A interceptor chain transformer that adds a context diff printer between all interceptors" "A interceptor chain transformer that adds a context diff printer between all interceptors"
[interceptors] [interceptors]
(reduce (reduce
(fn [chain interceptor] (fn [chain interceptor]
(into chain (keep identity [(diff-interceptor #{:leave :error} interceptor) (into chain (keep identity [(diff-interceptor #{:leave :error} interceptor)
interceptor interceptor
(diff-interceptor #{:enter} interceptor)]))) (diff-interceptor #{:enter} interceptor)])))
[(diff-interceptor #{:enter :leave :error} {:enter identity})] interceptors)) [(diff-interceptor #{:enter :leave :error} {:enter identity})] interceptors))

View file

@ -1,10 +1,10 @@
(ns reitit.http.interceptors.exception (ns reitit.http.interceptors.exception
(:require [reitit.coercion :as coercion] (:require [clojure.spec.alpha :as s]
[reitit.ring :as ring] [clojure.string :as str]
[clojure.spec.alpha :as s] [reitit.coercion :as coercion]
[clojure.string :as str]) [reitit.ring :as ring])
(:import (java.time Instant) (:import (java.io PrintWriter Writer)
(java.io PrintWriter Writer))) (java.time Instant)))
(s/def ::handlers (s/map-of any? fn?)) (s/def ::handlers (s/map-of any? fn?))
(s/def ::spec (s/keys :opt-un [::handlers])) (s/def ::spec (s/keys :opt-un [::handlers]))
@ -25,11 +25,11 @@
error-handler (or (get handlers type) error-handler (or (get handlers type)
(get handlers ex-class) (get handlers ex-class)
(some (some
(partial get handlers) (partial get handlers)
(descendants type)) (descendants type))
(some (some
(partial get handlers) (partial get handlers)
(super-classes ex-class)) (super-classes ex-class))
(get handlers ::default))] (get handlers ::default))]
(if-let [wrap (get handlers ::wrap)] (if-let [wrap (get handlers ::wrap)]
(wrap error-handler error request) (wrap error-handler error request)

View file

@ -1,8 +1,8 @@
(ns reitit.http.interceptors.multipart (ns reitit.http.interceptors.multipart
(:require [reitit.coercion :as coercion] (:require [clojure.spec.alpha :as s]
[reitit.coercion :as coercion]
[reitit.spec] [reitit.spec]
[ring.middleware.multipart-params :as multipart-params] [ring.middleware.multipart-params :as multipart-params]
[clojure.spec.alpha :as s]
[spec-tools.core :as st]) [spec-tools.core :as st])
(:import (java.io File))) (:import (java.io File)))
@ -18,14 +18,14 @@
(def temp-file-part (def temp-file-part
"Spec for file param created by ring.middleware.multipart-params.temp-file store." "Spec for file param created by ring.middleware.multipart-params.temp-file store."
(st/spec (st/spec
{:spec (s/keys :req-un [::filename ::content-type ::tempfile ::size]) {:spec (s/keys :req-un [::filename ::content-type ::tempfile ::size])
:swagger/type "file"})) :swagger/type "file"}))
(def bytes-part (def bytes-part
"Spec for file param created by ring.middleware.multipart-params.byte-array store." "Spec for file param created by ring.middleware.multipart-params.byte-array store."
(st/spec (st/spec
{:spec (s/keys :req-un [::filename ::content-type ::bytes]) {:spec (s/keys :req-un [::filename ::content-type ::bytes])
:swagger/type "file"})) :swagger/type "file"}))
(defn- coerced-request [request coercers] (defn- coerced-request [request coercers]
(if-let [coerced (if coercers (coercion/coerce-request coercers request))] (if-let [coerced (if coercers (coercion/coerce-request coercers request))]
@ -49,7 +49,7 @@
:compile (fn [{:keys [parameters coercion]} opts] :compile (fn [{:keys [parameters coercion]} opts]
(if-let [multipart (:multipart parameters)] (if-let [multipart (:multipart parameters)]
(let [parameter-coercion {:multipart (coercion/->ParameterCoercion (let [parameter-coercion {:multipart (coercion/->ParameterCoercion
:multipart-params :string true true)} :multipart-params :string true true)}
opts (assoc opts ::coercion/parameter-coercion parameter-coercion) opts (assoc opts ::coercion/parameter-coercion parameter-coercion)
coercers (if multipart (coercion/request-coercers coercion parameters opts))] coercers (if multipart (coercion/request-coercers coercion parameters opts))]
{:data {:swagger {:consumes ^:replace #{"multipart/form-data"}}} {:data {:swagger {:consumes ^:replace #{"multipart/form-data"}}}

View file

@ -1,7 +1,7 @@
(ns reitit.http.interceptors.muuntaja (ns reitit.http.interceptors.muuntaja
(:require [muuntaja.core :as m] (:require [clojure.spec.alpha :as s]
[muuntaja.interceptor] [muuntaja.core :as m]
[clojure.spec.alpha :as s])) [muuntaja.interceptor]))
(s/def ::muuntaja m/muuntaja?) (s/def ::muuntaja m/muuntaja?)
(s/def ::spec (s/keys :opt-un [::muuntaja])) (s/def ::spec (s/keys :opt-un [::muuntaja]))
@ -40,10 +40,10 @@
:compile (fn [{:keys [muuntaja parameters]} _] :compile (fn [{:keys [muuntaja parameters]} _]
(if-let [muuntaja (or muuntaja default-muuntaja)] (if-let [muuntaja (or muuntaja default-muuntaja)]
(merge (merge
(stripped (muuntaja.interceptor/format-interceptor muuntaja)) (stripped (muuntaja.interceptor/format-interceptor muuntaja))
(if (publish-swagger-data? parameters) (if (publish-swagger-data? parameters)
{:data {:swagger {:produces (displace (m/encodes muuntaja)) {:data {:swagger {:produces (displace (m/encodes muuntaja))
:consumes (displace (m/decodes muuntaja))}}}))))})) :consumes (displace (m/decodes muuntaja))}}}))))}))
(defn format-negotiate-interceptor (defn format-negotiate-interceptor
"Interceptor for content-negotiation. "Interceptor for content-negotiation.
@ -87,9 +87,9 @@
:compile (fn [{:keys [muuntaja parameters]} _] :compile (fn [{:keys [muuntaja parameters]} _]
(if-let [muuntaja (or muuntaja default-muuntaja)] (if-let [muuntaja (or muuntaja default-muuntaja)]
(merge (merge
(stripped (muuntaja.interceptor/format-request-interceptor muuntaja)) (stripped (muuntaja.interceptor/format-request-interceptor muuntaja))
(when (publish-swagger-data? parameters) (when (publish-swagger-data? parameters)
{:data {:swagger {:consumes (displace (m/decodes muuntaja))}}}))))})) {:data {:swagger {:consumes (displace (m/decodes muuntaja))}}}))))}))
(defn format-response-interceptor (defn format-response-interceptor
"Interceptor for response formatting. "Interceptor for response formatting.
@ -112,6 +112,6 @@
:compile (fn [{:keys [muuntaja parameters]} _] :compile (fn [{:keys [muuntaja parameters]} _]
(if-let [muuntaja (or muuntaja default-muuntaja)] (if-let [muuntaja (or muuntaja default-muuntaja)]
(merge (merge
(stripped (muuntaja.interceptor/format-response-interceptor muuntaja)) (stripped (muuntaja.interceptor/format-response-interceptor muuntaja))
(when (publish-swagger-data? parameters) (when (publish-swagger-data? parameters)
{:data {:swagger {:produces (displace (m/encodes muuntaja))}}}))))})) {:data {:swagger {:produces (displace (m/encodes muuntaja))}}}))))}))

View file

@ -0,0 +1 @@
../../../.clj-kondo/module_config.edn

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-malli "0.5.15" (defproject metosin/reitit-malli "0.5.18"
:description "Reitit: Malli coercion" :description "Reitit: Malli coercion"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

View file

@ -1,13 +1,14 @@
(ns reitit.coercion.malli (ns reitit.coercion.malli
(:require [reitit.coercion :as coercion] (:require [clojure.set :as set]
[malli.transform :as mt] [clojure.walk :as walk]
[malli.core :as m]
[malli.edn :as edn] [malli.edn :as edn]
[malli.error :as me] [malli.error :as me]
[malli.util :as mu] [malli.experimental.lite :as l]
[malli.swagger :as swagger] [malli.swagger :as swagger]
[malli.core :as m] [malli.transform :as mt]
[clojure.set :as set] [malli.util :as mu]
[clojure.walk :as walk])) [reitit.coercion :as coercion]))
;; ;;
;; coercion ;; coercion
@ -26,9 +27,9 @@
(reify TransformationProvider (reify TransformationProvider
(-transformer [_ {:keys [strip-extra-keys default-values]}] (-transformer [_ {:keys [strip-extra-keys default-values]}]
(mt/transformer (mt/transformer
(if strip-extra-keys (mt/strip-extra-keys-transformer)) (if strip-extra-keys (mt/strip-extra-keys-transformer))
transformer transformer
(if default-values (mt/default-value-transformer)))))) (if default-values (mt/default-value-transformer))))))
(def string-transformer-provider (-provider (mt/string-transformer))) (def string-transformer-provider (-provider (mt/string-transformer)))
(def json-transformer-provider (-provider (mt/json-transformer))) (def json-transformer-provider (-provider (mt/json-transformer)))
@ -61,7 +62,7 @@
transformed transformed
(let [error (-explain coercer transformed)] (let [error (-explain coercer transformed)]
(coercion/map->CoercionError (coercion/map->CoercionError
(assoc error :transformed transformed))))) (assoc error :transformed transformed)))))
value)) value))
;; encode: decode -> validate -> encode ;; encode: decode -> validate -> encode
(fn [value format] (fn [value format]
@ -71,7 +72,7 @@
(-encode coercer transformed) (-encode coercer transformed)
(let [error (-explain coercer transformed)] (let [error (-explain coercer transformed)]
(coercion/map->CoercionError (coercion/map->CoercionError
(assoc error :transformed transformed)))) (assoc error :transformed transformed))))
value)))))))) value))))))))
;; ;;
@ -91,15 +92,15 @@
(defmethod extract-parameter :default [in schema options] (defmethod extract-parameter :default [in schema options]
(let [{:keys [properties required]} (swagger/transform schema (merge options {:in in, :type :parameter}))] (let [{:keys [properties required]} (swagger/transform schema (merge options {:in in, :type :parameter}))]
(mapv (mapv
(fn [[k {:keys [type] :as schema}]] (fn [[k {:keys [type] :as schema}]]
(merge (merge
{:in (name in) {:in (name in)
:name k :name k
:description (:description schema "") :description (:description schema "")
:type type :type type
:required (contains? (set required) k)} :required (contains? (set required) k)}
schema)) schema))
properties))) properties)))
;; ;;
;; public api ;; public api
@ -113,7 +114,9 @@
: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?
:lite true
;; schema identity function (default: close all map schemas) ;; schema identity function (default: close all map schemas)
:compile mu/closed-schema :compile mu/closed-schema
;; validate request & response ;; validate request & response
@ -133,9 +136,11 @@
([] ([]
(create nil)) (create nil))
([opts] ([opts]
(let [{:keys [transformers compile options error-keys encode-error] :as opts} (merge default-options opts) (let [{:keys [transformers lite compile options error-keys encode-error] :as opts} (merge default-options opts)
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)]
^{:type ::coercion/coercion} ^{:type ::coercion/coercion}
(reify coercion/Coercion (reify coercion/Coercion
(-get-name [_] :malli) (-get-name [_] :malli)
@ -143,39 +148,39 @@
(-get-apidocs [_ specification {:keys [parameters responses]}] (-get-apidocs [_ specification {:keys [parameters responses]}]
(case specification (case specification
:swagger (merge :swagger (merge
(if parameters (if parameters
{:parameters {:parameters
(->> (for [[in schema] parameters (->> (for [[in schema] parameters
parameter (extract-parameter in (compile schema options) options)] parameter (extract-parameter in (compile schema options) options)]
parameter) parameter)
(into []))}) (into []))})
(if responses (if responses
{:responses {:responses
(into (into
(empty responses) (empty responses)
(for [[status response] responses] (for [[status response] responses]
[status (as-> response $ [status (as-> response $
(set/rename-keys $ {:body :schema}) (set/rename-keys $ {:body :schema})
(update $ :description (fnil identity "")) (update $ :description (fnil identity ""))
(if (:schema $) (if (:schema $)
(-> $ (-> $
(update :schema compile options) (update :schema compile options)
(update :schema swagger/transform {:type :schema})) (update :schema swagger/transform {:type :schema}))
$))]))})) $))]))}))
(throw (throw
(ex-info (ex-info
(str "Can't produce Schema apidocs for " specification) (str "Can't produce Schema apidocs for " specification)
{:type specification, :coercion :schema})))) {:type specification, :coercion :schema}))))
(-compile-model [_ model _] (compile model options)) (-compile-model [_ model _] (compile model options))
(-open-model [_ schema] schema) (-open-model [_ schema] schema)
(-encode-error [_ error] (-encode-error [_ error]
(cond-> error (cond-> error
(show? :humanized) (assoc :humanized (me/humanize error {:wrap :message})) (show? :humanized) (assoc :humanized (me/humanize error {:wrap :message}))
(show? :schema) (update :schema edn/write-string opts) (show? :schema) (update :schema edn/write-string opts)
(show? :errors) (-> (me/with-error-messages opts) (show? :errors) (-> (me/with-error-messages opts)
(update :errors (partial map #(update % :schema edn/write-string opts)))) (update :errors (partial map #(update % :schema edn/write-string opts))))
(seq error-keys) (select-keys error-keys) (seq error-keys) (select-keys error-keys)
encode-error (encode-error))) encode-error (encode-error)))
(-request-coercer [_ type schema] (-request-coercer [_ type schema]
(-coercer (compile schema options) type transformers :decode opts)) (-coercer (compile schema options) type transformers :decode opts))
(-response-coercer [_ schema] (-response-coercer [_ schema]

View file

@ -0,0 +1 @@
../../../.clj-kondo/module_config.edn

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-middleware "0.5.15" (defproject metosin/reitit-middleware "0.5.18"
:description "Reitit, common middleware bundled" :description "Reitit, common middleware bundled"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

View file

@ -48,6 +48,6 @@
printer between all middleware." printer between all middleware."
[chain] [chain]
(reduce (reduce
(fn [chain mw] (fn [chain mw]
(into chain [mw (print-diff-middleware (select-keys mw [:name]))])) (into chain [mw (print-diff-middleware (select-keys mw [:name]))]))
[(print-diff-middleware)] chain)) [(print-diff-middleware)] chain))

View file

@ -1,10 +1,10 @@
(ns reitit.ring.middleware.exception (ns reitit.ring.middleware.exception
(:require [reitit.coercion :as coercion] (:require [clojure.spec.alpha :as s]
[reitit.ring :as ring] [clojure.string :as str]
[clojure.spec.alpha :as s] [reitit.coercion :as coercion]
[clojure.string :as str]) [reitit.ring :as ring])
(:import (java.time Instant) (:import (java.io Writer PrintWriter)
(java.io Writer PrintWriter))) (java.time Instant)))
(s/def ::handlers (s/map-of any? fn?)) (s/def ::handlers (s/map-of any? fn?))
(s/def ::spec (s/keys :opt-un [::handlers])) (s/def ::spec (s/keys :opt-un [::handlers]))
@ -25,11 +25,11 @@
error-handler (or (get handlers type) error-handler (or (get handlers type)
(get handlers ex-class) (get handlers ex-class)
(some (some
(partial get handlers) (partial get handlers)
(descendants type)) (descendants type))
(some (some
(partial get handlers) (partial get handlers)
(super-classes ex-class)) (super-classes ex-class))
(get handlers ::default))] (get handlers ::default))]
(if-let [wrap (get handlers ::wrap)] (if-let [wrap (get handlers ::wrap)]
(wrap error-handler error request) (wrap error-handler error request)

View file

@ -1,8 +1,8 @@
(ns reitit.ring.middleware.multipart (ns reitit.ring.middleware.multipart
(:refer-clojure :exclude [compile]) (:refer-clojure :exclude [compile])
(:require [reitit.coercion :as coercion] (:require [clojure.spec.alpha :as s]
[reitit.coercion :as coercion]
[ring.middleware.multipart-params :as multipart-params] [ring.middleware.multipart-params :as multipart-params]
[clojure.spec.alpha :as s]
[spec-tools.core :as st]) [spec-tools.core :as st])
(:import (java.io File))) (:import (java.io File)))
@ -15,14 +15,14 @@
(def temp-file-part (def temp-file-part
"Spec for file param created by ring.middleware.multipart-params.temp-file store." "Spec for file param created by ring.middleware.multipart-params.temp-file store."
(st/spec (st/spec
{:spec (s/keys :req-un [::filename ::content-type ::tempfile ::size]) {:spec (s/keys :req-un [::filename ::content-type ::tempfile ::size])
:swagger/type "file"})) :swagger/type "file"}))
(def bytes-part (def bytes-part
"Spec for file param created by ring.middleware.multipart-params.byte-array store." "Spec for file param created by ring.middleware.multipart-params.byte-array store."
(st/spec (st/spec
{:spec (s/keys :req-un [::filename ::content-type ::bytes]) {:spec (s/keys :req-un [::filename ::content-type ::bytes])
:swagger/type "file"})) :swagger/type "file"}))
(defn- coerced-request [request coercers] (defn- coerced-request [request coercers]
(if-let [coerced (if coercers (coercion/coerce-request coercers request))] (if-let [coerced (if coercers (coercion/coerce-request coercers request))]
@ -33,7 +33,7 @@
(fn [{:keys [parameters coercion]} opts] (fn [{:keys [parameters coercion]} opts]
(if-let [multipart (:multipart parameters)] (if-let [multipart (:multipart parameters)]
(let [parameter-coercion {:multipart (coercion/->ParameterCoercion (let [parameter-coercion {:multipart (coercion/->ParameterCoercion
:multipart-params :string true true)} :multipart-params :string true true)}
opts (assoc opts ::coercion/parameter-coercion parameter-coercion) opts (assoc opts ::coercion/parameter-coercion parameter-coercion)
coercers (if multipart (coercion/request-coercers coercion parameters opts))] coercers (if multipart (coercion/request-coercers coercion parameters opts))]
{:data {:swagger {:consumes ^:replace #{"multipart/form-data"}}} {:data {:swagger {:consumes ^:replace #{"multipart/form-data"}}}

View file

@ -1,7 +1,7 @@
(ns reitit.ring.middleware.muuntaja (ns reitit.ring.middleware.muuntaja
(:require [muuntaja.core :as m] (:require [clojure.spec.alpha :as s]
[muuntaja.middleware] [muuntaja.core :as m]
[clojure.spec.alpha :as s])) [muuntaja.middleware]))
(s/def ::muuntaja m/muuntaja?) (s/def ::muuntaja m/muuntaja?)
(s/def ::spec (s/keys :opt-un [::muuntaja])) (s/def ::spec (s/keys :opt-un [::muuntaja]))
@ -34,10 +34,10 @@
:compile (fn [{:keys [muuntaja parameters]} _] :compile (fn [{:keys [muuntaja parameters]} _]
(if muuntaja (if muuntaja
(merge (merge
(if (publish-swagger-data? parameters) (if (publish-swagger-data? parameters)
{:data {:swagger {:produces (displace (m/encodes muuntaja)) {:data {:swagger {:produces (displace (m/encodes muuntaja))
:consumes (displace (m/decodes muuntaja))}}}) :consumes (displace (m/decodes muuntaja))}}})
{:wrap #(muuntaja.middleware/wrap-format % muuntaja)})))}) {:wrap #(muuntaja.middleware/wrap-format % muuntaja)})))})
(def format-negotiate-middleware (def format-negotiate-middleware
"Middleware for content-negotiation. "Middleware for content-negotiation.
@ -71,9 +71,9 @@
:compile (fn [{:keys [muuntaja parameters]} _] :compile (fn [{:keys [muuntaja parameters]} _]
(if muuntaja (if muuntaja
(merge (merge
(when (publish-swagger-data? parameters) (when (publish-swagger-data? parameters)
{:data {:swagger {:consumes (displace (m/decodes muuntaja))}}}) {:data {:swagger {:consumes (displace (m/decodes muuntaja))}}})
{:wrap #(muuntaja.middleware/wrap-format-request % muuntaja)})))}) {:wrap #(muuntaja.middleware/wrap-format-request % muuntaja)})))})
(def format-response-middleware (def format-response-middleware
"Middleware for response formatting. "Middleware for response formatting.
@ -91,6 +91,6 @@
:compile (fn [{:keys [muuntaja parameters]} _] :compile (fn [{:keys [muuntaja parameters]} _]
(if muuntaja (if muuntaja
(merge (merge
(when (publish-swagger-data? parameters) (when (publish-swagger-data? parameters)
{:data {:swagger {:produces (displace (m/encodes muuntaja))}}}) {:data {:swagger {:produces (displace (m/encodes muuntaja))}}})
{:wrap #(muuntaja.middleware/wrap-format-response % muuntaja)})))}) {:wrap #(muuntaja.middleware/wrap-format-response % muuntaja)})))})

View file

@ -0,0 +1 @@
../../../.clj-kondo/module_config.edn

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-pedestal "0.5.15" (defproject metosin/reitit-pedestal "0.5.18"
:description "Reitit + Pedestal Integration" :description "Reitit + Pedestal Integration"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

View file

@ -1,11 +1,11 @@
(ns reitit.pedestal (ns reitit.pedestal
(:require [io.pedestal.interceptor.chain :as chain] (:require [io.pedestal.http :as http]
[io.pedestal.interceptor :as interceptor] [io.pedestal.interceptor :as interceptor]
[io.pedestal.http :as http] [io.pedestal.interceptor.chain :as chain]
[reitit.interceptor] [reitit.http]
[reitit.http]) [reitit.interceptor])
(:import (reitit.interceptor Executor) (:import (java.lang.reflect Method)
(java.lang.reflect Method))) (reitit.interceptor Executor)))
;; TODO: variadic ;; TODO: variadic
(defn- arities [f] (defn- arities [f]
@ -36,9 +36,9 @@
interceptor interceptor
(->> (select-keys interceptor [:enter :leave :error]) (vals) (keep identity) (seq)) (->> (select-keys interceptor [:enter :leave :error]) (vals) (keep identity) (seq))
(interceptor/interceptor (interceptor/interceptor
(if (error-without-arity-2? interceptor) (if (error-without-arity-2? interceptor)
(wrap-error-arity-2->1 interceptor) (wrap-error-arity-2->1 interceptor)
interceptor)))) interceptor))))
;; ;;
;; Public API ;; Public API
@ -62,11 +62,11 @@
(routing-interceptor router default-handler nil)) (routing-interceptor router default-handler nil))
([router default-handler {:keys [interceptors]}] ([router default-handler {:keys [interceptors]}]
(interceptor/interceptor (interceptor/interceptor
(reitit.http/routing-interceptor (reitit.http/routing-interceptor
router router
default-handler default-handler
{:executor pedestal-executor {:executor pedestal-executor
:interceptors interceptors})))) :interceptors interceptors}))))
(defn replace-last-interceptor [service-map interceptor] (defn replace-last-interceptor [service-map interceptor]
(-> service-map (-> service-map

View file

@ -0,0 +1 @@
../../../.clj-kondo/module_config.edn

View file

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

View file

@ -1,12 +1,12 @@
(ns reitit.ring (ns reitit.ring
(:require [meta-merge.core :refer [meta-merge]] (:require [clojure.string :as str]
[reitit.middleware :as middleware] [meta-merge.core :refer [meta-merge]]
[reitit.exception :as ex]
[reitit.core :as r]
[reitit.impl :as impl]
#?@(: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]])
[clojure.string :as str])) [reitit.core :as r]
[reitit.exception :as ex]
[reitit.impl :as impl]
[reitit.middleware :as middleware]))
(declare get-match) (declare get-match)
(declare get-router) (declare get-router)
@ -17,23 +17,23 @@
(defn ^:no-wiki group-keys [data] (defn ^:no-wiki group-keys [data]
(reduce-kv (reduce-kv
(fn [[top childs] k v] (fn [[top childs] k v]
(if (http-methods k) (if (http-methods k)
[top (assoc childs k v)] [top (assoc childs k v)]
[(assoc top k v) childs])) [{} {}] data)) [(assoc top k v) childs])) [{} {}] data))
(defn coerce-handler [[path data] {:keys [expand] :as opts}] (defn coerce-handler [[path data] {:keys [expand] :as opts}]
[path (reduce [path (reduce
(fn [acc method] (fn [acc method]
(if (contains? acc method) (if (contains? acc method)
(update acc method expand opts) (update acc method expand opts)
acc)) data http-methods)]) acc)) data http-methods)])
(defn compile-result [[path data] {:keys [::default-options-endpoint expand] :as opts}] (defn compile-result [[path data] {:keys [::default-options-endpoint expand meta-merge-fn] :as opts}]
(let [[top childs] (group-keys data) (let [[top childs] (group-keys data)
childs (cond-> childs childs (cond-> childs
(and (not (:options childs)) (not (:handler top)) default-options-endpoint) (and (not (:options childs)) (not (:handler top)) default-options-endpoint)
(assoc :options (expand default-options-endpoint opts))) (assoc :options (expand default-options-endpoint opts)))
->endpoint (fn [p d m s] ->endpoint (fn [p d m s]
(-> (middleware/compile-result [p d] opts s) (-> (middleware/compile-result [p d] opts s)
(map->Endpoint) (map->Endpoint)
@ -41,30 +41,30 @@
(assoc :method m))) (assoc :method m)))
->methods (fn [any? data] ->methods (fn [any? data]
(reduce (reduce
(fn [acc method] (fn [acc method]
(cond-> acc (cond-> acc
any? (assoc method (->endpoint path data method nil)))) any? (assoc method (->endpoint path data method nil))))
(map->Methods {}) (map->Methods {})
http-methods))] http-methods))]
(if-not (seq childs) (if-not (seq childs)
(->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 ((or meta-merge-fn meta-merge) top data)]
(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
@ -318,26 +318,26 @@
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]

View file

@ -1,7 +1,7 @@
(ns reitit.ring.coercion (ns reitit.ring.coercion
(:require [reitit.coercion :as coercion] (:require [reitit.coercion :as coercion]
[reitit.spec :as rs] [reitit.impl :as impl]
[reitit.impl :as impl])) [reitit.spec :as rs]))
(defn handle-coercion-exception [e respond raise] (defn handle-coercion-exception [e respond raise]
(let [data (ex-data e)] (let [data (ex-data e)]
@ -10,8 +10,8 @@
::coercion/response-coercion 500 ::coercion/response-coercion 500
nil)] nil)]
(respond (respond
{:status status {:status status
:body (coercion/encode-error data)}) :body (coercion/encode-error data)})
(raise e)))) (raise e))))
;; ;;

View file

@ -1,8 +1,8 @@
(ns reitit.ring.spec (ns reitit.ring.spec
(:require [clojure.spec.alpha :as s] (:require [clojure.spec.alpha :as s]
[reitit.exception :as exception]
[reitit.middleware :as middleware] [reitit.middleware :as middleware]
[reitit.spec :as rs] [reitit.spec :as rs]))
[reitit.exception :as exception]))
;; ;;
;; Specs ;; Specs
@ -19,7 +19,6 @@
(s/def ::trace map?) (s/def ::trace map?)
(s/def ::patch map?) (s/def ::patch map?)
(s/def ::data (s/def ::data
(s/keys :opt-un [::rs/handler ::rs/name ::rs/no-doc ::middleware])) (s/keys :opt-un [::rs/handler ::rs/name ::rs/no-doc ::middleware]))
@ -30,9 +29,9 @@
(defn merge-specs [specs] (defn merge-specs [specs]
(when-let [non-specs (seq (remove #(or (s/spec? %) (s/get-spec %)) specs))] (when-let [non-specs (seq (remove #(or (s/spec? %) (s/get-spec %)) specs))]
(exception/fail! (exception/fail!
::invalid-specs ::invalid-specs
{:specs specs {:specs specs
:invalid non-specs})) :invalid non-specs}))
(s/merge-spec-impl (vec specs) (vec specs) nil)) (s/merge-spec-impl (vec specs) (vec specs) nil))
(defn validate-route-data [routes key wrap spec] (defn validate-route-data [routes key wrap spec]
@ -51,5 +50,5 @@
[routes {:keys [spec ::rs/wrap] :or {spec ::data, wrap identity}}] [routes {:keys [spec ::rs/wrap] :or {spec ::data, wrap identity}}]
(when-let [problems (validate-route-data routes :middleware wrap spec)] (when-let [problems (validate-route-data routes :middleware wrap spec)]
(exception/fail! (exception/fail!
::rs/invalid-route-data ::rs/invalid-route-data
{:problems problems}))) {:problems problems})))

View file

@ -0,0 +1 @@
../../../.clj-kondo/module_config.edn

View file

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

View file

@ -1,13 +1,13 @@
(ns reitit.coercion.schema (ns reitit.coercion.schema
(:require [clojure.walk :as walk] (:require [clojure.set :as set]
[schema.core :as s] [clojure.walk :as walk]
[schema-tools.core :as st]
[schema.coerce :as sc]
[schema.utils :as su]
[schema-tools.coerce :as stc]
[schema-tools.swagger.core :as swagger]
[reitit.coercion :as coercion] [reitit.coercion :as coercion]
[clojure.set :as set])) [schema-tools.coerce :as stc]
[schema-tools.core :as st]
[schema-tools.swagger.core :as swagger]
[schema.coerce :as sc]
[schema.core :as s]
[schema.utils :as su]))
(def string-coercion-matcher (def string-coercion-matcher
stc/string-coercion-matcher) stc/string-coercion-matcher)
@ -23,16 +23,16 @@
(defn stringify [schema] (defn stringify [schema]
(walk/prewalk (walk/prewalk
(fn [x] (fn [x]
(cond (cond
#?@(:clj [(class? x) (.getName ^Class x)]) #?@(:clj [(class? x) (.getName ^Class x)])
(instance? schema.core.OptionalKey x) (pr-str (list 'opt (:k x))) (instance? schema.core.OptionalKey x) (pr-str (list 'opt (:k x)))
(instance? schema.core.RequiredKey x) (pr-str (list 'req (:k x))) (instance? schema.core.RequiredKey x) (pr-str (list 'req (:k x)))
(and (satisfies? s/Schema x) (record? x)) (try (pr-str (s/explain x)) (catch #?(:clj Exception :cljs js/Error) _ x)) (and (satisfies? s/Schema x) (record? x)) (try (pr-str (s/explain x)) (catch #?(:clj Exception :cljs js/Error) _ x))
(instance? schema.utils.ValidationError x) (str (su/validation-error-explain x)) (instance? schema.utils.ValidationError x) (str (su/validation-error-explain x))
(instance? schema.utils.NamedError x) (str (su/named-error-explain x)) (instance? schema.utils.NamedError x) (str (su/named-error-explain x))
:else x)) :else x))
schema)) schema))
(def default-options (def default-options
{:coerce-response? coerce-response? {:coerce-response? coerce-response?
@ -50,27 +50,27 @@
;; 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
(if parameters (if parameters
{::swagger/parameters {::swagger/parameters
(into (into
(empty parameters) (empty parameters)
(for [[k v] parameters] (for [[k v] parameters]
[k (coercion/-compile-model this v nil)]))}) [k (coercion/-compile-model this v nil)]))})
(if responses (if responses
{::swagger/responses {::swagger/responses
(into (into
(empty responses) (empty responses)
(for [[k response] responses] (for [[k response] responses]
[k (as-> response $ [k (as-> response $
(set/rename-keys $ {:body :schema}) (set/rename-keys $ {:body :schema})
(if (:schema $) (if (:schema $)
(update $ :schema #(coercion/-compile-model this % nil)) (update $ :schema #(coercion/-compile-model this % nil))
$))]))}))) $))]))})))
(throw (throw
(ex-info (ex-info
(str "Can't produce Schema apidocs for " specification) (str "Can't produce Schema apidocs for " specification)
{:type specification, :coercion :schema})))) {:type specification, :coercion :schema}))))
(-compile-model [_ model _] model) (-compile-model [_ model _] model)
(-open-model [_ schema] (st/open-schema schema)) (-open-model [_ schema] (st/open-schema schema))
(-encode-error [_ error] (-encode-error [_ error]
@ -88,8 +88,8 @@
coerced (coercer value)] coerced (coercer value)]
(if-let [error (su/error-val coerced)] (if-let [error (su/error-val coerced)]
(coercion/map->CoercionError (coercion/map->CoercionError
{:schema schema {:schema schema
:errors error}) :errors error})
coerced)) coerced))
value)))) value))))
(-response-coercer [this schema] (-response-coercer [this schema]

View file

@ -1,6 +1,6 @@
(ns reitit.ring.schema (ns reitit.ring.schema
(:require [schema.core :as s] (:require [schema-tools.swagger.core :as swagger]
[schema-tools.swagger.core :as swagger]) [schema.core :as s])
#?(:clj (:import (java.io File)))) #?(:clj (:import (java.io File))))
(defrecord Upload [m] (defrecord Upload [m]

View file

@ -0,0 +1 @@
../../../.clj-kondo/module_config.edn

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-sieppari "0.5.15" (defproject metosin/reitit-sieppari "0.5.18"
:description "Reitit: Sieppari Interceptors" :description "Reitit: Sieppari Interceptors"
:url "https://github.com/metosin/reitit" :url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"

Some files were not shown because too many files have changed in this diff Show more