mirror of
https://github.com/metosin/reitit.git
synced 2026-02-27 19:12:22 +00:00
Compare commits
No commits in common. "master" and "0.9.0" have entirely different histories.
97 changed files with 1724 additions and 1518 deletions
|
|
@ -1,6 +1,5 @@
|
|||
{;;:skip-comments true
|
||||
:lint-as {potemkin/def-derived-map clojure.core/defrecord
|
||||
clojure.test.check.clojure-test/defspec clojure.test/deftest}
|
||||
:lint-as {potemkin/def-derived-map clojure.core/defrecord}
|
||||
:linters {:missing-else-branch {:level :off}
|
||||
:unused-binding {:level :off}
|
||||
:unused-referred-var {:exclude {clojure.test [deftest testing is are]
|
||||
|
|
|
|||
26
.github/workflows/release.yml
vendored
26
.github/workflows/release.yml
vendored
|
|
@ -1,26 +0,0 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published # reacts to releases and prereleases, but not their drafts
|
||||
|
||||
jobs:
|
||||
build-and-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: "Setup Java"
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 11
|
||||
- name: "Setup Clojure"
|
||||
uses: DeLaGuardo/setup-clojure@master
|
||||
with:
|
||||
lein: 2.9.5
|
||||
- name: Deploy to Clojars
|
||||
run: ./scripts/lein-modules do clean, deploy clojars
|
||||
env:
|
||||
CLOJARS_USERNAME: metosinci
|
||||
CLOJARS_PASSWORD: "${{ secrets.CLOJARS_DEPLOY_TOKEN }}"
|
||||
13
.github/workflows/testsuite.yml
vendored
13
.github/workflows/testsuite.yml
vendored
|
|
@ -2,19 +2,16 @@ name: testsuite
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
build-clj:
|
||||
strategy:
|
||||
matrix:
|
||||
# Supported Java versions: LTS releases and latest
|
||||
jdk: [11, 17, 21, 25]
|
||||
clojure: [11, 12]
|
||||
jdk: [11, 17, 21]
|
||||
|
||||
name: Clojure ${{ matrix.clojure }} (Java ${{ matrix.jdk }})
|
||||
name: Clojure (Java ${{ matrix.jdk }})
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
|
@ -39,18 +36,14 @@ jobs:
|
|||
uses: DeLaGuardo/setup-clojure@13.1
|
||||
with:
|
||||
lein: 2.9.5
|
||||
clj-kondo: 2025.12.23
|
||||
|
||||
# Install openapi-schema-validator for openapi-tests
|
||||
# Uses node version from the ubuntu-latest
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run Linter
|
||||
run: ./lint.sh
|
||||
|
||||
- name: Run tests
|
||||
run: ./scripts/test.sh clj${{ matrix.clojure }}
|
||||
run: ./scripts/test.sh clj
|
||||
|
||||
build-cljs:
|
||||
name: ClojureScript
|
||||
|
|
|
|||
45
CHANGELOG.md
45
CHANGELOG.md
|
|
@ -12,51 +12,6 @@ We use [Break Versioning][breakver]. The version numbers follow a `<major>.<mino
|
|||
|
||||
[breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md
|
||||
|
||||
## UNRELEASED
|
||||
|
||||
* **FIX** redirect-trailing-slash-handler won't make external redirects. [#776](https://github.com/metosin/reitit/pull/776)
|
||||
* Allow colons in bracket parameter syntax. [#770](https://github.com/metosin/reitit/pull/770)
|
||||
* Add `url-encode?` option to `match-by-name`. [#778](https://github.com/metosin/reitit/pull/778)
|
||||
|
||||
## 0.10.0 (2026-01-09)
|
||||
|
||||
* Improve & document how response schemas get picked in per-content-type coercion. See [docs](./doc/ring/coercion.md#per-content-type-coercion). [#745](https://github.com/metosin/reitit/issues/745).
|
||||
* **BREAKING** Remove unused `reitit.dependency` ns. [#763](https://github.com/metosin/reitit/pull/763)
|
||||
* Support passing options to malli humanize. See [docs](./doc/coercion/malli_coercion.md). [#467](https://github.com/metosin/reitit/issues/467)
|
||||
* **FIX** Handling of ex-type keyword hierarchies in create-exception-middleware. [#768](https://github.com/metosin/reitit/issues/768)
|
||||
* Updated dependencies:
|
||||
|
||||
```
|
||||
[metosin/malli "0.20.0"] is available but we use "0.19.2"
|
||||
[com.fasterxml.jackson.core/jackson-core "2.20.1"] is available but we use "2.20.0"
|
||||
[com.fasterxml.jackson.core/jackson-databind "2.20.1"] is available but we use "2.20.0"
|
||||
[org.clojure/core.rrb-vector "0.2.1"] is available but we use "0.2.0"
|
||||
```
|
||||
|
||||
## 0.9.2 (2025-10-28)
|
||||
|
||||
* Allow multimethods as handlers when validating [#755](https://github.com/metosin/reitit/pull/755)
|
||||
* Improve error reporting when generating OpenAPI fails [#754](https://github.com/metosin/reitit/pull/754)
|
||||
* Allow middleware registry to be used when defining middleware in `ring-handler`. See [docs](./doc/ring/middleware_registry.md). [#739](https://github.com/metosin/reitit/pull/739)
|
||||
* Allow passing options (eg. `:malli.transform/add-optional-keys`) to malli's `default-value-transformer`. See [docs](./doc/coercion/malli_coercion.md). [#756](https://github.com/metosin/reitit/pull/756)
|
||||
* **FIX**: `match-by-name!` returning `nil` instead of throwing an exception for some partial matches [#758](https://github.com/metosin/reitit/issues/758)
|
||||
* Updated dependencies:
|
||||
|
||||
```
|
||||
[com.fasterxml.jackson.core/jackson-core "2.20.0"] is available but we use "2.18.2"
|
||||
[com.fasterxml.jackson.core/jackson-databind "2.20.0"] is available but we use "2.18.2"
|
||||
[compojure "1.7.2"] is available but we use "1.7.1"
|
||||
[fipp "0.6.29"] is available but we use "0.6.27"
|
||||
[metosin/malli "0.19.2"] is available but we use "0.18.0"
|
||||
[ring "1.15.3"] is available but we use "1.14.1"
|
||||
[ring/ring-core "1.15.3"] is available but we use "1.14.1"
|
||||
[ring/ring-defaults "0.7.0"] is available but we use "0.6.0"
|
||||
```
|
||||
|
||||
## 0.9.1 (2025-05-27)
|
||||
|
||||
* **FIX**: response coercion threw an exception for unlisted HTTP status codes if there was no `:default`. Broken in 0.9.0. [#742](https://github.com/metosin/reitit/issues/742)
|
||||
|
||||
## 0.9.0 (2025-05-23)
|
||||
|
||||
* Improvements to mime type handling in `create-file-handler` and `create-resource-handler` [#733](https://github.com/metosin/reitit/pull/733)
|
||||
|
|
|
|||
51
README.md
51
README.md
|
|
@ -66,14 +66,14 @@ modules will continue to be released under `metosin` for compatibility purposes.
|
|||
All main modules bundled:
|
||||
|
||||
```clj
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
```
|
||||
|
||||
Optionally, the parts can be required separately.
|
||||
|
||||
Reitit requires Clojure 1.11 and Java 11.
|
||||
|
||||
Reitit is tested with the LTS releases Java 11, 17, 21 and 25
|
||||
Reitit is tested with the LTS releases Java 11, 17 and 21.
|
||||
|
||||
## Quick start
|
||||
|
||||
|
|
@ -109,7 +109,6 @@ A Ring routing app with input & output coercion using [data-specs](https://githu
|
|||
(require '[reitit.ring :as ring])
|
||||
(require '[reitit.coercion.spec])
|
||||
(require '[reitit.ring.coercion :as rrc])
|
||||
(require '[reitit.ring.middleware.exception :as exception])
|
||||
(require '[reitit.ring.middleware.muuntaja :as muuntaja])
|
||||
(require '[reitit.ring.middleware.parameters :as parameters])
|
||||
|
||||
|
|
@ -125,45 +124,39 @@ A Ring routing app with input & output coercion using [data-specs](https://githu
|
|||
;; router data affecting all routes
|
||||
{:data {:coercion reitit.coercion.spec/coercion
|
||||
:muuntaja m/instance
|
||||
:middleware [parameters/parameters-middleware ; decoding query & form params
|
||||
muuntaja/format-middleware ; content negotiation
|
||||
exception/exception-middleware ; converting exceptions to HTTP responses
|
||||
:middleware [parameters/parameters-middleware
|
||||
rrc/coerce-request-middleware
|
||||
muuntaja/format-response-middleware
|
||||
rrc/coerce-response-middleware]}})))
|
||||
```
|
||||
|
||||
Valid request:
|
||||
|
||||
```clj
|
||||
(-> (app {:request-method :get
|
||||
:uri "/api/math"
|
||||
:query-params {:x "1", :y "2"}})
|
||||
(update :body slurp))
|
||||
(app {:request-method :get
|
||||
:uri "/api/math"
|
||||
:query-params {:x "1", :y "2"}})
|
||||
; {:status 200
|
||||
; :body "{\"total\":3}"
|
||||
; :headers {"Content-Type" "application/json; charset=utf-8"}}
|
||||
; :body {:total 3}}
|
||||
```
|
||||
|
||||
Invalid request:
|
||||
|
||||
```clj
|
||||
(-> (app {:request-method :get
|
||||
:uri "/api/math"
|
||||
:query-params {:x "1", :y "a"}})
|
||||
(update :body jsonista.core/read-value))
|
||||
; {:status 400
|
||||
; :headers {"Content-Type" "application/json; charset=utf-8"}
|
||||
; :body {"spec" "(spec-tools.core/spec {:spec (clojure.spec.alpha/keys :req-un [:spec$8974/x :spec$8974/y]), :type :map, :leaf? false})"
|
||||
; "value" {"x" "1"
|
||||
; "y" "a"}
|
||||
; "problems" [{"via" ["spec$8974/y"]
|
||||
; "path" ["y"]
|
||||
; "pred" "clojure.core/int?"
|
||||
; "in" ["y"]
|
||||
; "val" "a"}]
|
||||
; "type" "reitit.coercion/request-coercion"
|
||||
; "coercion" "spec"
|
||||
; "in" ["request" "query-params"]}}
|
||||
(app {:request-method :get
|
||||
:uri "/api/math"
|
||||
:query-params {:x "1", :y "a"}})
|
||||
;{:status 400,
|
||||
; :body {:type :reitit.coercion/request-coercion,
|
||||
; :coercion :spec,
|
||||
; :spec "(spec-tools.core/spec {:spec (clojure.spec.alpha/keys :req-un [:$spec20745/x :$spec20745/y]), :type :map, :keys #{:y :x}, :keys/req #{:y :x}})",
|
||||
; :problems [{:path [:y],
|
||||
; :pred "clojure.core/int?",
|
||||
; :val "a",
|
||||
; :via [:$spec20745/y],
|
||||
; :in [:y]}],
|
||||
; :value {:x "1", :y "a"},
|
||||
; :in [:request :query-params]}}
|
||||
```
|
||||
|
||||
## More examples
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians
|
|||
All bundled:
|
||||
|
||||
```clj
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
```
|
||||
|
||||
Optionally, the parts can be required separately.
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ The default exception formatting uses `reitit.exception/exception`. It produces
|
|||
## Pretty Errors
|
||||
|
||||
```clj
|
||||
[metosin/reitit-dev "0.10.0"]
|
||||
[metosin/reitit-dev "0.9.0"]
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -75,17 +75,6 @@ Path-parameters are automatically coerced into strings, with the help of (curren
|
|||
; :path-params {:id "1"}}
|
||||
```
|
||||
|
||||
In case you want to do something like generate a template path for documentation, you can disable url-encoding:
|
||||
|
||||
```clj
|
||||
(r/match-by-name router ::user {:id "<id goes here>"} {:url-encode? false})
|
||||
; #reitit.core.Match{:template "/api/user/:id"
|
||||
; :data {:name :user/user}
|
||||
; :path "/api/user/<id goes here>"
|
||||
; :result nil
|
||||
; :path-params {:id "<id goes here>"}}
|
||||
```
|
||||
|
||||
There is also an exception throwing version:
|
||||
|
||||
```clj
|
||||
|
|
|
|||
|
|
@ -73,10 +73,9 @@ Using `create` with options to create the coercion instead of `coercion`:
|
|||
{:transformers {:body {:default reitit.coercion.malli/default-transformer-provider
|
||||
:formats {"application/json" reitit.coercion.malli/json-transformer-provider}}
|
||||
:string {:default reitit.coercion.malli/string-transformer-provider}
|
||||
:response {:default reitit.coercion.malli/default-transformer-provider
|
||||
:formats {"application/json" reitit.coercion.malli/json-transformer-provider}}}
|
||||
:response {:default reitit.coercion.malli/default-transformer-provider}}
|
||||
;; 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)
|
||||
|
|
@ -88,37 +87,7 @@ Using `create` with options to create the coercion instead of `coercion`:
|
|||
;; strip-extra-keys (affects only predefined transformers)
|
||||
:strip-extra-keys true
|
||||
;; add/set default values
|
||||
;; Can be false, true or a map of options to pass to malli.transform/default-value-transformer,
|
||||
;; for example {:malli.transform/add-optional-keys true}
|
||||
:default-values true
|
||||
;; encode-error
|
||||
:encode-error nil
|
||||
;; malli options
|
||||
:options nil})
|
||||
```
|
||||
|
||||
## Configuring humanize error messages
|
||||
|
||||
Malli humanized error messages can be configured using `:options :errors`:
|
||||
|
||||
```clj
|
||||
(reitit.coercion.malli/create
|
||||
{:options
|
||||
{:errors (assoc malli.error/default-errors
|
||||
:malli.core/missing-key {:error/message {:en "MISSING"}})}})
|
||||
```
|
||||
|
||||
See the malli docs for more info.
|
||||
|
||||
## Custom registry
|
||||
|
||||
Malli registry can be configured conveniently via `:options :registry`:
|
||||
|
||||
```clj
|
||||
(require '[malli.core :as m])
|
||||
|
||||
(reitit.coercion.malli/create
|
||||
{:options
|
||||
{:registry {:registry (merge (m/default-schemas)
|
||||
{:my-type :string})}}})
|
||||
```
|
||||
|
|
|
|||
|
|
@ -25,14 +25,6 @@ clojure-lsp clean-ns
|
|||
The documentation lives under `doc` and it is hosted on [cljdoc](https://cljdoc.org). See their
|
||||
documentation for [library authors](https://github.com/cljdoc/cljdoc/blob/master/doc/userguide/for-library-authors.adoc)
|
||||
|
||||
## Updating deps
|
||||
|
||||
|
||||
* `lein ancient upgrade`
|
||||
* Mention non-dev non-test dep upgrades in CHANGELOG.md
|
||||
* `npm update --save`
|
||||
* Make a PR, run CI
|
||||
|
||||
## Making a release
|
||||
|
||||
We use [Break Versioning][breakver]. Remember our promise: patch-level bumps never include breaking changes!
|
||||
|
|
@ -40,20 +32,29 @@ We use [Break Versioning][breakver]. Remember our promise: patch-level bumps nev
|
|||
[breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md
|
||||
|
||||
```bash
|
||||
# create a release commit
|
||||
# Check that you're using Java 8! Making the release with a newer Java version
|
||||
# means that it is broken when used with Java 8.
|
||||
java -version
|
||||
|
||||
# new version
|
||||
./scripts/set-version "1.0.0"
|
||||
|
||||
# !!! update the changelog
|
||||
|
||||
# create a release commit and a tag
|
||||
git add -u
|
||||
git commit -m "Release 1.0.0"
|
||||
git tag 1.0.0
|
||||
|
||||
# push the commit
|
||||
# works
|
||||
./scripts/lein-modules install
|
||||
lein test
|
||||
|
||||
# deploy to clojars
|
||||
CLOJARS_USERNAME=*** CLOJARS_PASSWORD=*** ./scripts/lein-modules do clean, deploy clojars
|
||||
|
||||
# push the commit and the tag
|
||||
git push
|
||||
|
||||
# !!! check that tests pass on CI
|
||||
git push --tags
|
||||
```
|
||||
|
||||
* Create a new release on github at <https://github.com/metosin/reitit/releases>
|
||||
* This will trigger the automated release workflow <https://github.com/metosin/reitit/actions/workflows/release.yml>
|
||||
* Remembor to update the changelog!
|
||||
* Announce the release at least on #reitit in Clojurians.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Default Interceptors
|
||||
|
||||
```clj
|
||||
[metosin/reitit-interceptors "0.10.0"]
|
||||
[metosin/reitit-interceptors "0.9.0"]
|
||||
```
|
||||
|
||||
Just like the [ring default middleware](../ring/default_middleware.md), but for interceptors.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Reitit has also support for [interceptors](http://pedestal.io/reference/intercep
|
|||
## Reitit-http
|
||||
|
||||
```clj
|
||||
[metosin/reitit-http "0.10.0"]
|
||||
[metosin/reitit-http "0.9.0"]
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
[Pedestal](http://pedestal.io/) is a backend web framework for Clojure. `reitit-pedestal` provides an alternative routing engine for Pedestal.
|
||||
|
||||
```clj
|
||||
[metosin/reitit-pedestal "0.10.0"]
|
||||
[metosin/reitit-pedestal "0.9.0"]
|
||||
```
|
||||
|
||||
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
|
||||
; [io.pedestal/pedestal.service "0.5.5"]
|
||||
; [io.pedestal/pedestal.jetty "0.5.5"]
|
||||
; [metosin/reitit-pedestal "0.10.0"]
|
||||
; [metosin/reitit "0.10.0"]
|
||||
; [metosin/reitit-pedestal "0.9.0"]
|
||||
; [metosin/reitit "0.9.0"]
|
||||
|
||||
(require '[io.pedestal.http :as server])
|
||||
(require '[reitit.pedestal :as pedestal])
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Sieppari
|
||||
|
||||
```clj
|
||||
[metosin/reitit-sieppari "0.10.0"]
|
||||
[metosin/reitit-sieppari "0.9.0"]
|
||||
```
|
||||
|
||||
[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).
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ There is an extra option in http-router (actually, in the underlying interceptor
|
|||
### Printing Context Diffs
|
||||
|
||||
```clj
|
||||
[metosin/reitit-interceptors "0.10.0"]
|
||||
[metosin/reitit-interceptors "0.9.0"]
|
||||
```
|
||||
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ Handlers can access the coerced parameters via the `:parameters` key in the requ
|
|||
{:status 200
|
||||
:body {:total total}}))})
|
||||
```
|
||||
|
||||
|
||||
### Nested parameter definitions
|
||||
|
||||
Parameters are accumulated recursively along the route tree, just like
|
||||
|
|
@ -92,26 +94,6 @@ handling for merging eg. malli `:map` schemas.
|
|||
; [:task-id :int]]}
|
||||
```
|
||||
|
||||
### Differences in behaviour for different parameters
|
||||
|
||||
All parameter coercions *except* `:body`:
|
||||
|
||||
1. Allow keys outside the schema (by opening up the schema using eg. `malli.util/open-schema`)
|
||||
2. Keywordize the keys (ie. header & query parameter names) of the input before coercing
|
||||
|
||||
In contrast, the `:body` coercion:
|
||||
|
||||
1. Uses the specified schema
|
||||
* depending on the coercion, it can be configured as open or closed, see specific coercion docs for details
|
||||
2. Does not keywordize the keys of the input before coercion
|
||||
* however, coercions like malli might do the keywordization when coercing json bodies, depending on configuration
|
||||
|
||||
This admittedly confusing behaviour is retained currently due to
|
||||
backwards compatibility reasons. It can be configured by passing
|
||||
option `:reitit.coercion/parameter-coercion` to `reitit.ring/router`
|
||||
or `reitit.coercion/compile-request-coercers`. See also:
|
||||
`reitit.coercion/default-parameter-coercion`.
|
||||
|
||||
## Coercion Middleware
|
||||
|
||||
Defining a coercion for a route data doesn't do anything, as it's just data. We have to attach some code to apply the actual coercion. We can use the middleware from `reitit.ring.coercion`:
|
||||
|
|
@ -220,11 +202,9 @@ is:
|
|||
"application/edn" {:schema {:x s/Int}}
|
||||
:default {:schema {:ww s/Int}}}}}
|
||||
:handler ...}}]]
|
||||
{:data {:muuntaja muuntaja.core/instance
|
||||
:middleware [reitit.ring.middleware.muuntaja/format-middleware
|
||||
reitit.ring.coercion/coerce-exceptions-middleware
|
||||
reitit.ring.coercion/coerce-request-middleware
|
||||
reitit.ring.coercion/coerce-response-middleware]}})))
|
||||
{:data {:middleware [rrc/coerce-exceptions-middleware
|
||||
rrc/coerce-request-middleware
|
||||
rrc/coerce-response-middleware]}})))
|
||||
```
|
||||
|
||||
The resolution logic for response coercers is:
|
||||
|
|
@ -235,17 +215,6 @@ The resolution logic for response coercers is:
|
|||
3. `:body`
|
||||
3. If nothing was found, do not coerce
|
||||
|
||||
To select the response content-type, you can either:
|
||||
1. Let muuntaja pick the content-type based on things like the request Accept header
|
||||
- This is what most users want
|
||||
2. Set `:muuntaja/content-type` in the response to pick an explicit content type
|
||||
3. Set the `"Content-Type"` header in the response
|
||||
- This disables muuntaja, so you need to encode your response body in some other way!
|
||||
- This is not compatible with response schema checking, since coercion won't know what to do with the already-encoded response body.
|
||||
4. Use the `:extract-response-format` option to inject your own logic. See `reitit.coercion/extract-response-format-default` for the default.
|
||||
|
||||
See also the [muuntaja content negotiation](./content_negotiation.md) docs.
|
||||
|
||||
## Pretty printing spec errors
|
||||
|
||||
Spec problems are exposed as is in request & response coercion errors. Pretty-printers like [expound](https://github.com/bhb/expound) can be enabled like this:
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
Ring [defines middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware) as a function of type `handler & args => request => response`. It is relatively easy to understand and allows for good performance. A downside is that the middleware chain is just a opaque function, making things like debugging and composition hard. It is too easy to apply the middlewares in wrong order.
|
||||
|
||||
For the basics of reitit middleware, [read this first](ring.md#middleware).
|
||||
|
||||
Reitit defines middleware as data:
|
||||
|
||||
1. A middleware can be defined as first-class data entries
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Default Middleware
|
||||
|
||||
```clj
|
||||
[metosin/reitit-middleware "0.10.0"]
|
||||
[metosin/reitit-middleware "0.9.0"]
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
@ -17,6 +17,8 @@ Any Ring middleware can be used with `reitit-ring`, but using data-driven middle
|
|||
`reitit.ring.middleware.parameters/parameters-middleware` to capture query- and form-params. Wraps
|
||||
`ring.middleware.params/wrap-params`.
|
||||
|
||||
**NOTE**: This middleware will be factored into two parts: a query-parameters middleware and a Muuntaja format responsible for the the `application/x-www-form-urlencoded` body format. cf. https://github.com/metosin/reitit/issues/134
|
||||
|
||||
## Exception Handling
|
||||
|
||||
See [Exception Handling with Ring](exceptions.md).
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Exception Handling with Ring
|
||||
|
||||
```clj
|
||||
[metosin/reitit-middleware "0.10.0"]
|
||||
[metosin/reitit-middleware "0.9.0"]
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -52,20 +52,6 @@ Router creation fails fast if the registry doesn't contain the middleware:
|
|||
;| :bonus | reitit.ring_test$wrap_bonus@59fddabb |
|
||||
```
|
||||
|
||||
Middleware defined in the registry can also be used on the `ring-handler` level:
|
||||
|
||||
```clj
|
||||
(def app
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
["/api"
|
||||
["/bonus" {:get (fn [{:keys [bonus]}]
|
||||
{:status 200, :body {:bonus bonus}})}]]
|
||||
{::middleware/registry {:bonus wrap-bonus}})
|
||||
nil
|
||||
{:middleware [[:bonus 15]]}))
|
||||
```
|
||||
|
||||
## When to use the registry?
|
||||
|
||||
Middleware as Keywords helps to keep the routes (all but handlers) as literal data (i.e. data that evaluates to itself), enabling the routes to be persisted in external formats like EDN-files and databases. Duct is a good example, where the [middleware can be referenced from EDN-files](https://github.com/duct-framework/duct/wiki/Configuration). It should be easy to make Duct configuration a Middleware Registry in `reitit-ring`.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
Read more about the [Ring Concepts](https://github.com/ring-clojure/ring/wiki/Concepts).
|
||||
|
||||
```clj
|
||||
[metosin/reitit-ring "0.10.0"]
|
||||
[metosin/reitit-ring "0.9.0"]
|
||||
```
|
||||
|
||||
## `reitit.ring/router`
|
||||
|
|
@ -141,7 +141,7 @@ Name-based reverse routing:
|
|||
|
||||
# Middleware
|
||||
|
||||
Middleware can be mounted using a `:middleware` key in [Route Data](../basics/route_data.md) - either to top-level or under request method submap. Its value should be a vector of `reitit.middleware/IntoMiddleware` values. These include:
|
||||
Middleware can be mounted using a `:middleware` key - either to top-level or under request method submap. Its value should be a vector of `reitit.middleware/IntoMiddleware` values. These include:
|
||||
|
||||
1. normal ring middleware function `handler -> request -> response`
|
||||
2. vector of middleware function `[handler args*] -> request -> response` and it's arguments
|
||||
|
|
@ -194,56 +194,11 @@ Top-level middleware, applied before any routing is done:
|
|||
(def app
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
["/api" {:middleware [[wrap :api]]}
|
||||
["/api" {:middleware [[mw :api]]}
|
||||
["/get" {:get handler}]])
|
||||
nil
|
||||
{:middleware [[wrap :top]]}))
|
||||
{:middleware [[mw :top]]}))
|
||||
|
||||
(app {:request-method :get, :uri "/api/get"})
|
||||
; {:status 200, :body [:top :api :ok]}
|
||||
```
|
||||
|
||||
Same middleware for all routes, using [top-level route data](route_data.md#top-level-route-data):
|
||||
|
||||
```clj
|
||||
(def app
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
["/api"
|
||||
["/get" {:get handler
|
||||
:middleware [[wrap :specific]]}]]
|
||||
{:data {:middleware [[wrap :generic]]}})))
|
||||
|
||||
(app {:request-method :get, :uri "/api/get"})
|
||||
; {:status 200, :body [:generic :specific :handler]}
|
||||
```
|
||||
|
||||
## Execution order
|
||||
|
||||
Here's a full example that shows the execution order of the middleware
|
||||
using all of the above techniques:
|
||||
|
||||
|
||||
```clj
|
||||
(def app
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
["/api" {:middleware [[wrap :3-parent]]}
|
||||
["/get" {:get handler
|
||||
:middleware [[wrap :4-route]]}]]
|
||||
{:data {:middleware [[wrap :2-top-level-route-data]]}})
|
||||
nil
|
||||
{:middleware [[wrap :1-top]]}))
|
||||
|
||||
(app {:request-method :get, :uri "/api/get"})
|
||||
; {:status 200, :body [:1-top :2-top-level-route-data :3-parent :4-route :handler]}
|
||||
```
|
||||
|
||||
## Which method should I use for defining middleware?
|
||||
|
||||
- If you have middleware that you want to apply to the default handler (second argument of `ring/ring-handler`), use _top-level middleware_
|
||||
- If you have a generic middleware, that doesn't depend on the route, use _top-level middleware_ or _top-level route data_
|
||||
- If you are using top-level route data anyway for some other reasons, it might be clearest to have all the middleware there. This is what most of the reitit examples do.
|
||||
- If you want to apply a middleware to only a couple of routes, use _nested middleware_ (ie. _route data_)
|
||||
- If you want a middleware to apply to all routes, but use route-specific data, you need _top-level route data_ combined with [Compiling Middleware](compiling_middleware.md)
|
||||
- This is what many reitit features like [Ring Coercion](coercion.md) do. Check the examples & docs for the reitit features you want to use!
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Swagger Support
|
||||
|
||||
```
|
||||
[metosin/reitit-swagger "0.10.0"]
|
||||
[metosin/reitit-swagger "0.9.0"]
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
@ -47,7 +47,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.
|
||||
|
||||
```
|
||||
[metosin/reitit-swagger-ui "0.10.0"]
|
||||
[metosin/reitit-swagger-ui "0.9.0"]
|
||||
```
|
||||
|
||||
`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:
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ There is an extra option in the Ring router (actually, in the underlying middlew
|
|||
### Printing Request Diffs
|
||||
|
||||
```clj
|
||||
[metosin/reitit-middleware "0.10.0"]
|
||||
[metosin/reitit-middleware "0.9.0"]
|
||||
```
|
||||
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@
|
|||
:description "Reitit Buddy Auth App"
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
[buddy "2.0.0"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit-schema "0.10.0"]
|
||||
[metosin/reitit-frontend "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
[metosin/reitit-schema "0.9.0"]
|
||||
[metosin/reitit-frontend "0.9.0"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit-schema "0.10.0"]
|
||||
[metosin/reitit-frontend "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
[metosin/reitit-schema "0.9.0"]
|
||||
[metosin/reitit-frontend "0.9.0"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.10.520"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit-spec "0.10.0"]
|
||||
[metosin/reitit-frontend "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
[metosin/reitit-spec "0.9.0"]
|
||||
[metosin/reitit-frontend "0.9.0"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit-malli "0.10.0"]
|
||||
[metosin/reitit-frontend "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
[metosin/reitit-malli "0.9.0"]
|
||||
[metosin/reitit-frontend "0.9.0"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit-spec "0.10.0"]
|
||||
[metosin/reitit-frontend "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
[metosin/reitit-spec "0.9.0"]
|
||||
[metosin/reitit-frontend "0.9.0"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
(defproject frontend-re-frame "0.1.0-SNAPSHOT"
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
[reagent "1.2.0"]
|
||||
[re-frame "0.10.6"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
(ns frontend-re-frame.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
[reagent.dom :as rd]
|
||||
[reitit.core :as r]
|
||||
[reitit.coercion.spec :as rss]
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit-spec "0.10.0"]
|
||||
[metosin/reitit-frontend "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
[metosin/reitit-spec "0.9.0"]
|
||||
[metosin/reitit-frontend "0.9.0"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[aleph "0.7.1"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
[metosin/ring-swagger-ui "5.9.0"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@
|
|||
[funcool/promesa "11.0.678"]
|
||||
[manifold "0.4.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.10.0"]]
|
||||
[metosin/reitit "0.9.0"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
:description "Reitit coercion with vanilla ring"
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.10.0"]])
|
||||
[metosin/reitit "0.9.0"]])
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[metosin/jsonista "0.3.8"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/ring-swagger-ui "5.9.0"]
|
||||
[org.slf4j/slf4j-simple "2.0.9"]]
|
||||
[metosin/reitit "0.9.0"]
|
||||
[metosin/ring-swagger-ui "5.9.0"]]
|
||||
:repl-options {:init-ns example.server}
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}})
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
[reitit.ring.middleware.multipart :as multipart]
|
||||
[reitit.ring.middleware.parameters :as parameters]
|
||||
[ring.adapter.jetty :as jetty]
|
||||
[malli.core :as malli]
|
||||
[muuntaja.core :as m]))
|
||||
|
||||
(def Transaction
|
||||
|
|
@ -51,34 +52,23 @@
|
|||
{:get {:summary "Fetch a pizza | Multiple content-types, multiple examples"
|
||||
:responses {200 {:description "Fetch a pizza as json or EDN"
|
||||
:content {"application/json" {:schema [:map
|
||||
[:format [:enum :json]]
|
||||
[:color :keyword]
|
||||
[:pineapple :boolean]]
|
||||
:examples {:white {:description "White pizza with pineapple"
|
||||
:value {:format :json
|
||||
:color :white
|
||||
:value {:color :white
|
||||
:pineapple true}}
|
||||
:red {:description "Red pizza"
|
||||
:value {:format :json
|
||||
:color :red
|
||||
:value {:color :red
|
||||
:pineapple false}}}}
|
||||
"application/edn" {:schema [:map
|
||||
[:format [:enum :edn]]
|
||||
[:color :keyword]
|
||||
[:pineapple :boolean]]
|
||||
:examples {:red {:description "Red pizza with pineapple"
|
||||
:value (pr-str {:format :edn :color :red :pineapple true})}}}}}}
|
||||
:value (pr-str {:color :red :pineapple true})}}}}}}
|
||||
:handler (fn [_request]
|
||||
(rand-nth [{:status 200
|
||||
:muuntaja/content-type "application/json"
|
||||
:body {:format :json
|
||||
:color :red
|
||||
:pineapple true}}
|
||||
{:status 200
|
||||
:muuntaja/content-type "application/edn"
|
||||
:body {:format :edn
|
||||
:color :red
|
||||
:pineapple true}}]))}
|
||||
{:status 200
|
||||
:body {:color :red
|
||||
:pineapple true}})}
|
||||
:post {:summary "Create a pizza | Multiple content-types, multiple examples | Default response schema"
|
||||
:request {:description "Create a pizza using json or EDN"
|
||||
:content {"application/json" {:schema [:map
|
||||
|
|
@ -156,22 +146,22 @@
|
|||
[:regex [:re "[0-9]+"]]
|
||||
[:enum [:enum 1 3 5 42]]
|
||||
[:multi [:multi {:dispatch :type}
|
||||
["literal" [:map
|
||||
[:type [:= "literal"]]
|
||||
[:literal [:map
|
||||
[:type [:= :literal]]
|
||||
[:value [:or :int :string]]]]
|
||||
["reference" [:map
|
||||
[:type [:= "reference"]]
|
||||
[:reference [:map
|
||||
[:type [:= :reference]]
|
||||
[:description :string]
|
||||
[:ref :uuid]]]]]]
|
||||
:example {:vector-of-tuples [["a" 1] ["b" 2]]
|
||||
:regex "01234"
|
||||
:enum 5
|
||||
:multi {:type "literal"
|
||||
:multi {:type :literal
|
||||
:value "x"}}}}}
|
||||
:responses {200 {:content {:default {:schema [:map-of :keyword :any]}}}}
|
||||
:responses {200 {:content {:default {:schema [:map]}}}}
|
||||
:handler (fn [request]
|
||||
{:status 200
|
||||
:body (get-in request [:parameters :request])})}}]
|
||||
:body (:body request)})}}]
|
||||
|
||||
["/secure"
|
||||
{:tags #{"secure"}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[io.pedestal/pedestal.service "0.6.3"]
|
||||
[io.pedestal/pedestal.jetty "0.6.3"]
|
||||
[metosin/reitit-malli "0.10.0"]
|
||||
[metosin/reitit-pedestal "0.10.0"]
|
||||
[metosin/reitit "0.10.0"]]
|
||||
[metosin/reitit-malli "0.9.0"]
|
||||
[metosin/reitit-pedestal "0.9.0"]
|
||||
[metosin/reitit "0.9.0"]]
|
||||
:repl-options {:init-ns server})
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[io.pedestal/pedestal.service "0.6.3"]
|
||||
[io.pedestal/pedestal.jetty "0.6.3"]
|
||||
[metosin/reitit-pedestal "0.10.0"]
|
||||
[metosin/reitit "0.10.0"]]
|
||||
[metosin/reitit-pedestal "0.9.0"]
|
||||
[metosin/reitit "0.9.0"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[io.pedestal/pedestal.service "0.6.3"]
|
||||
[io.pedestal/pedestal.jetty "0.6.3"]
|
||||
[metosin/reitit-pedestal "0.10.0"]
|
||||
[metosin/reitit "0.10.0"]]
|
||||
[metosin/reitit-pedestal "0.9.0"]
|
||||
[metosin/reitit "0.9.0"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -2,5 +2,5 @@
|
|||
:description "Reitit Ring App"
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.10.0"]]
|
||||
[metosin/reitit "0.9.0"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
:description "Reitit Ring App with Integrant"
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
[integrant "0.8.1"]]
|
||||
:main example.server
|
||||
:repl-options {:init-ns user}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[metosin/jsonista "0.3.8"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.10.0"]]
|
||||
[metosin/reitit "0.9.0"]]
|
||||
:repl-options {:init-ns example.server}
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}})
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[metosin/jsonista "0.3.8"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
[metosin/ring-swagger-ui "5.9.0"]]
|
||||
:repl-options {:init-ns example.server}
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}})
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
:description "Reitit Ring App with Swagger"
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit "0.9.0"]
|
||||
[metosin/ring-swagger-ui "5.9.0"]]
|
||||
:repl-options {:init-ns example.server}
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-core "0.10.0"
|
||||
(defproject metosin/reitit-core "0.9.0"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -152,10 +152,8 @@
|
|||
rcs (request-coercers coercion parameters (cond-> opts route-request (assoc ::skip #{:body})))]
|
||||
(if (and crc rcs) (into crc (vec rcs)) (or crc rcs)))))
|
||||
|
||||
(defn extract-response-format-default [request response]
|
||||
(or (get-in response [:headers "Content-Type"])
|
||||
(:muuntaja/content-type response)
|
||||
(-> request :muuntaja/response :format)))
|
||||
(defn extract-response-format-default [request _]
|
||||
(-> request :muuntaja/response :format))
|
||||
|
||||
(defn -format->coercer [coercion {:keys [content body]} _opts]
|
||||
(->> (concat (when body
|
||||
|
|
@ -180,9 +178,8 @@
|
|||
(let [format->coercer (or (status->format->coercer (:status response))
|
||||
(status->format->coercer :default))
|
||||
format (extract-response-format request response)
|
||||
coercer (when format->coercer
|
||||
(or (format->coercer format)
|
||||
(format->coercer :default)))]
|
||||
coercer (or (format->coercer format)
|
||||
(format->coercer :default))]
|
||||
(if-not coercer
|
||||
response
|
||||
(let [value (:body response)
|
||||
|
|
@ -244,7 +241,7 @@
|
|||
(if coercer
|
||||
(let [result (coercer query-params :default)]
|
||||
(if (error? result)
|
||||
(throw (ex-info "Query parameters coercion failed"
|
||||
(throw (ex-info (str "Query parameters coercion failed")
|
||||
result))
|
||||
result))
|
||||
query-params))))
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
(options [this])
|
||||
(route-names [this])
|
||||
(match-by-path [this path])
|
||||
(match-by-name [this name] [this name path-params] [this name path-params opts]))
|
||||
(match-by-name [this name] [this name path-params]))
|
||||
|
||||
(defn router? [x]
|
||||
(satisfies? Router x))
|
||||
|
|
@ -122,11 +122,9 @@
|
|||
(match-by-name [_ name]
|
||||
(if-let [match (impl/fast-get lookup name)]
|
||||
(match nil)))
|
||||
(match-by-name [r name path-params]
|
||||
(match-by-name r name path-params nil))
|
||||
(match-by-name [_ name path-params opts]
|
||||
(match-by-name [_ name path-params]
|
||||
(if-let [match (impl/fast-get lookup name)]
|
||||
(match (impl/path-params path-params opts))))))))
|
||||
(match (impl/path-params path-params))))))))
|
||||
|
||||
(defn lookup-router
|
||||
"Creates a lookup-router from resolved routes and optional
|
||||
|
|
@ -163,11 +161,9 @@
|
|||
(match-by-name [_ name]
|
||||
(if-let [match (impl/fast-get lookup name)]
|
||||
(match nil)))
|
||||
(match-by-name [r name path-params]
|
||||
(match-by-name r name path-params nil))
|
||||
(match-by-name [_ name path-params opts]
|
||||
(match-by-name [_ name path-params]
|
||||
(if-let [match (impl/fast-get lookup name)]
|
||||
(match (impl/path-params path-params opts))))))))
|
||||
(match (impl/path-params path-params))))))))
|
||||
|
||||
(defn trie-router
|
||||
"Creates a special prefix-tree router from resolved routes and optional
|
||||
|
|
@ -212,11 +208,9 @@
|
|||
(match-by-name [_ name]
|
||||
(if-let [match (impl/fast-get lookup name)]
|
||||
(match nil)))
|
||||
(match-by-name [r name path-params]
|
||||
(match-by-name r name path-params nil))
|
||||
(match-by-name [_ name path-params opts]
|
||||
(match-by-name [_ name path-params]
|
||||
(if-let [match (impl/fast-get lookup name)]
|
||||
(match (impl/path-params path-params opts))))))))
|
||||
(match (impl/path-params path-params))))))))
|
||||
|
||||
(defn single-static-path-router
|
||||
"Creates a fast router of 1 static route(s) and optional
|
||||
|
|
@ -244,10 +238,8 @@
|
|||
(if (#?(:clj .equals :cljs =) p path) match))
|
||||
(match-by-name [_ name]
|
||||
(if (= n name) match))
|
||||
(match-by-name [r name path-params]
|
||||
(match-by-name r name path-params nil))
|
||||
(match-by-name [_ name path-params opts]
|
||||
(if (= n name) (impl/fast-assoc match :path-params (impl/path-params path-params opts))))))))
|
||||
(match-by-name [_ name path-params]
|
||||
(if (= n name) (impl/fast-assoc match :path-params (impl/path-params path-params))))))))
|
||||
|
||||
(defn mixed-router
|
||||
"Creates two routers: [[lookup-router]] or [[single-static-path-router]] for
|
||||
|
|
@ -276,11 +268,9 @@
|
|||
(match-by-name [_ name]
|
||||
(or (match-by-name static-router name)
|
||||
(match-by-name wildcard-router name)))
|
||||
(match-by-name [r name path-params]
|
||||
(match-by-name r name path-params nil))
|
||||
(match-by-name [_ name path-params opts]
|
||||
(or (match-by-name static-router name path-params opts)
|
||||
(match-by-name wildcard-router name path-params opts)))))))
|
||||
(match-by-name [_ name path-params]
|
||||
(or (match-by-name static-router name path-params)
|
||||
(match-by-name wildcard-router name path-params)))))))
|
||||
|
||||
(defn quarantine-router
|
||||
"Creates two routers: [[mixed-router]] for non-conflicting routes
|
||||
|
|
@ -309,11 +299,9 @@
|
|||
(match-by-name [_ name]
|
||||
(or (match-by-name mixed-router name)
|
||||
(match-by-name linear-router name)))
|
||||
(match-by-name [r name path-params]
|
||||
(match-by-name r name path-params nil))
|
||||
(match-by-name [_ name path-params opts]
|
||||
(or (match-by-name mixed-router name path-params opts)
|
||||
(match-by-name linear-router name path-params opts)))))))
|
||||
(match-by-name [_ name path-params]
|
||||
(or (match-by-name mixed-router name path-params)
|
||||
(match-by-name linear-router name path-params)))))))
|
||||
|
||||
;;
|
||||
;; Creating Routers
|
||||
|
|
|
|||
52
modules/reitit-core/src/reitit/dependency.cljc
Normal file
52
modules/reitit-core/src/reitit/dependency.cljc
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
(ns reitit.dependency
|
||||
"Dependency resolution for middleware/interceptors."
|
||||
(:require [reitit.exception :as exception]))
|
||||
|
||||
(defn- providers
|
||||
"Map from provision key to provider. `get-provides` should return the provision keys of a dependent."
|
||||
[get-provides nodes]
|
||||
(reduce (fn [acc dependent]
|
||||
(into acc
|
||||
(map (fn [provide]
|
||||
(when (contains? acc provide)
|
||||
(exception/fail!
|
||||
(str "multiple providers for: " provide)
|
||||
{::multiple-providers provide}))
|
||||
[provide dependent]))
|
||||
(get-provides dependent)))
|
||||
{} nodes))
|
||||
|
||||
(defn- get-provider
|
||||
"Get the provider for `k`, throw if no provider can be found for it."
|
||||
[providers k]
|
||||
(if (contains? providers k)
|
||||
(get providers k)
|
||||
(exception/fail!
|
||||
(str "provider missing for dependency: " k)
|
||||
{::missing-provider k})))
|
||||
|
||||
(defn post-order
|
||||
"Put `nodes` in post-order. Can also be described as a reverse topological sort.
|
||||
`get-provides` and `get-requires` are callbacks that you can provide to compute the provide and depend
|
||||
key sets of nodes, the defaults are `:provides` and `:requires`."
|
||||
([nodes] (post-order :provides :requires nodes))
|
||||
([get-provides get-requires nodes]
|
||||
(let [providers-by-key (providers get-provides nodes)]
|
||||
(letfn [(toposort [node path colors]
|
||||
(case (get colors node)
|
||||
:white (let [requires (get-requires node)
|
||||
[nodes* colors] (toposort-seq (map (partial get-provider providers-by-key) requires)
|
||||
(conj path node)
|
||||
(assoc colors node :grey))]
|
||||
[(conj nodes* node)
|
||||
(assoc colors node :black)])
|
||||
:grey (exception/fail! "circular dependency" {:cycle (drop-while #(not= % node) (conj path node))})
|
||||
:black [() colors]))
|
||||
|
||||
(toposort-seq [nodes path colors]
|
||||
(reduce (fn [[nodes* colors] node]
|
||||
(let [[nodes** colors] (toposort node path colors)]
|
||||
[(into nodes* nodes**) colors]))
|
||||
[[] colors] nodes))]
|
||||
|
||||
(first (toposort-seq nodes [] (zipmap nodes (repeat :white))))))))
|
||||
|
|
@ -7,10 +7,6 @@
|
|||
([type data]
|
||||
(throw (ex-info (str type) {:type type, :data data}))))
|
||||
|
||||
(defn unsupported-protocol-method!
|
||||
[method]
|
||||
(fail! :unsupported-protocol-method {:method method}))
|
||||
|
||||
(defn get-message [e]
|
||||
#?(:clj (.getMessage ^Exception e) :cljs (ex-message e)))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
(ns ^:no-doc reitit.impl
|
||||
#?(:cljs (:require-macros [reitit.impl]))
|
||||
(:require [clojure.string :as str]
|
||||
(:require [clojure.set :as set]
|
||||
[clojure.string :as str]
|
||||
[meta-merge.core :as mm]
|
||||
[reitit.exception :as ex]
|
||||
[reitit.trie :as trie])
|
||||
|
|
@ -197,8 +198,9 @@
|
|||
(:path route)))
|
||||
|
||||
(defn throw-on-missing-path-params [template required path-params]
|
||||
(let [missing (set (remove #(get path-params %) required))]
|
||||
(when-not (empty? missing)
|
||||
(when-not (every? #(contains? path-params %) required)
|
||||
(let [defined (-> path-params keys set)
|
||||
missing (set/difference required defined)]
|
||||
(ex/fail!
|
||||
(str "missing path-params for route " template " -> " missing)
|
||||
{:path-params path-params, :required required}))))
|
||||
|
|
@ -297,11 +299,8 @@
|
|||
|
||||
(defn path-params
|
||||
"Convert parameters' values into URL-encoded strings, suitable for URL paths"
|
||||
([params] (path-params params nil))
|
||||
([params {:keys [url-encode?] :or {url-encode? true}}]
|
||||
(if url-encode?
|
||||
(maybe-map-values #(url-encode (into-string %)) params)
|
||||
(maybe-map-values #(into-string %) params))))
|
||||
[params]
|
||||
(maybe-map-values #(url-encode (into-string %)) params))
|
||||
|
||||
(defn- query-parameter [k v]
|
||||
(str (form-encode (into-string k))
|
||||
|
|
|
|||
|
|
@ -37,11 +37,8 @@
|
|||
;; Default data
|
||||
;;
|
||||
|
||||
(defn -multi? [x]
|
||||
(instance? #?(:clj clojure.lang.MultiFn :cljs cljs.core.MultiFn) x))
|
||||
|
||||
(s/def ::name keyword?)
|
||||
(s/def ::handler (s/or :fn fn? :var var? :multi -multi?))
|
||||
(s/def ::handler (s/or :fn fn? :var var?))
|
||||
(s/def ::no-doc boolean?)
|
||||
(s/def ::conflicting boolean?)
|
||||
(s/def ::default-data
|
||||
|
|
|
|||
|
|
@ -71,17 +71,11 @@
|
|||
|
||||
(and bracket? (= \{ c))
|
||||
(let [^long to' (or (str/index-of s "}" to) (ex/fail! ::unclosed-brackets {:path s}))]
|
||||
(cond
|
||||
(= \* (get s (inc to)))
|
||||
(if (= \* (get s (inc to)))
|
||||
(recur (concat ss (-static from to) (-catch-all (inc to) to')) (long (inc to')) (long (inc to')))
|
||||
|
||||
(= \: (get s (inc to)))
|
||||
(recur (concat ss (-static from to) (-wild (inc to) to')) (long (inc to')) (long (inc to')))
|
||||
|
||||
:else
|
||||
(recur (concat ss (-static from to) (-wild to to')) (long (inc to')) (long (inc to')))))
|
||||
|
||||
(and colon? (= \: c) (not= \{ (get s (dec to))))
|
||||
(and colon? (= \: c))
|
||||
(let [^long to' (or (str/index-of s "/" to) (count s))]
|
||||
(if (= 1 (- to' to))
|
||||
(recur ss from (inc to))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-dev "0.10.0"
|
||||
(defproject metosin/reitit-dev "0.9.0"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
;; TODO: this is hack, but seems to work and is safe.
|
||||
(defn source-str [[target _ file line]]
|
||||
(try
|
||||
(if (not= 1 line)
|
||||
(if (and (not= 1 line))
|
||||
(let [file-name (str/replace file #"(.*?)\.\S[^\.]+" "$1")
|
||||
target-name (name target)
|
||||
ns (str (subs target-name 0 (or (str/index-of target-name (str file-name "$")) 0)) file-name)]
|
||||
|
|
@ -190,7 +190,7 @@
|
|||
(color :title message " ")
|
||||
(color :title-dark (repeat-str "-" between) " ")
|
||||
(color :title source) " "
|
||||
(color :title-dark "--")]))
|
||||
(color :title-dark (str "--"))]))
|
||||
|
||||
(defn footer [{:keys [width]}]
|
||||
(color :title-dark (repeat-str "-" width)))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-frontend "0.10.0"
|
||||
(defproject metosin/reitit-frontend "0.9.0"
|
||||
:description "Reitit: Clojurescript frontend routing core"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-http "0.10.0"
|
||||
(defproject metosin/reitit-http "0.9.0"
|
||||
:description "Reitit: HTTP routing with interceptors"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -22,12 +22,12 @@
|
|||
compile (fn [[path data] opts scope]
|
||||
(interceptor/compile-result [path data] opts scope))
|
||||
->endpoint (fn [p d m s]
|
||||
(let [d (ring/-compile-coercion d)
|
||||
compiled (compile [p d] opts s)]
|
||||
(-> compiled
|
||||
(map->Endpoint)
|
||||
(assoc :path p)
|
||||
(assoc :method m))))
|
||||
(let [d (ring/-compile-coercion d)]
|
||||
(let [compiled (compile [p d] opts s)]
|
||||
(-> compiled
|
||||
(map->Endpoint)
|
||||
(assoc :path p)
|
||||
(assoc :method m)))))
|
||||
->methods (fn [any? data]
|
||||
(reduce
|
||||
(fn [acc method]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-interceptors "0.10.0"
|
||||
(defproject metosin/reitit-interceptors "0.9.0"
|
||||
:description "Reitit, common interceptors bundled"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-malli "0.10.0"
|
||||
(defproject metosin/reitit-malli "0.9.0"
|
||||
:description "Reitit: Malli coercion"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
[malli.swagger :as swagger]
|
||||
[malli.transform :as mt]
|
||||
[malli.util :as mu]
|
||||
[reitit.coercion :as coercion]))
|
||||
[reitit.coercion :as coercion]
|
||||
[clojure.string :as string]))
|
||||
|
||||
;;
|
||||
;; coercion
|
||||
|
|
@ -30,7 +31,7 @@
|
|||
(mt/transformer
|
||||
(if strip-extra-keys (mt/strip-extra-keys-transformer))
|
||||
transformer
|
||||
(if default-values (mt/default-value-transformer (if (map? default-values) default-values {})))))))
|
||||
(if default-values (mt/default-value-transformer))))))
|
||||
|
||||
(def string-transformer-provider (-provider (mt/string-transformer)))
|
||||
(def json-transformer-provider (-provider (mt/json-transformer)))
|
||||
|
|
@ -115,9 +116,7 @@
|
|||
:enabled true
|
||||
;; strip-extra-keys (affects only predefined transformers)
|
||||
:strip-extra-keys true
|
||||
;; add/set default values.
|
||||
;; Can be false, true or a map of options to pass to malli.transform/default-value-transformer,
|
||||
;; for example {:malli.transform/add-optional-keys true}
|
||||
;; add/set default values
|
||||
:default-values true
|
||||
;; encode-error
|
||||
:encode-error nil
|
||||
|
|
@ -188,8 +187,7 @@
|
|||
(-open-model [_ schema] schema)
|
||||
(-encode-error [_ error]
|
||||
(cond-> error
|
||||
(show? :humanized) (assoc :humanized (me/humanize error (cond-> {:wrap :message}
|
||||
options (merge options))))
|
||||
(show? :humanized) (assoc :humanized (me/humanize error {:wrap :message}))
|
||||
(show? :schema) (update :schema edn/write-string opts)
|
||||
(show? :errors) (-> (me/with-error-messages opts)
|
||||
(update :errors (partial map #(update % :schema edn/write-string opts))))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-middleware "0.10.0"
|
||||
(defproject metosin/reitit-middleware "0.9.0"
|
||||
:description "Reitit, common middleware bundled"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -19,17 +19,17 @@
|
|||
(recur (.getSuperclass sk) (conj ks sk))
|
||||
ks)))
|
||||
|
||||
(defn- find-closest-ancestor [val m]
|
||||
(or (get m val)
|
||||
(some #(find-closest-ancestor % m) (parents val))))
|
||||
(defn- descendants-safe [type]
|
||||
(when-not (class? type) (descendants type)))
|
||||
|
||||
(defn- call-error-handler [handlers error request]
|
||||
(let [type (:type (ex-data error))
|
||||
ex-class (class error)
|
||||
error-handler (or (get handlers type)
|
||||
(get handlers ex-class)
|
||||
(when-not (class? type)
|
||||
(find-closest-ancestor type handlers))
|
||||
(some
|
||||
(partial get handlers)
|
||||
(descendants-safe type))
|
||||
(some
|
||||
(partial get handlers)
|
||||
(super-classes ex-class))
|
||||
|
|
@ -143,9 +143,6 @@
|
|||
4) Super Classes of exception
|
||||
5) The ::default handler
|
||||
|
||||
Note! If the closest ancestor for `:type` is not unique, an
|
||||
arbitrary one is picked.
|
||||
|
||||
Example:
|
||||
|
||||
(require '[reitit.ring.middleware.exception :as exception])
|
||||
|
|
|
|||
|
|
@ -58,10 +58,7 @@
|
|||
"Creates a Middleware to handle the multipart params, based on
|
||||
ring.middleware.multipart-params, taking same options. Mounts only
|
||||
if endpoint has `[:parameters :multipart]` defined. Publishes coerced
|
||||
parameters into `[:parameters :multipart]` under request.
|
||||
|
||||
Note! You want to have multipart-middleware after coerce-request-middleware,
|
||||
because coerce-request-middleware overwrites `:parameters`."
|
||||
parameters into `[:parameters :multipart]` under request."
|
||||
([]
|
||||
(create-multipart-middleware nil))
|
||||
([options]
|
||||
|
|
@ -72,8 +69,5 @@
|
|||
"Middleware to handle the multipart params, based on
|
||||
ring.middleware.multipart-params, taking same options. Mounts only
|
||||
if endpoint has `[:parameters :multipart]` defined. Publishes coerced
|
||||
parameters into `[:parameters :multipart]` under request.
|
||||
|
||||
Note! You want to have multipart-middleware after coerce-request-middleware,
|
||||
because coerce-request-middleware overwrites `:parameters`."
|
||||
parameters into `[:parameters :multipart]` under request."
|
||||
(create-multipart-middleware))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject fi.metosin/reitit-openapi "0.10.0"
|
||||
(defproject fi.metosin/reitit-openapi "0.9.0"
|
||||
:description "Reitit: OpenAPI-support"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -206,23 +206,20 @@
|
|||
accept-route (fn [route]
|
||||
(-> route second :openapi :id (or ::default) (trie/into-set) (set/intersection ids) seq))
|
||||
definitions (volatile! {})
|
||||
transform-endpoint (fn [path [method {{:keys [coercion no-doc openapi] :as data} :data
|
||||
middleware :middleware
|
||||
interceptors :interceptors}]]
|
||||
(try
|
||||
(if (and data (not no-doc))
|
||||
[method
|
||||
(meta-merge
|
||||
(apply meta-merge (keep (comp :openapi :data) middleware))
|
||||
(apply meta-merge (keep (comp :openapi :data) interceptors))
|
||||
(if coercion
|
||||
(-get-apidocs-openapi coercion data definitions))
|
||||
(select-keys data [:tags :summary :description])
|
||||
(strip-top-level-keys openapi))])
|
||||
(catch Throwable t
|
||||
(throw (ex-info "While building openapi docs" {:path path :method method} t)))))
|
||||
transform-endpoint (fn [[method {{:keys [coercion no-doc openapi] :as data} :data
|
||||
middleware :middleware
|
||||
interceptors :interceptors}]]
|
||||
(if (and data (not no-doc))
|
||||
[method
|
||||
(meta-merge
|
||||
(apply meta-merge (keep (comp :openapi :data) middleware))
|
||||
(apply meta-merge (keep (comp :openapi :data) interceptors))
|
||||
(if coercion
|
||||
(-get-apidocs-openapi coercion data definitions))
|
||||
(select-keys data [:tags :summary :description])
|
||||
(strip-top-level-keys openapi))]))
|
||||
transform-path (fn [[p _ c]]
|
||||
(if-let [endpoint (some->> c (keep (partial transform-endpoint p)) (seq) (into {}))]
|
||||
(if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))]
|
||||
[(openapi-path p (r/options router)) endpoint]))
|
||||
map-in-order #(->> % (apply concat) (apply array-map))
|
||||
paths (->> router (r/compiled-routes) (filter accept-route) (map transform-path) map-in-order)]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-pedestal "0.10.0"
|
||||
(defproject metosin/reitit-pedestal "0.9.0"
|
||||
:description "Reitit + Pedestal Integration"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
(:require [io.pedestal.http :as http]
|
||||
[io.pedestal.interceptor :as interceptor]
|
||||
[io.pedestal.interceptor.chain :as chain]
|
||||
[reitit.exception :as ex]
|
||||
[reitit.http]
|
||||
[reitit.interceptor])
|
||||
(:import (java.lang.reflect Method)))
|
||||
(:import (java.lang.reflect Method)
|
||||
(reitit.interceptor Executor)))
|
||||
|
||||
;; TODO: variadic
|
||||
(defn- arities [f]
|
||||
|
|
@ -46,16 +46,12 @@
|
|||
|
||||
(def pedestal-executor
|
||||
(reify
|
||||
reitit.interceptor/Executor
|
||||
Executor
|
||||
(queue [_ interceptors]
|
||||
(->> interceptors
|
||||
(map (fn [{::interceptor/keys [handler] :as interceptor}]
|
||||
(or handler interceptor)))
|
||||
(keep ->interceptor)))
|
||||
(execute [_ _ _]
|
||||
(ex/unsupported-protocol-method! 'reitit.interceptor/execute))
|
||||
(execute [_ _ _ _ _]
|
||||
(ex/unsupported-protocol-method! 'reitit.interceptor/execute))
|
||||
(enqueue [_ context interceptors]
|
||||
(chain/enqueue context interceptors))))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-ring "0.10.0"
|
||||
(defproject metosin/reitit-ring "0.9.0"
|
||||
:description "Reitit: Ring routing"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -173,8 +173,7 @@
|
|||
(letfn [(maybe-redirect [{:keys [query-string] :as request} path]
|
||||
(if (and (seq path) (r/match-by-path (::r/router request) path))
|
||||
{:status (if (= (:request-method request) :get) 301 308)
|
||||
:headers {"Location" (let [path (str/replace-first path #"^/+" "/")] ; Locations starting with // redirect to another hostname. Avoid these due to security implications.
|
||||
(if query-string (str path "?" query-string) path))}
|
||||
:headers {"Location" (if query-string (str path "?" query-string) path)}
|
||||
:body ""}))
|
||||
(redirect-handler [request]
|
||||
(let [uri (:uri request)]
|
||||
|
|
@ -299,8 +298,7 @@
|
|||
| :index-redirect? | optional boolean: if true (default false), redirect to index file, if false serve it directly
|
||||
| :canonicalize-uris? | optional boolean: if true (default), try to serve index files for non directory paths (paths that end with slash)
|
||||
| :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)
|
||||
| :mime-types | optional map of filename extensions to mime-types that will be used to guess the content type in addition to the ones defined in ring.util.mime-type/default-mime-types
|
||||
| :allow-symlinks? | allow symlinks that lead to paths outside the root classpath directories, defaults to false"
|
||||
| :mime-types | optional map of filename extensions to mime-types that will be used to guess the content type in addition to the ones defined in ring.util.mime-type/default-mime-types"
|
||||
([]
|
||||
(create-resource-handler nil))
|
||||
([opts]
|
||||
|
|
@ -320,8 +318,7 @@
|
|||
| :index-redirect? | optional boolean: if true (default false), redirect to index file, if false serve it directly
|
||||
| :canonicalize-uris? | optional boolean: if true (default), try to serve index files for non directory paths (paths that end with slash)
|
||||
| :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)
|
||||
| :mime-types | optional map of filename extensions to mime-types that will be used to guess the content type in addition to the ones defined in ring.util.mime-type/default-mime-types
|
||||
| :allow-symlinks? | allow symlinks that lead to paths outside the root classpath directories, defaults to false"
|
||||
| :mime-types | optional map of filename extensions to mime-types that will be used to guess the content type in addition to the ones defined in ring.util.mime-type/default-mime-types"
|
||||
([]
|
||||
(create-file-handler nil))
|
||||
([opts]
|
||||
|
|
@ -373,7 +370,7 @@
|
|||
([router default-handler {:keys [middleware inject-match? inject-router?]
|
||||
:or {inject-match? true, inject-router? true}}]
|
||||
(let [default-handler (or default-handler (fn ([_]) ([_ respond _] (respond nil))))
|
||||
wrap (if middleware #(middleware/chain middleware % nil (r/options router)) identity)
|
||||
wrap (if middleware (partial middleware/chain middleware) identity)
|
||||
enrich-request (create-enrich-request inject-match? inject-router?)
|
||||
enrich-default-request (create-enrich-default-request inject-router?)]
|
||||
(with-meta
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-schema "0.10.0"
|
||||
(defproject metosin/reitit-schema "0.9.0"
|
||||
:description "Reitit: Plumatic Schema coercion"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-sieppari "0.10.0"
|
||||
(defproject metosin/reitit-sieppari "0.9.0"
|
||||
:description "Reitit: Sieppari Interceptors"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
(ns reitit.interceptor.sieppari
|
||||
(:require [reitit.exception :as ex]
|
||||
[reitit.interceptor :as interceptor]
|
||||
(:require [reitit.interceptor :as interceptor]
|
||||
[sieppari.core :as sieppari]
|
||||
[sieppari.queue :as queue]))
|
||||
|
||||
|
|
@ -16,6 +15,4 @@
|
|||
(execute [_ interceptors request]
|
||||
(sieppari/execute interceptors request))
|
||||
(execute [_ interceptors request respond raise]
|
||||
(sieppari/execute interceptors request respond raise))
|
||||
(enqueue [_ _ _]
|
||||
(ex/unsupported-protocol-method! 'reitit.interceptor/enqueue))))
|
||||
(sieppari/execute interceptors request respond raise))))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-spec "0.10.0"
|
||||
(defproject metosin/reitit-spec "0.9.0"
|
||||
:description "Reitit: clojure.spec coercion"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-swagger-ui "0.10.0"
|
||||
(defproject metosin/reitit-swagger-ui "0.9.0"
|
||||
:description "Reitit: Swagger-ui support"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-swagger "0.10.0"
|
||||
(defproject metosin/reitit-swagger "0.9.0"
|
||||
:description "Reitit: Swagger-support"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit "0.10.0"
|
||||
(defproject metosin/reitit "0.9.0"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
1607
package-lock.json
generated
1607
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -2,13 +2,13 @@
|
|||
"name": "reitit",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@seriousme/openapi-schema-validator": "^2.7.0",
|
||||
"@seriousme/openapi-schema-validator": "^2.4.0",
|
||||
"karma": "^6.4.4",
|
||||
"karma-chrome-launcher": "^3.2.0",
|
||||
"karma-cli": "^2.0.0",
|
||||
"karma-cljs-test": "^0.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"shadow-cljs": "^3.2.1"
|
||||
"shadow-cljs": "^2.28.22"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,15 +80,12 @@
|
|||
(defrecord NoOpCoercion []
|
||||
coercion/Coercion
|
||||
(-get-name [_] :no-op)
|
||||
(-get-options [_])
|
||||
(-get-apidocs [_ _ {:keys [parameters responses] :as info}])
|
||||
(-get-model-apidocs [_ _ _ _])
|
||||
(-compile-model [_ model _] model)
|
||||
(-open-model [_ spec] spec)
|
||||
(-encode-error [_ error] error)
|
||||
(-request-coercer [_ type spec] (fn [value format] value))
|
||||
(-response-coercer [this spec] (coercion/request-coercer this :response spec {}))
|
||||
(-query-string-coercer [_ _]))
|
||||
(-response-coercer [this spec] (coercion/request-coercer this :response spec {})))
|
||||
|
||||
(comment
|
||||
(doseq [coercion [nil (->NoOpCoercion) spec/coercion]]
|
||||
|
|
|
|||
|
|
@ -53,27 +53,27 @@
|
|||
request (json-request data)
|
||||
request! (request-stream request)]]
|
||||
|
||||
;; # 10b
|
||||
"10b"
|
||||
;; 38µs (c-api 1.x)
|
||||
;; 14µs (c-api 2.0.0-alpha21)
|
||||
;; 6µs
|
||||
|
||||
;; # 100b
|
||||
"100b"
|
||||
;; 74µs (c-api 1.x)
|
||||
;; 16µs (c-api 2.0.0-alpha21)
|
||||
;; 8µs
|
||||
|
||||
;; # 1k
|
||||
"1k"
|
||||
;; 322µs (c-api 1.x)
|
||||
;; 24µs (c-api 2.0.0-alpha21)
|
||||
;; 16µs
|
||||
|
||||
;; # 10k
|
||||
"10k"
|
||||
;; 3300µs (c-api 1.x)
|
||||
;; 120µs (c-api 2.0.0-alpha21)
|
||||
;; 110µs
|
||||
|
||||
;; # 100k
|
||||
"100k"
|
||||
;; 10600µs (c-api 1.x)
|
||||
;; 1100µs (c-api 2.0.0-alpha21)
|
||||
;; 1100µs
|
||||
|
|
@ -37,40 +37,40 @@
|
|||
(defn routing-test []
|
||||
|
||||
;; 21385 / 14337
|
||||
;; "barista"
|
||||
"barista"
|
||||
|
||||
;; 26259 / 25571
|
||||
;; "choreographer"
|
||||
"choreographer"
|
||||
|
||||
;; 24277 / 19174
|
||||
;; "clutch"
|
||||
"clutch"
|
||||
|
||||
;; 26158 / 25584
|
||||
;; "connect"
|
||||
"connect"
|
||||
|
||||
;; 24614 / 25413
|
||||
;; "escort"
|
||||
"escort"
|
||||
|
||||
;; 21979 / 18595
|
||||
;; "express"
|
||||
"express"
|
||||
|
||||
;; 23123 / 25405
|
||||
;; "find-my-way"
|
||||
"find-my-way"
|
||||
|
||||
;; 24798 / 25286
|
||||
;; "http-hash"
|
||||
"http-hash"
|
||||
|
||||
;; 24215 / 23670
|
||||
;; "i40"
|
||||
"i40"
|
||||
|
||||
;; 23561 / 26278
|
||||
;; "light-router"
|
||||
"light-router"
|
||||
|
||||
;; 28362 / 30056
|
||||
;; "http-raw"
|
||||
"http-raw"
|
||||
|
||||
;; 25310 / 25126
|
||||
;; "regex"
|
||||
"regex"
|
||||
|
||||
;; 112719 / 113959
|
||||
(title "reitit")
|
||||
|
|
|
|||
105
project.clj
105
project.clj
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-parent "0.10.0"
|
||||
(defproject metosin/reitit-parent "0.9.0"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
@ -6,8 +6,7 @@
|
|||
:test-paths ["test/clj" "test/cljc"]
|
||||
:deploy-repositories [["clojars" {:url "https://repo.clojars.org"
|
||||
:username :env/clojars_username
|
||||
:password :env/clojars_password
|
||||
:sign-releases false}]]
|
||||
:password :env/clojars_password}]]
|
||||
:repositories [["clojars" {:url "https://repo.clojars.org"
|
||||
:username :env/clojars_username
|
||||
:password :env/clojars_password}]]
|
||||
|
|
@ -16,47 +15,48 @@
|
|||
:metadata {:doc/format :markdown}}
|
||||
:scm {:name "git"
|
||||
:url "https://github.com/metosin/reitit"}
|
||||
;; TODO: need to verify that the code actually worked with Java1.8, see #242
|
||||
;; Ring 1.13.1 drops support for Java 1.8 so lets target 11
|
||||
:javac-options ["-Xlint:unchecked" "-target" "11" "-source" "11"]
|
||||
:managed-dependencies [[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit-core "0.10.0"]
|
||||
[metosin/reitit-dev "0.10.0"]
|
||||
[metosin/reitit-spec "0.10.0"]
|
||||
[metosin/reitit-malli "0.10.0"]
|
||||
[metosin/reitit-schema "0.10.0"]
|
||||
[metosin/reitit-ring "0.10.0"]
|
||||
[metosin/reitit-middleware "0.10.0"]
|
||||
[metosin/reitit-http "0.10.0"]
|
||||
[metosin/reitit-interceptors "0.10.0"]
|
||||
[metosin/reitit-swagger "0.10.0"]
|
||||
[fi.metosin/reitit-openapi "0.10.0"]
|
||||
[metosin/reitit-swagger-ui "0.10.0"]
|
||||
[metosin/reitit-frontend "0.10.0"]
|
||||
[metosin/reitit-sieppari "0.10.0"]
|
||||
[metosin/reitit-pedestal "0.10.0"]
|
||||
:managed-dependencies [[metosin/reitit "0.9.0"]
|
||||
[metosin/reitit-core "0.9.0"]
|
||||
[metosin/reitit-dev "0.9.0"]
|
||||
[metosin/reitit-spec "0.9.0"]
|
||||
[metosin/reitit-malli "0.9.0"]
|
||||
[metosin/reitit-schema "0.9.0"]
|
||||
[metosin/reitit-ring "0.9.0"]
|
||||
[metosin/reitit-middleware "0.9.0"]
|
||||
[metosin/reitit-http "0.9.0"]
|
||||
[metosin/reitit-interceptors "0.9.0"]
|
||||
[metosin/reitit-swagger "0.9.0"]
|
||||
[fi.metosin/reitit-openapi "0.9.0"]
|
||||
[metosin/reitit-swagger-ui "0.9.0"]
|
||||
[metosin/reitit-frontend "0.9.0"]
|
||||
[metosin/reitit-sieppari "0.9.0"]
|
||||
[metosin/reitit-pedestal "0.9.0"]
|
||||
[metosin/ring-swagger-ui "5.20.0"]
|
||||
[metosin/spec-tools "0.10.8"]
|
||||
[metosin/spec-tools "0.10.7"]
|
||||
[metosin/schema-tools "0.13.1"]
|
||||
[metosin/muuntaja "0.6.11"]
|
||||
[metosin/jsonista "0.3.13"]
|
||||
[metosin/sieppari "0.0.0-alpha13"]
|
||||
[metosin/malli "0.20.0"]
|
||||
[metosin/malli "0.18.0"]
|
||||
|
||||
;; https://clojureverse.org/t/depending-on-the-right-versions-of-jackson-libraries/5111
|
||||
[com.fasterxml.jackson.core/jackson-core "2.20.1"]
|
||||
[com.fasterxml.jackson.core/jackson-databind "2.20.1"]
|
||||
[com.fasterxml.jackson.core/jackson-core "2.18.2"]
|
||||
[com.fasterxml.jackson.core/jackson-databind "2.18.2"]
|
||||
|
||||
[meta-merge "1.0.0"]
|
||||
[fipp "0.6.29" :exclusions [org.clojure/core.rrb-vector]]
|
||||
[fipp "0.6.27" :exclusions [org.clojure/core.rrb-vector]]
|
||||
;; Deep-diff uses this version, override olders versiom from fipp.
|
||||
[org.clojure/core.rrb-vector "0.2.1"]
|
||||
[org.clojure/core.rrb-vector "0.2.0"]
|
||||
[expound "0.9.0"]
|
||||
[lambdaisland/deep-diff "0.0-47"]
|
||||
[com.bhauman/spell-spec "0.1.2"]
|
||||
[mvxcvi/arrangement "2.1.0"]
|
||||
[ring/ring-core "1.15.3"]
|
||||
[ring/ring-core "1.14.1"]
|
||||
|
||||
[io.pedestal/pedestal.service "0.6.4" :upgrade false]]
|
||||
[io.pedestal/pedestal.service "0.6.4"]]
|
||||
|
||||
:plugins [[jonase/eastwood "1.4.3"]
|
||||
;[lein-virgil "0.1.7"]
|
||||
|
|
@ -67,8 +67,7 @@
|
|||
[lein-codox "0.10.8"]
|
||||
[metosin/bat-test "0.4.4"]]
|
||||
|
||||
:profiles {:clj11 {:dependencies [[org.clojure/clojure "1.11.4"]]}
|
||||
:dev {:jvm-opts ^:replace ["-server"]
|
||||
:profiles {:dev {:jvm-opts ^:replace ["-server"]
|
||||
|
||||
;; all module sources for development
|
||||
:source-paths ["modules/reitit/src"
|
||||
|
|
@ -90,45 +89,44 @@
|
|||
|
||||
:java-source-paths ["modules/reitit-core/java-src"]
|
||||
|
||||
:dependencies [[org.clojure/clojure "1.12.4"]
|
||||
[thheller/shadow-cljs "3.3.4"]
|
||||
[org.clojure/clojurescript "1.12.134"]
|
||||
:dependencies [[org.clojure/clojure "1.11.4"]
|
||||
[thheller/shadow-cljs "2.28.22"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
|
||||
;; modules dependencies
|
||||
[metosin/schema-tools "0.13.1"]
|
||||
[metosin/spec-tools "0.10.8"]
|
||||
[metosin/spec-tools "0.10.7"]
|
||||
[metosin/muuntaja "0.6.11"]
|
||||
[metosin/sieppari "0.0.0-alpha13"]
|
||||
[metosin/jsonista "0.3.13"]
|
||||
[metosin/malli "0.20.0"]
|
||||
[metosin/malli "0.18.0"]
|
||||
[lambdaisland/deep-diff "0.0-47"]
|
||||
[meta-merge "1.0.0"]
|
||||
[com.bhauman/spell-spec "0.1.2"]
|
||||
[expound "0.9.0"]
|
||||
[fipp "0.6.29"]
|
||||
[fipp "0.6.27"]
|
||||
|
||||
[orchestra "2021.01.01-1"]
|
||||
|
||||
[ring "1.15.3"]
|
||||
[ring "1.14.1"]
|
||||
[ikitommi/immutant-web "3.0.0-alpha1"]
|
||||
[metosin/ring-http-response "0.9.5"]
|
||||
[metosin/ring-swagger-ui "5.20.0"]
|
||||
[org.clojure/tools.analyzer "1.2.1"]
|
||||
[org.clojure/tools.analyzer "1.2.0"]
|
||||
|
||||
[criterium "0.4.6"]
|
||||
[org.clojure/test.check "1.1.3"]
|
||||
[org.clojure/tools.namespace "1.5.1"]
|
||||
[com.gfredericks/test.chuck "0.2.15"]
|
||||
[nubank/matcher-combinators "3.9.2"]
|
||||
[org.clojure/test.check "1.1.1"]
|
||||
[org.clojure/tools.namespace "1.5.0"]
|
||||
[com.gfredericks/test.chuck "0.2.14"]
|
||||
[nubank/matcher-combinators "3.9.1"]
|
||||
|
||||
;; TODO: adapt to breaking changes in pedestal 0.7 and 0.8
|
||||
[io.pedestal/pedestal.service "0.6.4" :upgrade false]
|
||||
[io.pedestal/pedestal.service "0.6.4"]
|
||||
|
||||
[org.clojure/core.async "1.8.741"]
|
||||
[manifold "0.5.0"]
|
||||
[org.clojure/core.async "1.7.701"]
|
||||
[manifold "0.4.3"]
|
||||
[funcool/promesa "11.0.678"]
|
||||
|
||||
[com.clojure-goes-fast/clj-async-profiler "1.7.0"]
|
||||
[com.clojure-goes-fast/clj-async-profiler "1.6.1"]
|
||||
[ring-cors "0.1.13"]
|
||||
|
||||
[com.bhauman/rebel-readline "0.1.5"]]}
|
||||
|
|
@ -137,18 +135,18 @@
|
|||
"-Xmx4096m"
|
||||
"-Dclojure.compiler.direct-linking=true"]
|
||||
:test-paths ["perf-test/clj"]
|
||||
:dependencies [[compojure "1.7.2"]
|
||||
[ring/ring-defaults "0.7.0"]
|
||||
:dependencies [[compojure "1.7.1"]
|
||||
[ring/ring-defaults "0.6.0"]
|
||||
[ikitommi/immutant-web "3.0.0-alpha1"]
|
||||
[io.pedestal/pedestal.service "0.6.4" :upgrade false]
|
||||
[io.pedestal/pedestal.jetty "0.6.4" :upgrade false]
|
||||
[io.pedestal/pedestal.service "0.6.4"]
|
||||
[io.pedestal/pedestal.jetty "0.6.4"]
|
||||
[calfpath "0.8.1"]
|
||||
[org.clojure/core.async "1.8.741"]
|
||||
[manifold "0.5.0"]
|
||||
[org.clojure/core.async "1.7.701"]
|
||||
[manifold "0.4.3"]
|
||||
[funcool/promesa "11.0.678"]
|
||||
[metosin/sieppari]
|
||||
[yada "1.2.16"]
|
||||
[aleph "0.9.3"]
|
||||
[aleph "0.8.3"]
|
||||
[ataraxy "0.4.3"]
|
||||
[bidi "2.1.6"]
|
||||
[janus "1.3.2"]]}
|
||||
|
|
@ -160,7 +158,6 @@
|
|||
:aliases {"all" ["with-profile" "dev,default"]
|
||||
"perf" ["with-profile" "default,dev,perf"]
|
||||
"test-clj" ["all" "do" ["bat-test"] ["check"]]
|
||||
"test-clj11" ["with-profile" "dev,default,clj11" "do" ["bat-test"] ["check"]]
|
||||
;; NOTE: These are deprecated, kept around for ensuring shadow-cljs works
|
||||
;; the same way.
|
||||
"test-browser" ["doo" "chrome-headless" "test"]
|
||||
|
|
|
|||
|
|
@ -13,14 +13,11 @@ case $1 in
|
|||
npx shadow-cljs release karma
|
||||
npx karma start --single-run
|
||||
;;
|
||||
clj11)
|
||||
lein test-clj11
|
||||
;;
|
||||
clj12)
|
||||
clj)
|
||||
lein test-clj
|
||||
;;
|
||||
*)
|
||||
echo "Please select [clj11|clj12|cljs]"
|
||||
echo "Please select [clj|cljs]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -308,10 +308,10 @@
|
|||
["/ping" {:get {:interceptors [{:enter #(a/go %)}]
|
||||
:handler (fn [_] (a/go response))}}])
|
||||
(ring/create-default-handler)
|
||||
{:executor sieppari/executor})
|
||||
respond (promise)]
|
||||
(app {:request-method :get, :uri "/ping"} respond ::irrelevant)
|
||||
(is (= response (deref respond 100 ::timeout))))))
|
||||
{:executor sieppari/executor})]
|
||||
(let [respond (promise)]
|
||||
(app {:request-method :get, :uri "/ping"} respond ::irrelevant)
|
||||
(is (= response (deref respond 100 ::timeout)))))))
|
||||
|
||||
(defrecord MyAsyncContext [])
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
(:import (clojure.lang ExceptionInfo)
|
||||
(java.sql SQLException SQLWarning)))
|
||||
|
||||
(derive ::kukka ::kikka)
|
||||
(derive ::kikka ::kukka)
|
||||
|
||||
(deftest exception-test
|
||||
(letfn [(create
|
||||
|
|
@ -147,55 +147,6 @@
|
|||
(is (= status 500))
|
||||
(is (= body "too many tries")))))))
|
||||
|
||||
(derive ::table ::object)
|
||||
(derive ::living ::object)
|
||||
(derive ::plant ::living)
|
||||
(derive ::animal ::living)
|
||||
(derive ::dog ::animal)
|
||||
(derive ::cat ::animal)
|
||||
(derive ::garfield ::cat)
|
||||
|
||||
(deftest exception-hierarchy-test
|
||||
(letfn [(create [f]
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
[["/defaults"
|
||||
{:handler f}]]
|
||||
{:data {:middleware [(exception/create-exception-middleware
|
||||
(merge
|
||||
exception/default-handlers
|
||||
{::object (constantly (http-response/bad-request "object"))
|
||||
::living (constantly (http-response/bad-request "living"))
|
||||
::animal (constantly (http-response/bad-request "animal"))
|
||||
::cat (constantly (http-response/bad-request "cat"))}))]}})))
|
||||
(call [ex-typ]
|
||||
(let [app (create (fn [_] (throw (ex-info "fail" {:type ex-typ}))))]
|
||||
(app {:request-method :get, :uri "/defaults"})))]
|
||||
(let [{:keys [status body]} (call ::object)]
|
||||
(is (= status 400))
|
||||
(is (= body "object")))
|
||||
(let [{:keys [status body]} (call ::table)]
|
||||
(is (= status 400))
|
||||
(is (= body "object")))
|
||||
(let [{:keys [status body]} (call ::living)]
|
||||
(is (= status 400))
|
||||
(is (= body "living")))
|
||||
(let [{:keys [status body]} (call ::plant)]
|
||||
(is (= status 400))
|
||||
(is (= body "living")))
|
||||
(let [{:keys [status body]} (call ::animal)]
|
||||
(is (= status 400))
|
||||
(is (= body "animal")))
|
||||
(let [{:keys [status body]} (call ::dog)]
|
||||
(is (= status 400))
|
||||
(is (= body "animal")))
|
||||
(let [{:keys [status body]} (call ::cat)]
|
||||
(is (= status 400))
|
||||
(is (= body "cat")))
|
||||
(let [{:keys [status body]} (call ::garfield)]
|
||||
(is (= status 400))
|
||||
(is (= body "cat")))))
|
||||
|
||||
(deftest spec-coercion-exception-test
|
||||
(let [app (ring/ring-handler
|
||||
(ring/router
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
(:require [clojure.spec.alpha :as cs]
|
||||
[clojure.string :as str]
|
||||
[clojure.test :refer [deftest is testing]]
|
||||
[malli.core :as m]
|
||||
[malli.experimental.lite :as l]
|
||||
[reitit.coercion :as coercion]
|
||||
[reitit.coercion.malli]
|
||||
|
|
@ -9,7 +10,8 @@
|
|||
[reitit.coercion.spec]
|
||||
[reitit.core :as r]
|
||||
[schema.core :as s]
|
||||
[spec-tools.data-spec :as ds])
|
||||
[spec-tools.data-spec :as ds]
|
||||
[malli.transform :as mt])
|
||||
#?(:clj
|
||||
(:import (clojure.lang ExceptionInfo))))
|
||||
|
||||
|
|
@ -108,6 +110,7 @@
|
|||
(testing "spec-coercion (shallow)"
|
||||
(testing "succeeds"
|
||||
(let [m (r/match-by-path r "/spec-shallow/1/abba")]
|
||||
(def MATCH m)
|
||||
(is (= {:path {:keyword :abba, :number 1}, :query nil}
|
||||
(coercion/coerce! m))))
|
||||
(let [m (r/match-by-path r "/spec-shallow/1/abba")]
|
||||
|
|
@ -137,36 +140,6 @@
|
|||
(let [m (r/match-by-path r "/none/kikka/abba")]
|
||||
(is (= nil (coercion/coerce! m))))))))
|
||||
|
||||
(deftest malli-query-parameter-coercion-test
|
||||
(let [router (fn [coercion]
|
||||
(r/router ["/test"
|
||||
{:coercion coercion
|
||||
:parameters {:query [:map
|
||||
[:a [:string {:default "a"}]]
|
||||
[:x {:optional true} [:keyword {:default :a}]]]}}]
|
||||
{:compile coercion/compile-request-coercers}))]
|
||||
(testing "default values for :optional query keys do not get added"
|
||||
(is (= {:query {:a "a"}}
|
||||
(-> (r/match-by-path (router reitit.coercion.malli/coercion) "/test")
|
||||
(assoc :query-params {})
|
||||
(coercion/coerce!)))))
|
||||
(testing "default values for :optional query keys get added when :malli.transform/add-optional-keys is set"
|
||||
(is (= {:query {:a "a" :x :a}}
|
||||
(-> (r/match-by-path (router (reitit.coercion.malli/create
|
||||
(assoc reitit.coercion.malli/default-options
|
||||
:default-values {:malli.transform/add-optional-keys true}))) "/test")
|
||||
(assoc :query-params {})
|
||||
(coercion/coerce!)))))
|
||||
(testing "default values can be disabled"
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo
|
||||
#"Request coercion failed"
|
||||
(-> (r/match-by-path (router (reitit.coercion.malli/create
|
||||
(assoc reitit.coercion.malli/default-options
|
||||
:default-values false))) "/test")
|
||||
(assoc :query-params {})
|
||||
(coercion/coerce!)))))))
|
||||
|
||||
(defn match-by-path-and-coerce! [router path]
|
||||
(if-let [match (r/match-by-path router path)]
|
||||
(assoc match :parameters (coercion/coerce! match))))
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
(ns reitit.core-test
|
||||
(:require [clojure.test :refer [are deftest is testing]]
|
||||
[reitit.core :as r]
|
||||
[reitit.core :as r #?@(:cljs [:refer [Router]])]
|
||||
[reitit.impl :as impl])
|
||||
#?(:clj
|
||||
(:import (clojure.lang ExceptionInfo))))
|
||||
(:import (clojure.lang ExceptionInfo)
|
||||
(reitit.core Router))))
|
||||
|
||||
(defn- var-handler [& _]
|
||||
"var-handler")
|
||||
|
|
@ -12,7 +13,8 @@
|
|||
|
||||
(testing "routers handling wildcard paths"
|
||||
(are [r name]
|
||||
(testing (str name)
|
||||
(testing "wild"
|
||||
|
||||
(testing "simple"
|
||||
(let [router (r/router ["/api" ["/ipa" ["/:size" ::beer]]] {:router r})]
|
||||
(is (= name (r/router-name router)))
|
||||
|
|
@ -33,12 +35,6 @@
|
|||
:path "/api/ipa/large"
|
||||
:path-params {:size "large"}})
|
||||
(r/match-by-name router ::beer {:size "large"})))
|
||||
(is (= (r/map->Match
|
||||
{:template "/api/ipa/:size"
|
||||
:data {:name ::beer}
|
||||
:path "/api/ipa/:large"
|
||||
:path-params {:size ":large"}})
|
||||
(r/match-by-name router ::beer {:size ":large"} {:url-encode? false})))
|
||||
(is (= (r/map->Match
|
||||
{:template "/api/ipa/:size"
|
||||
:data {:name ::beer}
|
||||
|
|
@ -56,17 +52,10 @@
|
|||
:path-params nil})
|
||||
(r/match-by-name router ::beer)))
|
||||
(is (r/partial-match? (r/match-by-name router ::beer)))
|
||||
(is (r/partial-match? (r/match-by-name router ::beer {:size nil}))
|
||||
"nil counts as missing")
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo
|
||||
#"^missing path-params for route /api/ipa/:size -> \#\{:size\}$"
|
||||
(r/match-by-name! router ::beer)))
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo
|
||||
#"^missing path-params for route /api/ipa/:size -> \#\{:size\}$"
|
||||
(r/match-by-name! router ::beer {:size nil}))
|
||||
"nil counts as missing"))))
|
||||
(r/match-by-name! router ::beer))))))
|
||||
|
||||
(testing "decode %-encoded path params"
|
||||
(let [router (r/router [["/one-param-path/:param1" ::one]
|
||||
|
|
@ -266,12 +255,10 @@
|
|||
(is (= #'var-handler result))))))
|
||||
|
||||
(testing "custom router"
|
||||
(let [router (r/router
|
||||
["/ping"]
|
||||
{:router (fn [_ _]
|
||||
#_{:clj-kondo/ignore [:missing-protocol-method]}
|
||||
(reify r/Router
|
||||
(router-name [_] ::custom)))})]
|
||||
(let [router (r/router ["/ping"] {:router (fn [_ _]
|
||||
(reify Router
|
||||
(r/router-name [_]
|
||||
::custom)))})]
|
||||
(is (= ::custom (r/router-name router)))))
|
||||
|
||||
(testing "bide sample"
|
||||
|
|
|
|||
33
test/cljc/reitit/dependency_test.cljc
Normal file
33
test/cljc/reitit/dependency_test.cljc
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
(ns reitit.dependency-test
|
||||
(:require [clojure.test :refer [are deftest is testing]]
|
||||
[reitit.dependency :as rc])
|
||||
#?(:clj (:import [clojure.lang ExceptionInfo])))
|
||||
|
||||
(deftest post-order-test
|
||||
(let [base-middlewares [{:name ::bar, :provides #{:bar}, :requires #{:foo}, :wrap identity}
|
||||
{:name ::baz, :provides #{:baz}, :requires #{:bar :foo}, :wrap identity}
|
||||
{:name ::foo, :provides #{:foo}, :requires #{}, :wrap identity}]]
|
||||
(testing "happy cases"
|
||||
(testing "default ordering works"
|
||||
(is (= (rc/post-order base-middlewares)
|
||||
(into (vec (drop 2 base-middlewares)) (take 2 base-middlewares)))))
|
||||
|
||||
(testing "custom provides and requires work"
|
||||
(is (= (rc/post-order (comp hash-set :name)
|
||||
(fn [node] (into #{} (map (fn [k] (keyword "reitit.dependency-test" (name k))))
|
||||
(:requires node)))
|
||||
base-middlewares)
|
||||
(into (vec (drop 2 base-middlewares)) (take 2 base-middlewares))))))
|
||||
|
||||
(testing "errors"
|
||||
(testing "missing dependency detection"
|
||||
(is (thrown-with-msg? ExceptionInfo #"missing"
|
||||
(rc/post-order (drop 1 base-middlewares)))))
|
||||
|
||||
(testing "ambiguous dependency detection"
|
||||
(is (thrown-with-msg? ExceptionInfo #"multiple providers"
|
||||
(rc/post-order (update-in base-middlewares [0 :provides] conj :foo)))))
|
||||
|
||||
(testing "circular dependency detection"
|
||||
(is (thrown-with-msg? ExceptionInfo #"circular"
|
||||
(rc/post-order (assoc-in base-middlewares [2 :requires] #{:baz}))))))))
|
||||
|
|
@ -41,33 +41,7 @@
|
|||
:u #uuid "c2541900-17a7-4353-9024-db8ac258ba4e"
|
||||
:k :kikka
|
||||
:qk ::kikka
|
||||
:nil nil})))
|
||||
(is (= {:n "1"
|
||||
:n1 "-1"
|
||||
:n2 "1"
|
||||
:n3 "1"
|
||||
:n4 "1"
|
||||
:n5 "1"
|
||||
:d "2.2"
|
||||
:b "true"
|
||||
:s "kikka"
|
||||
:u "c2541900-17a7-4353-9024-db8ac258ba4e"
|
||||
:k "kikka"
|
||||
:qk "reitit.impl-test/kikka"
|
||||
:nil nil}
|
||||
(impl/path-params {:n 1
|
||||
:n1 -1
|
||||
:n2 (long 1)
|
||||
:n3 (int 1)
|
||||
:n4 (short 1)
|
||||
:n5 (byte 1)
|
||||
:d 2.2
|
||||
:b true
|
||||
:s "kikka"
|
||||
:u #uuid "c2541900-17a7-4353-9024-db8ac258ba4e"
|
||||
:k :kikka
|
||||
:qk ::kikka
|
||||
:nil nil} {:url-encode? false}))))
|
||||
:nil nil}))))
|
||||
|
||||
(deftest query-params-test
|
||||
(are [x y]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
[jsonista.core :as j]
|
||||
[malli.core :as mc]
|
||||
[matcher-combinators.test :refer [match?]]
|
||||
[matcher-combinators.matchers :as matchers]
|
||||
[muuntaja.core :as m]
|
||||
[reitit.coercion.malli :as malli]
|
||||
[reitit.coercion.schema :as schema]
|
||||
|
|
@ -17,7 +18,6 @@
|
|||
[reitit.swagger-ui :as swagger-ui]
|
||||
[schema.core :as s]
|
||||
[schema-tools.core]
|
||||
[clojure.spec.alpha :as sp]
|
||||
[spec-tools.core :as st]
|
||||
[spec-tools.data-spec :as ds]))
|
||||
|
||||
|
|
@ -901,11 +901,13 @@
|
|||
:request {:description "body description"
|
||||
:content {"application/json" {:schema {:x int?, :y int?}
|
||||
:examples {"1+1" {:value {:x 1, :y 1}}
|
||||
"1+2" {:value {:x 1, :y 2}}}}}}
|
||||
"1+2" {:value {:x 1, :y 2}}}
|
||||
:openapi {:example {:x 2, :y 2}}}}}
|
||||
:responses {200 {:description "success"
|
||||
:content {"application/json" {:schema {:total int?}
|
||||
:examples {"2" {:value {:total 2}}
|
||||
"3" {:value {:total 3}}}}}}}
|
||||
"3" {:value {:total 3}}}
|
||||
:openapi {:example {:total 4}}}}}}
|
||||
:handler (fn [request]
|
||||
(let [{:keys [x y]} (-> request :parameters :body)]
|
||||
{:status 200, :body {:total (+ x y)}}))}}]]]
|
||||
|
|
@ -923,14 +925,16 @@
|
|||
:required [:x :y],
|
||||
:additionalProperties false},
|
||||
:examples {"1+1" {:value {:x 1, :y 1}}
|
||||
"1+2" {:value {:x 1, :y 2}}}}}},
|
||||
"1+2" {:value {:x 1, :y 2}}},
|
||||
:example {:x 2, :y 2}}}},
|
||||
:responses {200 {:description "success",
|
||||
:content {"application/json" {:schema {:type "object",
|
||||
:properties {:total {:type "integer"}},
|
||||
:required [:total],
|
||||
:additionalProperties false},
|
||||
:examples {"2" {:value {:total 2}},
|
||||
"3" {:value {:total 3}}}}}}},
|
||||
"3" {:value {:total 3}}},
|
||||
:example {:total 4}}}}},
|
||||
:summary "plus with body"}}}
|
||||
(:paths spec)))
|
||||
(is (nil? (validate spec))))
|
||||
|
|
@ -1027,36 +1031,3 @@
|
|||
"reitit.openapi-test.Y" {:type "integer"}}}}
|
||||
spec))
|
||||
(is (nil? (validate spec))))))
|
||||
|
||||
(sp/def ::address string?)
|
||||
(sp/def ::zip int?)
|
||||
(sp/def ::city string?)
|
||||
(sp/def ::street string?)
|
||||
(sp/def ::or-and-schema (sp/keys :req-un [(or (and ::address ::zip) (and ::city ::street))]))
|
||||
|
||||
(deftest openapi-spec-tests
|
||||
(testing "s/keys + or maps to :anyOf"
|
||||
(let [app (ring/ring-handler
|
||||
(ring/router
|
||||
[["/openapi.json"
|
||||
{:get {:no-doc true
|
||||
:openapi {:info {:title "" :version "0.0.1"}}
|
||||
:handler (openapi/create-openapi-handler)}}]
|
||||
|
||||
["/spec" {:coercion spec/coercion
|
||||
:post {:summary "or-and-schema"
|
||||
:request {:content {"application/json" {:schema ::or-and-schema}}}
|
||||
:handler identity}}]]
|
||||
{:validate reitit.ring.spec/validate
|
||||
:data {:middleware [openapi/openapi-feature]}}))
|
||||
spec (:body (app {:request-method :get :uri "/openapi.json"}))]
|
||||
(is (nil? (validate spec)))
|
||||
(is (= {:title "reitit.openapi-test/or-and-schema"
|
||||
:type "object"
|
||||
:properties {"address" {:type "string"}
|
||||
"zip" {:type "integer" :format "int64"}
|
||||
"city" {:type "string"}
|
||||
"street" {:type "string"}}
|
||||
:anyOf [{:required ["address" "zip"]}
|
||||
{:required ["city" "street"]}]}
|
||||
(get-in spec [:paths "/spec" :post :requestBody :content "application/json" :schema]))))))
|
||||
|
|
|
|||
|
|
@ -1,21 +1,19 @@
|
|||
(ns reitit.ring-coercion-test
|
||||
(:require [clojure.test :refer [deftest is testing]]
|
||||
[malli.experimental.lite :as l]
|
||||
#?@(:clj [[muuntaja.core]
|
||||
[muuntaja.middleware]
|
||||
[jsonista.core :as j]
|
||||
[reitit.coercion.schema :as schema]
|
||||
[reitit.ring.middleware.muuntaja]
|
||||
[schema.core :as s]])
|
||||
#?@(:clj [[muuntaja.middleware]
|
||||
[jsonista.core :as j]])
|
||||
[malli.core :as m]
|
||||
[malli.util :as mu]
|
||||
[meta-merge.core :refer [meta-merge]]
|
||||
[reitit.coercion.malli :as malli]
|
||||
[reitit.coercion.schema :as schema]
|
||||
[reitit.coercion.spec :as spec]
|
||||
[reitit.core :as r]
|
||||
[reitit.ring :as ring]
|
||||
[reitit.ring.spec]
|
||||
[reitit.ring.coercion :as rrc]
|
||||
[schema.core :as s]
|
||||
[clojure.spec.alpha]
|
||||
[spec-tools.data-spec :as ds])
|
||||
#?(:clj
|
||||
|
|
@ -583,168 +581,103 @@
|
|||
:request-method :get}))]
|
||||
|
||||
(is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call m/schema custom-meta-merge-checking-schema)))
|
||||
(is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call identity custom-meta-merge-checking-parameters)))))
|
||||
|
||||
(testing "malli options"
|
||||
(let [->app (fn [options]
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
["/api" {:get {:parameters {:body [:map
|
||||
[:i :int]
|
||||
[:x :string]]}
|
||||
:handler (fn [{{:keys [body]} :parameters}]
|
||||
{:status 200 :body body})}}]
|
||||
{:data {:middleware [rrc/coerce-exceptions-middleware
|
||||
rrc/coerce-request-middleware
|
||||
rrc/coerce-response-middleware]
|
||||
:coercion (malli/create options)}})))
|
||||
request {:uri "/api"
|
||||
:request-method :get
|
||||
:muuntaja/request {:format "application/json"}}]
|
||||
(testing "humanize options"
|
||||
(is (= {:i ["should be an integer"] :x ["missing required key"]}
|
||||
(-> ((->app nil) (assoc request :body-params {:i "x"}))
|
||||
:body
|
||||
:humanized)))
|
||||
(is (= {:i ["SHOULD INT"] :x ["MISSING"]}
|
||||
(-> ((->app {:options {:errors {:int {:error/message {:en "SHOULD INT"}}
|
||||
:malli.core/missing-key {:error/message {:en "MISSING"}}}}})
|
||||
(assoc request :body-params {:i "x"}))
|
||||
:body
|
||||
:humanized))))
|
||||
(testing "overriding registry"
|
||||
(is (= {:body {:i "x" :x "x"} :status 200}
|
||||
(-> ((->app {:options {:registry (merge (m/default-schemas)
|
||||
{:int :string})}})
|
||||
(assoc request :body-params {:i "x" :x "x"}))))))))))
|
||||
(is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call identity custom-meta-merge-checking-parameters)))))))
|
||||
|
||||
#?(:clj
|
||||
(deftest per-content-type-test
|
||||
(let [normalize-json (fn [resp]
|
||||
(update resp :body #(-> % j/write-value-as-string (j/read-value j/keyword-keys-object-mapper))))]
|
||||
(doseq [[coercion json-request edn-request default-request json-response edn-response default-response]
|
||||
[[malli/coercion
|
||||
[:map [:request [:enum :json]] [:response any?]]
|
||||
[:map [:request [:enum :edn]] [:response any?]]
|
||||
[:map [:request [:enum :default]] [:response any?]]
|
||||
[:map [:request any?] [:response [:enum :json]]]
|
||||
[:map [:request any?] [:response [:enum :edn]]]
|
||||
[:map [:request any?] [:response [:enum :default]]]]
|
||||
[schema/coercion
|
||||
{:request (s/eq :json) :response s/Any}
|
||||
{:request (s/eq :edn) :response s/Any}
|
||||
{:request (s/eq :default) :response s/Any}
|
||||
{:request s/Any :response (s/eq :json)}
|
||||
{:request s/Any :response (s/eq :edn)}
|
||||
{:request s/Any :response (s/eq :default)}]
|
||||
[spec/coercion
|
||||
{:request (clojure.spec.alpha/spec #{:json}) :response any?}
|
||||
{:request (clojure.spec.alpha/spec #{:edn}) :response any?}
|
||||
{:request (clojure.spec.alpha/spec #{:default}) :response any?}
|
||||
{:request any? :response (clojure.spec.alpha/spec #{:json})}
|
||||
{:request any? :response (clojure.spec.alpha/spec #{:end})}
|
||||
{:request any? :response (clojure.spec.alpha/spec #{:default})}]]]
|
||||
(testing (str coercion)
|
||||
(doseq [{:keys [name app]}
|
||||
[{:name "using top-level :body"
|
||||
:app (ring/ring-handler
|
||||
(ring/router
|
||||
["/foo" {:post {:request {:content {"application/json" {:schema json-request}
|
||||
"application/edn" {:schema edn-request}}
|
||||
:body default-request}
|
||||
:responses {200 {:content {"application/json" {:schema json-response}
|
||||
"application/edn" {:schema edn-response}}
|
||||
:body default-response}}
|
||||
:handler (fn [req]
|
||||
{:status 200
|
||||
:body (-> req :parameters :request)})}}]
|
||||
{:validate reitit.ring.spec/validate
|
||||
:data {:middleware [rrc/coerce-request-middleware
|
||||
rrc/coerce-response-middleware]
|
||||
:coercion coercion}}))}
|
||||
{:name "using :default content"
|
||||
:app (ring/ring-handler
|
||||
(ring/router
|
||||
["/foo" {:post {:request {:content {"application/json" {:schema json-request}
|
||||
"application/edn" {:schema edn-request}
|
||||
:default {:schema default-request}}
|
||||
:body json-request} ;; not applied as :default exists
|
||||
:responses {200 {:content {"application/json" {:schema json-response}
|
||||
"application/edn" {:schema edn-response}
|
||||
:default {:schema default-response}}
|
||||
:body json-response}} ;; not applied as :default exists
|
||||
:handler (fn [req]
|
||||
{:status 200
|
||||
:body (-> req :parameters :request)})}}]
|
||||
{:validate reitit.ring.spec/validate
|
||||
:data {:middleware [rrc/coerce-request-middleware
|
||||
rrc/coerce-response-middleware]
|
||||
:coercion coercion}}))}]]
|
||||
(testing name
|
||||
(let [call (fn [request]
|
||||
(try
|
||||
(app request)
|
||||
(catch ExceptionInfo e
|
||||
(select-keys (ex-data e) [:type :in]))))
|
||||
request (fn [request-format response-format body]
|
||||
{:request-method :post
|
||||
:uri "/foo"
|
||||
:muuntaja/request {:format request-format}
|
||||
:muuntaja/response {:format response-format}
|
||||
:body-params body})]
|
||||
(testing "succesful call"
|
||||
(is (= {:status 200 :body {:request "json", :response "json"}}
|
||||
(normalize-json (call (request "application/json" "application/json" {:request :json :response :json})))))
|
||||
(is (= {:status 200 :body {:request "edn", :response "json"}}
|
||||
(normalize-json (call (request "application/edn" "application/json" {:request :edn :response :json})))))
|
||||
(is (= {:status 200 :body {:request :default, :response :default}}
|
||||
(call (request "application/transit" "application/transit" {:request :default :response :default})))))
|
||||
(testing "request validation fails"
|
||||
(is (= {:type :reitit.coercion/request-coercion :in [:request :body-params]}
|
||||
(call (request "application/edn" "application/json" {:request :json :response :json}))))
|
||||
(is (= {:type :reitit.coercion/request-coercion :in [:request :body-params]}
|
||||
(call (request "application/json" "application/json" {:request :edn :response :json}))))
|
||||
(is (= {:type :reitit.coercion/request-coercion :in [:request :body-params]}
|
||||
(call (request "application/transit" "application/json" {:request :edn :response :json})))))
|
||||
(testing "response validation fails"
|
||||
(is (= {:type :reitit.coercion/response-coercion :in [:response :body]}
|
||||
(call (request "application/json" "application/json" {:request :json :response :edn}))))
|
||||
(is (= {:type :reitit.coercion/response-coercion :in [:response :body]}
|
||||
(call (request "application/json" "application/edn" {:request :json :response :json}))))
|
||||
(is (= {:type :reitit.coercion/response-coercion :in [:response :body]}
|
||||
(call (request "application/json" "application/transit" {:request :json :response :json}))))))))
|
||||
(testing "explicit response content type"
|
||||
(let [response (atom nil)
|
||||
app (ring/ring-handler
|
||||
(ring/router
|
||||
["/foo" {:post {:responses {200 {:content {"application/json" {:schema json-response}
|
||||
"application/edn" {:schema edn-response}
|
||||
:default {:schema default-response}}}}
|
||||
:handler (fn [req]
|
||||
@response)}}]
|
||||
{:validate reitit.ring.spec/validate
|
||||
:data {:middleware [rrc/coerce-request-middleware
|
||||
rrc/coerce-response-middleware]
|
||||
:coercion coercion}}))
|
||||
call (fn [request]
|
||||
(doseq [[coercion json-request edn-request default-request json-response edn-response default-response]
|
||||
[[malli/coercion
|
||||
[:map [:request [:enum :json]] [:response any?]]
|
||||
[:map [:request [:enum :edn]] [:response any?]]
|
||||
[:map [:request [:enum :default]] [:response any?]]
|
||||
[:map [:request any?] [:response [:enum :json]]]
|
||||
[:map [:request any?] [:response [:enum :edn]]]
|
||||
[:map [:request any?] [:response [:enum :default]]]]
|
||||
[schema/coercion
|
||||
{:request (s/eq :json) :response s/Any}
|
||||
{:request (s/eq :edn) :response s/Any}
|
||||
{:request (s/eq :default) :response s/Any}
|
||||
{:request s/Any :response (s/eq :json)}
|
||||
{:request s/Any :response (s/eq :edn)}
|
||||
{:request s/Any :response (s/eq :default)}]
|
||||
[spec/coercion
|
||||
{:request (clojure.spec.alpha/spec #{:json}) :response any?}
|
||||
{:request (clojure.spec.alpha/spec #{:edn}) :response any?}
|
||||
{:request (clojure.spec.alpha/spec #{:default}) :response any?}
|
||||
{:request any? :response (clojure.spec.alpha/spec #{:json})}
|
||||
{:request any? :response (clojure.spec.alpha/spec #{:end})}
|
||||
{:request any? :response (clojure.spec.alpha/spec #{:default})}]]]
|
||||
(testing (str coercion)
|
||||
(doseq [{:keys [name app]}
|
||||
[{:name "using top-level :body"
|
||||
:app (ring/ring-handler
|
||||
(ring/router
|
||||
["/foo" {:post {:request {:content {"application/json" {:schema json-request}
|
||||
"application/edn" {:schema edn-request}}
|
||||
:body default-request}
|
||||
:responses {200 {:content {"application/json" {:schema json-response}
|
||||
"application/edn" {:schema edn-response}}
|
||||
:body default-response}}
|
||||
:handler (fn [req]
|
||||
{:status 200
|
||||
:body (-> req :parameters :request)})}}]
|
||||
{:validate reitit.ring.spec/validate
|
||||
:data {:middleware [rrc/coerce-request-middleware
|
||||
rrc/coerce-response-middleware]
|
||||
:coercion coercion}}))}
|
||||
{:name "using :default content"
|
||||
:app (ring/ring-handler
|
||||
(ring/router
|
||||
["/foo" {:post {:request {:content {"application/json" {:schema json-request}
|
||||
"application/edn" {:schema edn-request}
|
||||
:default {:schema default-request}}
|
||||
:body json-request} ;; not applied as :default exists
|
||||
:responses {200 {:content {"application/json" {:schema json-response}
|
||||
"application/edn" {:schema edn-response}
|
||||
:default {:schema default-response}}
|
||||
:body json-response}} ;; not applied as :default exists
|
||||
:handler (fn [req]
|
||||
{:status 200
|
||||
:body (-> req :parameters :request)})}}]
|
||||
{:validate reitit.ring.spec/validate
|
||||
:data {:middleware [rrc/coerce-request-middleware
|
||||
rrc/coerce-response-middleware]
|
||||
:coercion coercion}}))}]]
|
||||
(testing name
|
||||
(let [call (fn [request]
|
||||
(try
|
||||
(app request)
|
||||
(catch ExceptionInfo e
|
||||
#_(ex-data e)
|
||||
(select-keys (ex-data e) [:type :in]))))
|
||||
request (fn [request-format body resp]
|
||||
(reset! response resp)
|
||||
request (fn [request-format response-format body]
|
||||
{:request-method :post
|
||||
:uri "/foo"
|
||||
:muuntaja/request {:format request-format}
|
||||
:body-params body})]
|
||||
(testing "via :muuntaja/content-type"
|
||||
(is (= {:status 200 :body {:request "json" :response "json"} :muuntaja/content-type "application/json"}
|
||||
(normalize-json (call (request "application/json" {:request :json :response :json} {:status 200 :body {:request :json :response :json} :muuntaja/content-type "application/json"}))))
|
||||
"valid reponse")
|
||||
(is (= {:in [:response :body] :type :reitit.coercion/response-coercion}
|
||||
(call (request "application/json" {:request :json :response :json} {:status 200 :body {:request :json :response :invalid} :muuntaja/content-type "application/json"})))
|
||||
"invalid reponse")))))))))
|
||||
:muuntaja/response {:format response-format}
|
||||
:body-params body})
|
||||
normalize-json (fn[body]
|
||||
(-> body j/write-value-as-string (j/read-value j/keyword-keys-object-mapper)))]
|
||||
(testing "succesful call"
|
||||
(is (= {:status 200 :body {:request "json", :response "json"}}
|
||||
(normalize-json (call (request "application/json" "application/json" {:request :json :response :json})))))
|
||||
(is (= {:status 200 :body {:request "edn", :response "json"}}
|
||||
(normalize-json (call (request "application/edn" "application/json" {:request :edn :response :json})))))
|
||||
(is (= {:status 200 :body {:request :default, :response :default}}
|
||||
(call (request "application/transit" "application/transit" {:request :default :response :default})))))
|
||||
(testing "request validation fails"
|
||||
(is (= {:type :reitit.coercion/request-coercion :in [:request :body-params]}
|
||||
(call (request "application/edn" "application/json" {:request :json :response :json}))))
|
||||
(is (= {:type :reitit.coercion/request-coercion :in [:request :body-params]}
|
||||
(call (request "application/json" "application/json" {:request :edn :response :json}))))
|
||||
(is (= {:type :reitit.coercion/request-coercion :in [:request :body-params]}
|
||||
(call (request "application/transit" "application/json" {:request :edn :response :json})))))
|
||||
(testing "response validation fails"
|
||||
(is (= {:type :reitit.coercion/response-coercion :in [:response :body]}
|
||||
(call (request "application/json" "application/json" {:request :json :response :edn}))))
|
||||
(is (= {:type :reitit.coercion/response-coercion :in [:response :body]}
|
||||
(call (request "application/json" "application/edn" {:request :json :response :json}))))
|
||||
(is (= {:type :reitit.coercion/response-coercion :in [:response :body]}
|
||||
(call (request "application/json" "application/transit" {:request :json :response :json}))))))))))))
|
||||
|
||||
|
||||
#?(:clj
|
||||
|
|
@ -762,22 +695,15 @@
|
|||
(testing (str coercion)
|
||||
(let [app (ring/ring-handler
|
||||
(ring/router
|
||||
[["/foo" {:post {:responses {200 {:content {:default {:schema schema-200}}}
|
||||
201 {:content {"application/edn" {:schema schema-200}}}
|
||||
202 {:description "status code and content-type explicitly mentioned, but no :schema"
|
||||
:content {"application/edn" {}
|
||||
"application/json" {}}}
|
||||
:default {:content {"application/json" {:schema schema-default}}}}
|
||||
:handler (fn [req]
|
||||
{:status (-> req :body-params :status)
|
||||
:body (-> req :body-params :response)})}}]
|
||||
["/bar" {:post {:responses {200 {:content {:default {:schema schema-200}}}}
|
||||
:handler (fn [req]
|
||||
{:status (-> req :body-params :status)
|
||||
:body (-> req :body-params :response)})}}]
|
||||
["/quux" {:post {:handler (fn [req]
|
||||
{:status (-> req :body-params :status)
|
||||
:body (-> req :body-params :response)})}}]]
|
||||
["/foo" {:post {:responses {200 {:content {:default {:schema schema-200}}}
|
||||
201 {:content {"application/edn" {:schema schema-200}}}
|
||||
202 {:description "status code and content-type explicitly mentioned, but no :schema"
|
||||
:content {"application/edn" {}
|
||||
"application/json" {}}}
|
||||
:default {:content {"application/json" {:schema schema-default}}}}
|
||||
:handler (fn [req]
|
||||
{:status (-> req :body-params :status)
|
||||
:body (-> req :body-params :response)})}}]
|
||||
{:validate reitit.ring.spec/validate
|
||||
:data {:middleware [rrc/coerce-request-middleware
|
||||
rrc/coerce-response-middleware]
|
||||
|
|
@ -787,52 +713,40 @@
|
|||
(app request)
|
||||
(catch ExceptionInfo e
|
||||
(select-keys (ex-data e) [:type :in]))))
|
||||
request (fn [uri body]
|
||||
request (fn [body]
|
||||
{:request-method :post
|
||||
:uri uri
|
||||
:uri "/foo"
|
||||
:muuntaja/request {:format "application/json"}
|
||||
:muuntaja/response {:format (:format body "application/json")}
|
||||
:body-params body})]
|
||||
(testing "explicit response schema"
|
||||
(is (= {:status 200 :body {:a 1}}
|
||||
(call (request "/foo" {:status 200 :response {:a 1}})))
|
||||
(call (request {:status 200 :response {:a 1}})))
|
||||
"valid response")
|
||||
(is (= {:type :reitit.coercion/response-coercion, :in [:response :body]}
|
||||
(call (request "/foo" {:status 200 :response {:b 1}})))
|
||||
(call (request {:status 200 :response {:b 1}})))
|
||||
"invalid response")
|
||||
(is (= {:type :reitit.coercion/response-coercion, :in [:response :body]}
|
||||
(call (request "/foo" {:status 200 :response {:b 1} :format "application/edn"})))
|
||||
(call (request {:status 200 :response {:b 1} :format "application/edn"})))
|
||||
"invalid response, different content-type"))
|
||||
(testing "explicit response schema, but for the wrong content-type"
|
||||
(is (= {:status 201 :body "anything goes!"}
|
||||
(call (request "/foo" {:status 201 :response "anything goes!"})))
|
||||
(call (request {:status 201 :response "anything goes!"})))
|
||||
"no coercion applied"))
|
||||
(testing "response config without :schema"
|
||||
(is (= {:status 202 :body "anything goes!"}
|
||||
(call (request "/foo" {:status 202 :response "anything goes!"})))
|
||||
(call (request {:status 202 :response "anything goes!"})))
|
||||
"no coercion applied"))
|
||||
(testing "default response schema"
|
||||
(is (= {:status 300 :body {:b 2}}
|
||||
(call (request "/foo" {:status 300 :response {:b 2}})))
|
||||
(call (request {:status 300 :response {:b 2}})))
|
||||
"valid response")
|
||||
(is (= {:type :reitit.coercion/response-coercion, :in [:response :body]}
|
||||
(call (request "/foo" {:status 300 :response {:a 2}})))
|
||||
(call (request {:status 300 :response {:a 2}})))
|
||||
"invalid response")
|
||||
(is (= {:status 300 :body "anything goes!"}
|
||||
(call (request "/foo" {:status 300 :response "anything goes!" :format "application/edn"})))
|
||||
"no coercion applied due to content-type"))
|
||||
(testing "no default"
|
||||
(is (= {:status 200 :body {:a 1}}
|
||||
(call (request "/bar" {:status 200 :response {:a 1}})))
|
||||
"valid response")
|
||||
(testing "unlisted response code"
|
||||
(is (= {:status 202 :body "anything goes!"}
|
||||
(call (request "/bar" {:status 202 :response "anything goes!"})))
|
||||
"no coercion applied")))
|
||||
(testing "no response coercion"
|
||||
(is (= {:status 200 :body "anything goes!"}
|
||||
(call (request "/quux" {:status 200 :response "anything goes!"})))
|
||||
"no coercion applied")))))))
|
||||
(call (request {:status 300 :response "anything goes!" :format "application/edn"})))
|
||||
"no coercion applied due to content-type")))))))
|
||||
|
||||
#?(:clj
|
||||
(deftest muuntaja-test
|
||||
|
|
@ -868,100 +782,3 @@
|
|||
(app) :body slurp (read-string))]
|
||||
(is (= data-edn (e2e (assoc data-edn :EXTRA "VALUE"))))
|
||||
(is (thrown? ExceptionInfo (e2e data-json))))))))
|
||||
|
||||
#?(:clj
|
||||
(deftest muuntaja-per-content-type-coercion-test
|
||||
;; Test integration between per-content-type coercion and muuntaja.
|
||||
;; Malli-only for now.
|
||||
(let [response (atom nil)
|
||||
app (ring/ring-handler
|
||||
(ring/router
|
||||
["/foo" {:post {:request {:content {"application/json" {:schema [:map [:request [:enum :json]]]}
|
||||
"application/edn" {:schema [:map [:request [:enum :edn]]]}
|
||||
:default {:schema [:map [:request [:enum :default]]]}}}
|
||||
:responses {200 {:content {"application/json" {:schema [:map [:response [:enum :json]]]}
|
||||
"application/edn" {:schema [:map [:response [:enum :edn]]]}
|
||||
:default {}}}}
|
||||
:handler (fn [req] @response)}}]
|
||||
{:data {:middleware [reitit.ring.middleware.muuntaja/format-middleware
|
||||
rrc/coerce-request-middleware
|
||||
rrc/coerce-response-middleware]
|
||||
:muuntaja muuntaja.core/instance
|
||||
:coercion malli/coercion}}))
|
||||
maybe-slurp #(if (instance? java.io.InputStream %)
|
||||
(slurp %)
|
||||
%)
|
||||
call (fn [request resp]
|
||||
(reset! response resp)
|
||||
(try
|
||||
(-> (merge {:request-method :post :uri "/foo"} request)
|
||||
(update :body #(ByteArrayInputStream. (.getBytes % "UTF-8")))
|
||||
(app))
|
||||
(catch ExceptionInfo e
|
||||
#_(ex-data e)
|
||||
(select-keys (ex-data e) [:in :type]))))
|
||||
read-json #(j/read-value % (j/object-mapper {:decode-key-fn true}))
|
||||
json-response? (fn [resp]
|
||||
(and (.startsWith (get-in resp [:headers "Content-Type"]) "application/json") ;; ignore the ;charset=utf-8 part
|
||||
(= {:response "json"} (read-json (maybe-slurp (:body resp))))))
|
||||
edn-response? (fn [resp]
|
||||
(and (.startsWith (get-in resp [:headers "Content-Type"]) "application/edn") ;; ignore the ;charset=utf-8 part
|
||||
(= {:response :edn} (read-string (maybe-slurp (:body resp))))))
|
||||
custom-response? (fn [resp]
|
||||
(and (= (get-in resp [:headers "Content-Type"]) "application/custom")
|
||||
(= "custom data" (maybe-slurp (:body resp)))))]
|
||||
(testing "response content-type defaults to json"
|
||||
(is (json-response?
|
||||
(call {:headers {"content-type" "application/json"}
|
||||
:body (j/write-value-as-string {:request :json})}
|
||||
{:status 200
|
||||
:body {:response :json}})))
|
||||
(is (json-response?
|
||||
(call {:headers {"content-type" "application/edn"}
|
||||
:body (pr-str {:request :edn})}
|
||||
{:status 200
|
||||
:body {:response :json}})))
|
||||
(is (= {:in [:response :body] :type :reitit.coercion/response-coercion}
|
||||
(call {:headers {"content-type" "application/json"}
|
||||
:body (j/write-value-as-string {:request :json})}
|
||||
{:status 200
|
||||
:body {:response :invalid}}))
|
||||
"invalid response"))
|
||||
(testing "response content-type negotiated via accept header"
|
||||
(is (json-response?
|
||||
(call {:headers {"content-type" "application/json" "accept" "application/json"}
|
||||
:body (j/write-value-as-string {:request :json})}
|
||||
{:status 200
|
||||
:body {:response :json}})))
|
||||
(is (edn-response?
|
||||
(call {:headers {"content-type" "application/json" "accept" "application/edn"}
|
||||
:body (j/write-value-as-string {:request :json})}
|
||||
{:status 200
|
||||
:body {:response :edn}})))
|
||||
(is (= {:in [:response :body] :type :reitit.coercion/response-coercion}
|
||||
(call {:headers {"content-type" "application/json" "accept" "application/edn"}
|
||||
:body (j/write-value-as-string {:request :json})}
|
||||
{:status 200
|
||||
:body {:response :invalid}}))
|
||||
"invalid response"))
|
||||
(testing "response content-type set via :muuntaja/content-type"
|
||||
(is (edn-response?
|
||||
(call {:headers {"content-type" "application/json" "accept" "application/json"}
|
||||
:body (j/write-value-as-string {:request :json})}
|
||||
{:status 200
|
||||
:muuntaja/content-type "application/edn"
|
||||
:body {:response :edn}})))
|
||||
(is (= {:in [:response :body] :type :reitit.coercion/response-coercion}
|
||||
(call {:headers {"content-type" "application/json" "accept" "application/json"}
|
||||
:body (j/write-value-as-string {:request :json})}
|
||||
{:status 200
|
||||
:muuntaja/content-type "application/edn"
|
||||
:body {:response :invalid}}))
|
||||
"invalid response"))
|
||||
(testing "response content-type set via Content-Type header. muuntaja disabled for response."
|
||||
(is (custom-response?
|
||||
(call {:headers {"content-type" "application/json" "accept" "application/json"}
|
||||
:body (j/write-value-as-string {:request :json})}
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/custom"}
|
||||
:body "custom data"})))))))
|
||||
|
|
|
|||
|
|
@ -12,9 +12,6 @@
|
|||
(s/def ::role #{:admin :user})
|
||||
(s/def ::roles (s/and (s/coll-of ::role :into #{}) set?))
|
||||
|
||||
(defmulti my-multi (constantly :default))
|
||||
(defmethod my-multi :default [x] x)
|
||||
|
||||
(deftest route-data-validation-test
|
||||
(testing "validation is turned off by default"
|
||||
(is (r/router?
|
||||
|
|
@ -88,12 +85,6 @@
|
|||
(ring/router
|
||||
["/api" {:handler identity
|
||||
:middleware '()}]
|
||||
{:validate rrs/validate}))))
|
||||
|
||||
(testing "handler can be a multimethod"
|
||||
(is (r/router?
|
||||
(ring/router
|
||||
["/api" {:get {:handler my-multi}}]
|
||||
{:validate rrs/validate})))))
|
||||
|
||||
(deftest coercion-spec-test
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@
|
|||
[reitit.core :as r]
|
||||
[reitit.middleware :as middleware]
|
||||
[reitit.ring :as ring]
|
||||
#?(:clj [reitit.trie :as trie]
|
||||
:cljs [reitit.trie :as-alias trie]))
|
||||
[reitit.trie :as trie])
|
||||
#?(:clj
|
||||
(:import (clojure.lang ExceptionInfo))))
|
||||
|
||||
|
|
@ -115,25 +114,6 @@
|
|||
(is (= {:status 200, :body [:top :api :ok]}
|
||||
(app {:uri "/api/get" :request-method :get}))))))
|
||||
|
||||
(testing "middleware from registry"
|
||||
(let [router (ring/router
|
||||
["/api" {:middleware [:mw-foo]}
|
||||
["/get" {:middleware [[:mw :inner]]
|
||||
:get handler}]]
|
||||
{::middleware/registry {:mw mw
|
||||
:mw-foo #(mw % :foo)}})
|
||||
app (ring/ring-handler router nil {:middleware [[:mw :top]]})]
|
||||
|
||||
(testing "router can be extracted"
|
||||
(is (= router (ring/get-router app))))
|
||||
|
||||
(testing "not found"
|
||||
(is (= nil (app {:uri "/favicon.ico"}))))
|
||||
|
||||
(testing "on match"
|
||||
(is (= {:status 200, :body [:top :foo :inner :ok]}
|
||||
(app {:uri "/api/get" :request-method :get}))))))
|
||||
|
||||
(testing "named routes"
|
||||
(let [router (ring/router
|
||||
[["/api"
|
||||
|
|
@ -416,31 +396,7 @@
|
|||
:get "/slash-less//" "/slash-less?kikka=kukka"
|
||||
:post "/with-slash" "/with-slash/?kikka=kukka"
|
||||
:post "/slash-less/" "/slash-less?kikka=kukka"
|
||||
:post "/slash-less//" "/slash-less?kikka=kukka"))))
|
||||
|
||||
;; See issue #337
|
||||
(testing "Avoid external redirects"
|
||||
(let [app (ring/ring-handler
|
||||
(ring/router [["*" {:get (constantly nil)}]])
|
||||
(ring/redirect-trailing-slash-handler))
|
||||
resp (fn [uri & [query-string]]
|
||||
(let [r (app {:request-method :get :uri uri :query-string query-string})]
|
||||
{:status (:status r)
|
||||
:Location (get-in r [:headers "Location"])}))]
|
||||
(testing "without query params"
|
||||
(is (= {:status 301 :Location "/malicious.com/foo/"} (resp "//malicious.com/foo")))
|
||||
(is (= {:status 301 :Location "/malicious.com/foo"} (resp "//malicious.com/foo/")))
|
||||
(is (= {:status 301 :Location "/malicious.com/foo"} (resp "//malicious.com/foo//")))
|
||||
(is (= {:status 301 :Location "/malicious.com/foo/"} (resp "///malicious.com/foo")))
|
||||
(is (= {:status 301 :Location "/malicious.com/foo"} (resp "///malicious.com/foo/")))
|
||||
(is (= {:status 301 :Location "/malicious.com/foo"} (resp "///malicious.com/foo//"))))
|
||||
(testing "with query params"
|
||||
(is (= {:status 301 :Location "/malicious.com/foo/?bar=quux"} (resp "//malicious.com/foo" "bar=quux")))
|
||||
(is (= {:status 301 :Location "/malicious.com/foo?bar=quux"} (resp "//malicious.com/foo/" "bar=quux")))
|
||||
(is (= {:status 301 :Location "/malicious.com/foo?bar=quux"} (resp "//malicious.com/foo//" "bar=quux")))
|
||||
(is (= {:status 301 :Location "/malicious.com/foo/?bar=quux"} (resp "///malicious.com/foo" "bar=quux")))
|
||||
(is (= {:status 301 :Location "/malicious.com/foo?bar=quux"} (resp "///malicious.com/foo/" "bar=quux")))
|
||||
(is (= {:status 301 :Location "/malicious.com/foo?bar=quux"} (resp "///malicious.com/foo//" "bar=quux"))))))))
|
||||
:post "/slash-less//" "/slash-less?kikka=kukka"))))))
|
||||
|
||||
(deftest async-ring-test
|
||||
(let [promise #(let [value (atom ::nil)]
|
||||
|
|
@ -872,22 +828,23 @@
|
|||
(let [body (:body (app {:request-method :get, :uri (str "/" n)}))]
|
||||
(is (= body (str n))))))))))))
|
||||
|
||||
(def routes (atom nil))
|
||||
(declare routes)
|
||||
|
||||
(deftest reloading-ring-handler-test
|
||||
(let [r (fn [body] {:status 200, :body body})]
|
||||
(reset! routes ["/" (constantly (r "1"))]) ;; initial value
|
||||
(let [create-handler (fn [] (ring/ring-handler (ring/router @routes)))]
|
||||
(def routes ["/" (constantly (r "1"))]) ;; initial value
|
||||
|
||||
(let [create-handler (fn [] (ring/ring-handler (ring/router routes)))]
|
||||
(testing "static ring handler does not see underlying route changes"
|
||||
(let [app (create-handler)]
|
||||
(is (= (r "1") (app {:uri "/", :request-method :get})))
|
||||
(reset! routes ["/" (constantly (r "2"))]) ;; redefine
|
||||
(def routes ["/" (constantly (r "2"))]) ;; redefine
|
||||
(is (= (r "1") (app {:uri "/", :request-method :get})))))
|
||||
|
||||
(testing "reloading ring handler sees underlying route changes"
|
||||
(let [app (ring/reloading-ring-handler create-handler)]
|
||||
(is (= (r "2") (app {:uri "/", :request-method :get})))
|
||||
(reset! routes ["/" (constantly (r "3"))]) ;; redefine again
|
||||
(def routes ["/" (constantly (r "3"))]) ;; redefine again
|
||||
(is (= (r "3") (app {:uri "/", :request-method :get}))))))))
|
||||
|
||||
(defrecord FooTest [a b])
|
||||
|
|
|
|||
|
|
@ -41,9 +41,7 @@
|
|||
|
||||
"/olipa/:kerran/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"]
|
||||
"/olipa/{kerran}/avaruus", ["/olipa/{kerran}/avaruus"]
|
||||
"/olipa/{:kerran}/avaruus", ["/olipa/{:kerran}/avaruus"]
|
||||
"/olipa/{a.b/c}/avaruus", ["/olipa/{a.b/c}/avaruus"]
|
||||
"/olipa/{:a.b/c}/avaruus", ["/olipa/{:a.b/c}/avaruus"]
|
||||
"/olipa/kerran/*avaruus", ["/olipa/kerran/" (trie/->CatchAll :avaruus)]
|
||||
"/olipa/kerran/{*avaruus}", ["/olipa/kerran/{" (trie/->CatchAll (keyword "avaruus}"))]
|
||||
"/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/{" (trie/->CatchAll (keyword "valtavan.suuri/avaruus}"))])))
|
||||
|
|
@ -55,9 +53,7 @@
|
|||
|
||||
"/olipa/:kerran/avaruus", ["/olipa/:kerran/avaruus"]
|
||||
"/olipa/{kerran}/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"]
|
||||
"/olipa/{:kerran}/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"]
|
||||
"/olipa/{a.b/c}/avaruus", ["/olipa/" (trie/->Wild :a.b/c) "/avaruus"]
|
||||
"/olipa/{:a.b/c}/avaruus", ["/olipa/" (trie/->Wild :a.b/c) "/avaruus"]
|
||||
"/olipa/kerran/*avaruus", ["/olipa/kerran/*avaruus"]
|
||||
"/olipa/kerran/{*avaruus}", ["/olipa/kerran/" (trie/->CatchAll :avaruus)]
|
||||
"/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/" (trie/->CatchAll :valtavan.suuri/avaruus)])))
|
||||
|
|
@ -69,9 +65,7 @@
|
|||
|
||||
"/olipa/:kerran/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"]
|
||||
"/olipa/{kerran}/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"]
|
||||
"/olipa/{:kerran}/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"]
|
||||
"/olipa/{a.b/c}/avaruus", ["/olipa/" (trie/->Wild :a.b/c) "/avaruus"]
|
||||
"/olipa/{:a.b/c}/avaruus", ["/olipa/" (trie/->Wild :a.b/c) "/avaruus"]
|
||||
"/olipa/kerran/*avaruus", ["/olipa/kerran/" (trie/->CatchAll :avaruus)]
|
||||
"/olipa/kerran/{*avaruus}", ["/olipa/kerran/" (trie/->CatchAll :avaruus)]
|
||||
"/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/" (trie/->CatchAll :valtavan.suuri/avaruus)])))
|
||||
|
|
@ -83,9 +77,7 @@
|
|||
|
||||
"/olipa/:kerran/avaruus", ["/olipa/:kerran/avaruus"]
|
||||
"/olipa/{kerran}/avaruus", ["/olipa/{kerran}/avaruus"]
|
||||
"/olipa/{:kerran}/avaruus", ["/olipa/{:kerran}/avaruus"]
|
||||
"/olipa/{a.b/c}/avaruus", ["/olipa/{a.b/c}/avaruus"]
|
||||
"/olipa/{:a.b/c}/avaruus", ["/olipa/{:a.b/c}/avaruus"]
|
||||
"/olipa/kerran/*avaruus", ["/olipa/kerran/*avaruus"]
|
||||
"/olipa/kerran/{*avaruus}", ["/olipa/kerran/{*avaruus}"]
|
||||
"/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/{*valtavan.suuri/avaruus}"]))))
|
||||
|
|
|
|||
Loading…
Reference in a new issue