mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 08:51:12 +00:00
Compare commits
284 commits
0.7.0-alph
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3a152a44e | ||
|
|
c0bc789863 | ||
|
|
78aba57d2d | ||
|
|
451b286f1d | ||
|
|
eb06404f1e | ||
|
|
af7313bd9b | ||
|
|
ea58100fec | ||
|
|
a4576cc622 | ||
|
|
9d88d92241 | ||
|
|
d16aac673e | ||
|
|
dede2db213 | ||
|
|
1dc961f661 | ||
|
|
2ce9850de6 | ||
|
|
0bc30e9361 | ||
|
|
9b26d5c0fd | ||
|
|
e671f78741 | ||
|
|
55f8d98bde | ||
|
|
342bae3ffe | ||
|
|
ae52000b29 | ||
|
|
39c5ae86a4 | ||
|
|
7fb9c27e46 | ||
|
|
f4da07c222 | ||
|
|
d25dca19f6 | ||
|
|
1b02662c78 | ||
|
|
36af88b65e | ||
|
|
152c598858 | ||
|
|
6d9632e85e | ||
|
|
5ff8ba2e3e | ||
|
|
c684c83c99 | ||
|
|
defebb0f1f | ||
|
|
54c0935078 | ||
|
|
cde050f964 | ||
|
|
560c6d7969 | ||
|
|
abe95bfc17 | ||
|
|
d2f44b8015 | ||
|
|
ef9dd495be | ||
|
|
9509e40dae | ||
|
|
67918a3f9c | ||
|
|
45951aa82e | ||
|
|
1cdca2e3d5 | ||
|
|
2f22838820 | ||
|
|
d809291553 | ||
|
|
4e572e86d6 | ||
|
|
10700e0ca2 | ||
|
|
3e0f1d3188 | ||
|
|
f26dc1ab19 | ||
|
|
01766ee211 | ||
|
|
79627aea7b | ||
|
|
4d284385ec | ||
|
|
31fa0376cf | ||
|
|
05bc331397 | ||
|
|
7520d20f12 | ||
|
|
210f20e714 | ||
|
|
aad054ef39 | ||
|
|
653db7efa3 | ||
|
|
5025ca3a75 | ||
|
|
55c30af979 | ||
|
|
9ee8e364f3 | ||
|
|
ad9cd31168 | ||
|
|
99267d2943 | ||
|
|
ecf22d5c4a | ||
|
|
1803643f1f | ||
|
|
f247751409 | ||
|
|
48fdb692ef | ||
|
|
b9cef492f8 | ||
|
|
524d0d4d57 | ||
|
|
20c28ecc7c | ||
|
|
3793a7b23b | ||
|
|
ef64fc50b8 | ||
|
|
15987d9952 | ||
|
|
2bf8aa98a7 | ||
|
|
832d9ebe95 | ||
|
|
4b7a596ece | ||
|
|
0454e8914f | ||
|
|
7a77c9f86b | ||
|
|
a8b4bc0d2d | ||
|
|
9db58f21dc | ||
|
|
9797725ae8 | ||
|
|
3c6139950c | ||
|
|
9534f6df8b | ||
|
|
a390180975 | ||
|
|
f038fe1941 | ||
|
|
8058cecae0 | ||
|
|
dd835e73a8 | ||
|
|
78cf477b88 | ||
|
|
2d1d0cc5d2 | ||
|
|
75a5e816a9 | ||
|
|
e16c95c5b3 | ||
|
|
91fa60324f | ||
|
|
6dd9cb1b7c | ||
|
|
fa28489b63 | ||
|
|
9849ed5ebb | ||
|
|
a103411bf7 | ||
|
|
a57662693c | ||
|
|
44d54cc9f2 | ||
|
|
3342e77538 | ||
|
|
8fb44467a0 | ||
|
|
c2feb5b983 | ||
|
|
9fb0b7233a | ||
|
|
89c05932dc | ||
|
|
2f1defe3cd | ||
|
|
f50feff63c | ||
|
|
34e6db0d3f | ||
|
|
04cb70f1f7 | ||
|
|
58195eed68 | ||
|
|
d5d46d5b0b | ||
|
|
f0fc440425 | ||
|
|
30fd739fa9 | ||
|
|
de0e1f4462 | ||
|
|
34b6bb9349 | ||
|
|
3fcd6cfd73 | ||
|
|
e5483cb1fc | ||
|
|
481c653139 | ||
|
|
5ca22193d0 | ||
|
|
4eb29d3ed9 | ||
|
|
dfc5a4ef67 | ||
|
|
7e9116f77e | ||
|
|
7ae118fbb5 | ||
|
|
ce6d9e26cd | ||
|
|
7ae2bfafc2 | ||
|
|
1b37c87aa2 | ||
|
|
f60a7ad902 | ||
|
|
1ba77a7267 | ||
|
|
21e5840f13 | ||
|
|
5f10465533 | ||
|
|
dba8d159cc | ||
|
|
5829e1c656 | ||
|
|
1819fa5d75 | ||
|
|
25dd0abcaf | ||
|
|
a19b6034dd | ||
|
|
0256642f64 | ||
|
|
b1d066246a | ||
|
|
b19dda8d5d | ||
|
|
0a7b50a730 | ||
|
|
bf82533028 | ||
|
|
0370750a3f | ||
|
|
ea88b06206 | ||
|
|
f1e3ed88ea | ||
|
|
ada41ec7dd | ||
|
|
1abff4937c | ||
|
|
cc1cd114e4 | ||
|
|
e86662561f | ||
|
|
059f93aee8 | ||
|
|
fc132f3a92 | ||
|
|
431242c926 | ||
|
|
0f9414847a | ||
|
|
86e04414ba | ||
|
|
c89b6bbe31 | ||
|
|
f6b8054669 | ||
|
|
4f4d05fe65 | ||
|
|
702e7b8972 | ||
|
|
d11deb3473 | ||
|
|
e2c63d6579 | ||
|
|
86871a6a55 | ||
|
|
923bafdc9b | ||
|
|
afc8945d78 | ||
|
|
610586f0d3 | ||
|
|
5a811421db | ||
|
|
c96b22bc5f | ||
|
|
21a967145f | ||
|
|
494aa29cdb | ||
|
|
02d4f797ca | ||
|
|
0158dbb54b | ||
|
|
4aff73305f | ||
|
|
f5bb9ce70e | ||
|
|
337e6c39c7 | ||
|
|
3136948453 | ||
|
|
c155ee7160 | ||
|
|
ff99ab3ff9 | ||
|
|
a40d9893fb | ||
|
|
ed78552a72 | ||
|
|
dd1d507d90 | ||
|
|
2ac6ee84df | ||
|
|
400a2be0fe | ||
|
|
65ff2ee22d | ||
|
|
f1ec7bbe8e | ||
|
|
f466271e15 | ||
|
|
d926ef7591 | ||
|
|
c9848bd6e4 | ||
|
|
8b0c8a3c18 | ||
|
|
bffe360c6d | ||
|
|
734fca7d4a | ||
|
|
5a2ae56991 | ||
|
|
d8a8bce272 | ||
|
|
517ae8ffc3 | ||
|
|
78cc54d3a8 | ||
|
|
c94ecf5ca7 | ||
|
|
4eab67a8db | ||
|
|
7dfc0e5fca | ||
|
|
61783e4c81 | ||
|
|
59642e51f1 | ||
|
|
dcb7258caf | ||
|
|
7ab6021630 | ||
|
|
c48b6a3704 | ||
|
|
a0467d52cd | ||
|
|
5589328a3c | ||
|
|
af2fc137d5 | ||
|
|
be35375387 | ||
|
|
d88b693e26 | ||
|
|
e8c3035254 | ||
|
|
aec024a943 | ||
|
|
49e8d887da | ||
|
|
ee67a746d4 | ||
|
|
72dadb3c76 | ||
|
|
e0cc8b4cd8 | ||
|
|
c792a6b794 | ||
|
|
129de37d3e | ||
|
|
77f0798c06 | ||
|
|
eb4c231c23 | ||
|
|
2da94f733d | ||
|
|
d136975154 | ||
|
|
411bf39c7d | ||
|
|
2f3fc21c84 | ||
|
|
4e85cb14c5 | ||
|
|
2da5d8bb7d | ||
|
|
4d4307ef57 | ||
|
|
fbfd7ad5f3 | ||
|
|
509b4dd232 | ||
|
|
877c45af90 | ||
|
|
bed41fa46c | ||
|
|
2be0dbbb2a | ||
|
|
710eb69e8c | ||
|
|
275b789ca9 | ||
|
|
18550cd297 | ||
|
|
15790f3028 | ||
|
|
3296323059 | ||
|
|
037763561e | ||
|
|
c84433352e | ||
|
|
c8c8c0eb03 | ||
|
|
a06b2c98a7 | ||
|
|
3cb387747b | ||
|
|
57fc00a45e | ||
|
|
ce52b26329 | ||
|
|
337d94823a | ||
|
|
288b701d4e | ||
|
|
c2372473d0 | ||
|
|
066c54b1d2 | ||
|
|
7432ef9bb9 | ||
|
|
fa24dcd29a | ||
|
|
bbaab0b8f8 | ||
|
|
81b9464f68 | ||
|
|
1b7fc0fc58 | ||
|
|
13e8dd86e5 | ||
|
|
06c0fd8566 | ||
|
|
cd6c23823c | ||
|
|
ff76f5d888 | ||
|
|
cc9863c95f | ||
|
|
59df1f995b | ||
|
|
f41d555b62 | ||
|
|
a69cfdac41 | ||
|
|
b6c5b69ffe | ||
|
|
8ce2de3631 | ||
|
|
ff957661e5 | ||
|
|
01b476b342 | ||
|
|
fbec1e2ecc | ||
|
|
00b5487cc0 | ||
|
|
fb2f4b2ee9 | ||
|
|
c67a748915 | ||
|
|
d24b501281 | ||
|
|
5d30a73bad | ||
|
|
659e96e780 | ||
|
|
2fe448c3d8 | ||
|
|
c295e645c5 | ||
|
|
ca434f9c05 | ||
|
|
0e8d635e44 | ||
|
|
6c9b280fa2 | ||
|
|
cb1c5e8748 | ||
|
|
e7be6327d4 | ||
|
|
d2c00026e6 | ||
|
|
ed280f9a33 | ||
|
|
f1e6d37dcf | ||
|
|
98f3eb0a72 | ||
|
|
82c714d0cc | ||
|
|
982ac3ec72 | ||
|
|
f99a76886e | ||
|
|
989ab72a58 | ||
|
|
5444bad439 | ||
|
|
7e00de835d | ||
|
|
620d0c2711 | ||
|
|
8d2d295a60 | ||
|
|
0fff06ec6b | ||
|
|
f4a8013388 | ||
|
|
24669cf58f | ||
|
|
999f6c3dbd |
132 changed files with 4349 additions and 3880 deletions
26
.github/workflows/release.yml
vendored
Normal file
26
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
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 }}"
|
||||
64
.github/workflows/testsuite.yml
vendored
64
.github/workflows/testsuite.yml
vendored
|
|
@ -8,30 +8,40 @@ jobs:
|
|||
build-clj:
|
||||
strategy:
|
||||
matrix:
|
||||
# Supported Java versions: LTS releases 8 and 11 and the latest release
|
||||
jdk: [8, 11, 15]
|
||||
# Supported Java versions: LTS releases and latest
|
||||
jdk: [11, 17, 21]
|
||||
|
||||
name: Clojure (Java ${{ matrix.jdk }})
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-clj-${{ hashFiles('**/project.clj') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-clj-
|
||||
|
||||
- name: Setup Java ${{ matrix.jdk }}
|
||||
uses: actions/setup-java@v1.4.3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: ${{ matrix.jdk }}
|
||||
|
||||
- name: Setup Clojure
|
||||
uses: DeLaGuardo/setup-clojure@3.1
|
||||
uses: DeLaGuardo/setup-clojure@13.1
|
||||
with:
|
||||
lein: 2.9.5
|
||||
|
||||
# Install openapi-schema-validator for openapi-tests
|
||||
# Uses node version from the ubuntu-latest
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run tests
|
||||
run: ./scripts/test.sh clj
|
||||
|
||||
|
|
@ -39,33 +49,37 @@ jobs:
|
|||
name: ClojureScript
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.m2/repository
|
||||
**/node_modules
|
||||
key: ${{ runner.os }}-cljs-${{ hashFiles('**/project.clj', '**/package-lock.json') }}
|
||||
key: ${{ runner.os }}-cljs-${{ hashFiles('**/project.clj') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cljs-
|
||||
- name: Setup Java 11
|
||||
uses: actions/setup-java@v1.4.3
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
|
||||
- name: Setup Clojure
|
||||
uses: DeLaGuardo/setup-clojure@3.1
|
||||
uses: DeLaGuardo/setup-clojure@13.1
|
||||
with:
|
||||
lein: 2.9.5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2.1.2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 22
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
npm ci
|
||||
- name: Install modules
|
||||
run: ./scripts/lein-modules install
|
||||
run: npm ci
|
||||
|
||||
- name: Run tests
|
||||
run: ./scripts/test.sh cljs
|
||||
|
||||
|
|
@ -73,7 +87,8 @@ jobs:
|
|||
name: Lint cljdoc.edn
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Verify cljdoc.edn
|
||||
run: curl -fsSL https://raw.githubusercontent.com/cljdoc/cljdoc/master/script/verify-cljdoc-edn | bash -s doc/cljdoc.edn
|
||||
|
||||
|
|
@ -81,13 +96,16 @@ jobs:
|
|||
name: Check cljdoc analysis
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Clojure
|
||||
uses: DeLaGuardo/setup-clojure@11.0
|
||||
uses: DeLaGuardo/setup-clojure@13.1
|
||||
with:
|
||||
lein: 2.9.5
|
||||
cli: 1.11.0.1100
|
||||
|
||||
- name: Install cljdoc analyzer
|
||||
run: clojure -Ttools install io.github.cljdoc/cljdoc-analyzer '{:git/tag "RELEASE"}' :as cljdoc-analyzer
|
||||
|
||||
- name: CljDoc Check
|
||||
run: ./scripts/cljdoc-check.sh
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -14,3 +14,5 @@ pom.xml.asc
|
|||
figwheel_server.log
|
||||
/.idea
|
||||
.clj-kondo
|
||||
.shadow-cljs
|
||||
.cache
|
||||
|
|
|
|||
214
CHANGELOG.md
214
CHANGELOG.md
|
|
@ -12,8 +12,184 @@ We use [Break Versioning][breakver]. The version numbers follow a `<major>.<mino
|
|||
|
||||
[breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md
|
||||
|
||||
## UNRELEASED
|
||||
|
||||
* 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)
|
||||
|
||||
## 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)
|
||||
* New `:mime-types` option to configure a map from file extension to mime type
|
||||
* Don't set Content-Type header at all if mime type is not known
|
||||
* Fix location of OpenAPI deprecated metadata [#714](https://github.com/metosin/reitit/pull/714)
|
||||
* **BREAKING** Fix & clarify `:responses :default` and `:content :default` handling. See [docs](./doc/ring/coercion.md). [#735](https://github.com/metosin/reitit/pull/735)
|
||||
* Summary: If `:responses <status>` is present, `:responses :default` is not used, even if `:responses <status>` defines no schema.
|
||||
* Should not break normal use, but might cause surprises related to defaults applying/not applying
|
||||
* **NOTE** This release depends on malli 0.18.0, which changes the format of OpenAPI & Swagger named schemas from `foo.bar/quux` to `foo.bar.quux`
|
||||
|
||||
## 0.8.0 (2025-03-28)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.2..0.8.0)**
|
||||
|
||||
* **BREAKING**: throw error if `:responses` keys are not integers [#667](https://github.com/metosin/reitit/issues/667)
|
||||
* **BREAKING**: Java 8 is no longer supported (Ring-core requires Apache Commons FileUpload which now requires Java 11)
|
||||
* File and resource handlers (`create-file-handler` and `create-resource-handler`)
|
||||
* **BREAKING**: New default is to redirect from `dir` path to `dir/` and serve the index file (if found) on the path ending with `/`
|
||||
* For example the Swagger UI handler now serves the index from `/api-docs/` instead of redirecting to `/api-docs/index.html` (both work)
|
||||
* Mostly this is a visual change, though if you have unit tests checking for response status or redirect, those could break
|
||||
* New option `:index-redirect?` (default false) allows enable redirecting to the index file, e.g. `dir` -> `dir/index.html` (same as the old default)
|
||||
* New option `:canonicalize-uris?` (default true) enables redirect from `dir` to `dir/` if the index file exists for the path
|
||||
* Without this option `dir` would return 404 and `dir/` and `dir/index.html` would return the file
|
||||
* Changes in 0.8.0-alpha1
|
||||
* Updated dependencies:
|
||||
|
||||
```
|
||||
[fipp "0.6.27"] is available but we use "0.6.26"
|
||||
[metosin/jsonista "0.3.13"] is available but we use "0.3.10"
|
||||
[metosin/malli "0.17.0"] is available but we use "0.16.4"
|
||||
[metosin/muuntaja "0.6.11"] is available but we use "0.6.10"
|
||||
[metosin/ring-swagger-ui "5.20.0"] is available but we use "5.9.0"
|
||||
[ring/ring-core "1.14.1"] is available but we use "1.12.2"
|
||||
[ring/ring-defaults "0.6.0"] is available but we use "0.5.0"
|
||||
```
|
||||
|
||||
## 0.8.0-alpha1 (2025-01-31)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.2..0.8.0-alpha1)**
|
||||
|
||||
* Improve OpenAPI docs, plus don't emit `:description` in the wrong place [#702](https://github.com/metosin/reitit/pull/702)
|
||||
* Support reitit.walk for all IPersitentMap implementations, fixes coercion with
|
||||
aleph 0.7.2 [#700](https://github.com/metosin/reitit/issues/700), [#701](https://github.com/metosin/reitit/pull/701)
|
||||
* *POTENTIALLY BREAKING* The frontend functions (href, push/replace-state, navigate, set-query) now
|
||||
encode query-string values using configured coercion when possible (only Malli supports encoding).
|
||||
[#716](https://github.com/metosin/reitit/pull/716)
|
||||
- You can use this to encode query parameter values before they are URL-encoded. This works for DateTimes, collections etc.
|
||||
- In most cases this shouldn't break existing uses, but it is possible even without
|
||||
a custom encoding function, the default Malli string-transformer could encode some values differently
|
||||
then previously.
|
||||
|
||||
## 0.7.2 (2024-09-02)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.1..0.7.2)**
|
||||
|
||||
* Speed up routes and inline it in code ring handler [#693](https://github.com/metosin/reitit/pull/693) [#693](https://github.com/metosin/reitit/pull/696)
|
||||
* Fix: Can't get descendants of classes [#555](https://github.com/metosin/reitit/issues/555)
|
||||
* Faster keywordize [#506](https://github.com/metosin/reitit/pull/506)
|
||||
* Updated dependencies:
|
||||
|
||||
```clojure
|
||||
[metosin/jsonista "0.3.10"] is available but we use "0.3.9"
|
||||
[metosin/malli "0.16.4"] is available but we use "0.16.2"
|
||||
[com.fasterxml.jackson.core/jackson-core "2.17.2"] is available but we use "2.17.1"
|
||||
[com.fasterxml.jackson.core/jackson-databind "2.17.2"] is available but we use "2.17.1"
|
||||
```
|
||||
|
||||
## 0.7.1 (2024-06-30)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.0..0.7.1)**
|
||||
|
||||
* FIX: Route data maps ignore meta-merge options in 0.7.0, breaking compatibility [#679](https://github.com/metosin/reitit/issues/679)
|
||||
* FIX: Clojure record in route data is converted to a plain map [#686](https://github.com/metosin/reitit/issues/686)
|
||||
* Add arities 1 and 2 to rf/match->path [#685](https://github.com/metosin/reitit/pull/685)
|
||||
* Updated dependencies:
|
||||
|
||||
```clojure
|
||||
[ring/ring-core "1.12.2"] is available but we use "1.12.1"
|
||||
[metosin/malli "0.16.2"] is available but we use "0.16.1"
|
||||
[metosin/jsonista "0.3.9"] is available but we use "0.3.8"
|
||||
[metosin/spec-tools "0.10.7"] is available but we use "0.10.6"
|
||||
[com.fasterxml.jackson.core/jackson-core "2.17.1"] is available but we use "2.17.0"
|
||||
[com.fasterxml.jackson.core/jackson-databind "2.17.1"] is available but we use "2.17.0"
|
||||
```
|
||||
|
||||
## 0.7.0 (2024-04-30)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.6.0..0.7.0)**
|
||||
|
||||
The OpenAPI3 release, Year in the making - the changes span over multiple repositories.
|
||||
|
||||
* Openapi3 support, see the [docs](https://github.com/metosin/reitit/blob/master/doc/ring/openapi.md)
|
||||
* Fetch OpenAPI content types from Muuntaja [#636](https://github.com/metosin/reitit/issues/636)
|
||||
* OpenAPI 3 parameter descriptions get populated from malli/spec/schema descriptions. [#612](https://github.com/metosin/reitit/issues/612)
|
||||
* Generate correct OpenAPI $ref schemas for malli var and ref schemas [#673](https://github.com/metosin/reitit/pull/673)
|
||||
* new syntax for `:request` and `:response` per-content-type coercions. See [coercion.md](doc/ring/coercion.md). [#627](https://github.com/metosin/reitit/issues/627)
|
||||
* [#84](https://github.com/metosin/reitit/issues/84)
|
||||
* Handlers can be vars [#585](https://github.com/metosin/reitit/pull/585)
|
||||
* Fix swagger generation when unsupported coercions are present [#671](https://github.com/metosin/reitit/pull/671)
|
||||
* **BREAKING**: require Clojure 1.11, drop support for Clojure 1.10
|
||||
* **BREAKING**: `compile-request-coercers` returns a map with `:data` and `:coerce` instead of plain `:coerce` function
|
||||
* **BREAKING**: Parameter and Response schemas are merged into the route data vector - so they can be properly merged into the compiled result, fixes [#422](https://github.com/metosin/reitit/issues/422) - merging multiple schemas together works with `Malli` and `Schema`, partially with `data-spec` but not with `spec`.
|
||||
* Fixed some module dependencies so Cljdoc can properly analyze all the modules
|
||||
* Fix reading fragment string on `Html5History` initialization
|
||||
* Add fragment string parameter to reitit-frontend functions ([#604](https://github.com/metosin/reitit/pull/604))
|
||||
* Frontend: provide easy way to update current query params. [#600](https://github.com/metosin/reitit/issues/600)
|
||||
|
||||
* Updated dependencies:
|
||||
|
||||
```clojure
|
||||
[metosin/malli "0.16.1"] is available but we use "0.10.1"
|
||||
[metosin/muuntaja "0.6.10"] is available but we use "0.6.8"
|
||||
[metosin/spec-tools "0.10.6"] is available but we use "0.10.5"
|
||||
[metosin/schema-tools "0.13.1"] is available but we use "0.13.0"
|
||||
[metosin/jsonista "0.3.8"] is available but we use "0.3.7"
|
||||
[com.fasterxml.jackson.core/jackson-core "2.17.0"] is available but we use "2.14.2"
|
||||
[com.fasterxml.jackson.core/jackson-databind "2.17.0"] is available but we use "2.14.2"
|
||||
[ring/ring-core "1.12.1"] is available but we use "1.9.6"
|
||||
[metosin/ring-swagger-ui "5.9.0"] is available but we use "4.15.5"
|
||||
```
|
||||
|
||||
## 0.7.0-alpha8 (2024-04-30)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.0-alpha7...0.7.0-alpha8)**
|
||||
|
||||
* Handlers can be vars [#585](https://github.com/metosin/reitit/pull/585)
|
||||
* Fetch OpenAPI content types from Muuntaja [#636](https://github.com/metosin/reitit/issues/636)
|
||||
* **BREAKING** OpenAPI support is now clj only
|
||||
* Fix swagger generation when unsupported coercions are present [#671](https://github.com/metosin/reitit/pull/671)
|
||||
* Generate correct OpenAPI $ref schemas for malli var and ref schemas [#673](https://github.com/metosin/reitit/pull/673)
|
||||
* Updated dependencies:
|
||||
|
||||
```clojure
|
||||
[metosin/malli "0.16.1"] is available but we use "0.13.0"
|
||||
[metosin/muuntaja "0.6.10"] is available but we use "0.6.8"
|
||||
[metosin/spec-tools "0.10.6"] is available but we use "0.10.5"
|
||||
[metosin/jsonista "0.3.8"] is available but we use "0.3.7"
|
||||
[com.fasterxml.jackson.core/jackson-core "2.17.0"] is available but we use "2.15.1"
|
||||
[com.fasterxml.jackson.core/jackson-databind "2.17.0"] is available but we use "2.15.1"
|
||||
[ring/ring-core "1.12.1"] is available but we use "1.10.0"
|
||||
[metosin/ring-swagger-ui "5.9.0"] is available but we use "4.19.1"
|
||||
```
|
||||
|
||||
## 0.7.0-alpha7 (2023-10-03)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.0-alpha6...0.7.0-alpha7)**
|
||||
|
||||
* Revert the group id change from alpha6
|
||||
* New release to bring alpha6 changes to the old group id
|
||||
* Updated dependencies:
|
||||
|
|
@ -22,20 +198,20 @@ We use [Break Versioning][breakver]. The version numbers follow a `<major>.<mino
|
|||
[metosin/ring-swagger-ui "4.19.1"] is available but we use "4.18.1"
|
||||
```
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.0-alpha6...0.7.0-alpha7)**
|
||||
|
||||
## 0.7.0-alpha6 (2023-09-11)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.0-alpha5...0.7.0-alpha6)**
|
||||
|
||||
* **BREAKING**: require Clojure 1.11, drop support for Clojure 1.10
|
||||
* **BREAKING**: new syntax for `:request` and `:response` per-content-type coercions. See [coercion.md](doc/ring/coercion.md). [#627](https://github.com/metosin/reitit/issues/627)
|
||||
* **BREAKING**: replace the openapi `:content-types` keyword with separate `:openapi/request-content-types` and `:openapi/response-content-types`. See [openapi.md](doc/ring/openapi.md)
|
||||
* **NOTE!**: all reitit libraries are now under the `fi.metosin` group on clojars instead of `metosin`. Use `fi.metosin/reitit` in your dependencies instead of `metosin/reitit` to get new versions.
|
||||
- **Reverted in alpha7 due to problems with renaming artifacts**
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.0-alpha5...0.7.0-alpha6)**
|
||||
|
||||
## 0.7.0-alpha5 (2023-06-14)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.0-alpha4...0.7.0-alpha5)**
|
||||
|
||||
* **BREAKING**: `compile-request-coercers` returns a map with `:data` and `:coerce` instead of plain `:coerce` function
|
||||
* **BREAKING**: Parameter and Response schemas are merged into the route data vector - so they can be properly merged into the compiled result, fixes [#422](https://github.com/metosin/reitit/issues/422) - merging multiple schemas together works with `Malli` and `Schema`, partially with `data-spec` but not with `spec`.
|
||||
* Fixed some module dependencies so Cljdoc can properly analyze all the modules
|
||||
|
|
@ -47,29 +223,29 @@ We use [Break Versioning][breakver]. The version numbers follow a `<major>.<mino
|
|||
[com.fasterxml.jackson.core/jackson-databind "2.15.1"] is available but we use "2.14.2"
|
||||
```
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.0-alpha4...0.7.0-alpha5)**
|
||||
|
||||
## 0.7.0-alpha4 (2023-05-17)
|
||||
|
||||
* OpenAPI 3 parameter descriptions get populated from malli/spec/schema descriptions. [#612](https://github.com/metosin/reitit/issues/612)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.0-alpha3...0.7.0-alpha4)**
|
||||
|
||||
## 0.7.0-alpha3 (2023-05-05)
|
||||
* OpenAPI 3 parameter descriptions get populated from malli/spec/schema descriptions. [#612](https://github.com/metosin/reitit/issues/612)
|
||||
|
||||
* Compile `reitit.Trie` with Java 1.8 target for compatibility
|
||||
## 0.7.0-alpha3 (2023-05-05)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.0-alpha2...0.7.0-alpha3)**
|
||||
|
||||
* Compile `reitit.Trie` with Java 1.8 target for compatibility
|
||||
|
||||
## 0.7.0-alpha2 (2023-05-04)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.0-alpha1...0.7.0-alpha2)**
|
||||
|
||||
* Fix reading fragment string on `Html5History` initialization
|
||||
* Add fragment string parameter to reitit-frontend functions ([#604](https://github.com/metosin/reitit/pull/604))
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.7.0-alpha1...0.7.0-alpha2)**
|
||||
|
||||
## 0.7.0-alpha1 (2023-05-03)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.6.0...0.7.0-alpha1)**
|
||||
|
||||
* Initial Openapi3 support. See [docs](./doc/ring/openapi.md). Works for simple cases but might still have some rough edges. [#84](https://github.com/metosin/reitit/issues/84)
|
||||
* Frontend: provide easy way to update current query params. [#600](https://github.com/metosin/reitit/issues/600)
|
||||
* Updated dependencies:
|
||||
|
|
@ -80,10 +256,10 @@ We use [Break Versioning][breakver]. The version numbers follow a `<major>.<mino
|
|||
[ring/ring-core "1.10.0"] is available but we use "1.9.6"
|
||||
```
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.6.0...0.7.0-alpha1)**
|
||||
|
||||
## 0.6.0 (2023-02-21)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.5.18...0.6.0)**
|
||||
|
||||
* Add reitit-frontend support for fragment string [#581](https://github.com/metosin/reitit/pull/581)
|
||||
* reloading-ring-handler [#584](https://github.com/metosin/reitit/pull/584)
|
||||
* Remove redundant s/and [#552](https://github.com/metosin/reitit/pull/552)
|
||||
|
|
@ -109,23 +285,21 @@ We use [Break Versioning][breakver]. The version numbers follow a `<major>.<mino
|
|||
[com.fasterxml.jackson.core/jackson-databind "2.14.2"] is available but we use "2.14.1"
|
||||
```
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.5.18...0.6.0)**
|
||||
|
||||
## 0.5.18 (2022-04-05)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.5.17...0.5.18)**
|
||||
|
||||
* FIX [#334](https://github.com/metosin/reitit/pull/334) - Frontend: there is no way to catch the exception if coercion fails (via [#549](https://github.com/metosin/reitit/pull/549))
|
||||
* Save three seq constructions [#537](https://github.com/metosin/reitit/pull/537)
|
||||
* update jackson-databind for CVE-2020-36518 [#544](https://github.com/metosin/reitit/pull/544)
|
||||
* Balance parenthesis in docs [#547](https://github.com/metosin/reitit/pull/547)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.5.17...0.5.18)**
|
||||
|
||||
## 0.5.17 (2022-03-10)
|
||||
|
||||
* FIX match-by-path is broken if there are no non-conflicting wildcard routes [#538](https://github.com/metosin/reitit/issues/538)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.5.16...0.5.17)**
|
||||
|
||||
* FIX match-by-path is broken if there are no non-conflicting wildcard routes [#538](https://github.com/metosin/reitit/issues/538)
|
||||
|
||||
## 0.5.16 (2022-02-15)
|
||||
|
||||
**[compare](https://github.com/metosin/reitit/compare/0.5.15...0.5.16)**
|
||||
|
|
|
|||
10
Justfile
10
Justfile
|
|
@ -1,10 +0,0 @@
|
|||
help:
|
||||
@just --list
|
||||
|
||||
# Initializes lint
|
||||
init-lint:
|
||||
clj-kondo --lint $(lein classpath)
|
||||
|
||||
# Lints the project
|
||||
lint:
|
||||
./lint.sh
|
||||
97
README.md
97
README.md
|
|
@ -1,5 +1,11 @@
|
|||
# reitit [](https://github.com/metosin/reitit/actions?query=workflow%3Atestsuite) [](https://cljdoc.org/jump/release/metosin/reitit) [](https://clojurians.slack.com/messages/reitit/)
|
||||
# reitit
|
||||
|
||||
[](https://github.com/metosin/reitit/actions)
|
||||
[](https://cljdoc.org/d/metosin/reitit/)
|
||||
[](https://clojars.org/metosin/reitit)
|
||||
[](https://clojurians.slack.com/messages/reitit/)
|
||||
|
||||
<img src="https://github.com/metosin/reitit/blob/master/doc/images/reitit.png?raw=true" align="right" width="200" />
|
||||
A fast data-driven router for Clojure(Script).
|
||||
|
||||
* Simple data-driven [route syntax](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-syntax/)
|
||||
|
|
@ -22,26 +28,34 @@ Presentations:
|
|||
|
||||
**Status:** [stable](https://github.com/metosin/open-source#project-lifecycle-model)
|
||||
|
||||
> Hi! We are [Metosin](https://metosin.fi), a consulting company. These libraries have evolved out of the work we do for our clients.
|
||||
> We maintain & develop this project, for you, for free. Issues and pull requests welcome!
|
||||
> However, if you want more help using the libraries, or want us to build something as cool for you, consider our [commercial support](https://www.metosin.fi/en/open-source-support).
|
||||
|
||||
## [Full Documentation](https://cljdoc.org/d/metosin/reitit/CURRENT)
|
||||
|
||||
There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians Slack](http://clojurians.net/) for discussion & help.
|
||||
|
||||
## Main Modules
|
||||
|
||||
* `reitit` - all bundled
|
||||
* `reitit-core` - the routing core
|
||||
* `reitit-ring` - a [ring router](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/ring/ring/)
|
||||
* `reitit-middleware` - [common middleware](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/ring/default-middleware/)
|
||||
* `reitit-spec` [clojure.spec](https://clojure.org/about/spec) coercion
|
||||
* `reitit-malli` [malli](https://github.com/metosin/malli) coercion
|
||||
* `reitit-schema` [Schema](https://github.com/plumatic/schema) coercion
|
||||
* `reitit-swagger` [Swagger2](https://swagger.io/) apidocs
|
||||
* `reitit-swagger-ui` Integrated [Swagger UI](https://github.com/swagger-api/swagger-ui)
|
||||
* `reitit-frontend` Tools for [frontend routing]((https://cljdoc.org/d/metosin/reitit/CURRENT/doc/frontend/basics/))
|
||||
* `reitit-http` http-routing with Interceptors
|
||||
* `reitit-interceptors` - [common interceptors](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/http/default-interceptors/)
|
||||
* `reitit-sieppari` support for [Sieppari](https://github.com/metosin/sieppari)
|
||||
* `reitit-dev` - development utilities
|
||||
* `metosin/reitit` - all bundled
|
||||
* `metosin/reitit-core` - the routing core
|
||||
* `metosin/reitit-ring` - a [ring router](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/ring/ring-router/)
|
||||
* `metosin/reitit-middleware` - [common middleware](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/ring/default-middleware/)
|
||||
* `metosin/reitit-spec` [clojure.spec](https://clojure.org/about/spec) coercion
|
||||
* `metosin/reitit-malli` [malli](https://github.com/metosin/malli) coercion
|
||||
* `metosin/reitit-schema` [Schema](https://github.com/plumatic/schema) coercion
|
||||
* `fi.metosin/reitit-openapi` [OpenAPI](https://www.openapis.org/) apidocs *
|
||||
* `metosin/reitit-swagger` [Swagger2](https://swagger.io/) apidocs
|
||||
* `metosin/reitit-swagger-ui` Integrated [Swagger UI](https://github.com/swagger-api/swagger-ui)
|
||||
* `metosin/reitit-frontend` Tools for [frontend routing]((https://cljdoc.org/d/metosin/reitit/CURRENT/doc/frontend/basics/))
|
||||
* `metosin/reitit-http` http-routing with Interceptors
|
||||
* `metosin/reitit-interceptors` - [common interceptors](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/http/default-interceptors/)
|
||||
* `metosin/reitit-sieppari` support for [Sieppari](https://github.com/metosin/sieppari)
|
||||
* `metosin/reitit-dev` - development utilities
|
||||
|
||||
... * This is not a typo; the new `reitit-openapi` was released under the new, verified `fi.metosin` group. Existing
|
||||
modules will continue to be released under `metosin` for compatibility purposes.
|
||||
|
||||
## Extra modules
|
||||
|
||||
|
|
@ -52,11 +66,15 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians
|
|||
All main modules bundled:
|
||||
|
||||
```clj
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
```
|
||||
|
||||
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 and 21.
|
||||
|
||||
## Quick start
|
||||
|
||||
```clj
|
||||
|
|
@ -91,6 +109,7 @@ 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])
|
||||
|
||||
|
|
@ -106,39 +125,45 @@ 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
|
||||
:middleware [parameters/parameters-middleware ; decoding query & form params
|
||||
muuntaja/format-middleware ; content negotiation
|
||||
exception/exception-middleware ; converting exceptions to HTTP responses
|
||||
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"}})
|
||||
(-> (app {:request-method :get
|
||||
:uri "/api/math"
|
||||
:query-params {:x "1", :y "2"}})
|
||||
(update :body slurp))
|
||||
; {:status 200
|
||||
; :body {:total 3}}
|
||||
; :body "{\"total\":3}"
|
||||
; :headers {"Content-Type" "application/json; charset=utf-8"}}
|
||||
```
|
||||
|
||||
Invalid request:
|
||||
|
||||
```clj
|
||||
(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]}}
|
||||
(-> (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"]}}
|
||||
```
|
||||
|
||||
## More examples
|
||||
|
|
|
|||
26
bb.edn
Normal file
26
bb.edn
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{:tasks
|
||||
{init-lint {:task (shell "sh -c" "clj-kondo --copy-configs --lint $(lein classpath)")}
|
||||
lint {:doc "Run clj-kondo"
|
||||
:task (shell "./lint.sh")}
|
||||
|
||||
watch-node-test {:doc "Watch files for changes and run Cljs tests on Node.js"
|
||||
:task (shell "npx shadow-cljs watch node-test")}
|
||||
node-test {:doc "Compile and run Cljs tests"
|
||||
:task (shell "npx shadow-cljs compile node-test")}
|
||||
|
||||
watch-browser-test-local {:doc "Start watching Cljs tests for changes and start HTTP server for running tests in a local browser"
|
||||
:task (shell "npx shadow-cljs watch browser-test")}
|
||||
|
||||
;; Karma watch needs to file to exist before start
|
||||
-karma-placeholder (shell "sh -c" "mkdir -p target/karma && touch target/karma/ci.js")
|
||||
-watch-karma-cljs {:depends [-karma-placeholder]
|
||||
:task (shell "npx shadow-cljs watch karma")}
|
||||
-watch-karma-test (shell "npx karma start")
|
||||
-watch-karma {:depends [-watch-karma-cljs -watch-karma-test]}
|
||||
watch-karma {:doc "Watch Cljs tests for changes, compile for Karma and run Karma tests on changes"
|
||||
:task (run '-watch-karma {:parallel true})}
|
||||
|
||||
test-karma {:doc "Compile Cljs tests and run using Karma once"
|
||||
:task (do
|
||||
(shell "npx shadow-cljs compile karma")
|
||||
(shell "npx karma start --single-run"))}}}
|
||||
3
dev-resources/public/site.webmanifest
Normal file
3
dev-resources/public/site.webmanifest
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"name": "Example"
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians
|
|||
All bundled:
|
||||
|
||||
```clj
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
```
|
||||
|
||||
Optionally, the parts can be required separately.
|
||||
|
|
|
|||
|
|
@ -147,3 +147,19 @@ Let's apply a small change to our ```ns3```. We'll replace our router by two dif
|
|||
|
||||
And there you have it, dynamic during dev, performance at production. We have it all !
|
||||
|
||||
## Var handlers
|
||||
|
||||
You can use a var instead of a function as a `:handler`. This will
|
||||
allow you to modify the handler function without rebuilding the reitit
|
||||
router.
|
||||
|
||||
For example:
|
||||
|
||||
```clj
|
||||
(def router
|
||||
(ring/router
|
||||
["/ping" {:get #'my-ns/handler}]))
|
||||
```
|
||||
|
||||
Now you can reload `my-ns` or redefine `my-ns/handler` and the router
|
||||
will use the new definition automatically.
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ The default exception formatting uses `reitit.exception/exception`. It produces
|
|||
## Pretty Errors
|
||||
|
||||
```clj
|
||||
[metosin/reitit-dev "0.7.0-alpha7"]
|
||||
[metosin/reitit-dev "0.9.2"]
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@ Route data is the key feature of reitit. Routes can have any map-like data attac
|
|||
:handler identity}}]]
|
||||
```
|
||||
|
||||
Besides map-like data, raw routes can have any non-sequential route argument after the path. This argument is expanded by `Router` (via `:expand` option) into route data at router creation time.
|
||||
Besides map-like data, raw routes can have any non-sequential route argument after the path. This argument is expanded by `Router` (via `:expand` option) into route data at router creation time.
|
||||
|
||||
By default, Keywords are expanded into `:name` and functions into `:handler` keys.
|
||||
By default, Keywords are expanded into `:name` (see [Name-based Routing](./name_based_routing.md))
|
||||
and functions into `:handler` keys.
|
||||
|
||||
```clj
|
||||
(require '[reitit.core :as r])
|
||||
|
|
@ -84,6 +85,8 @@ Resolved route tree:
|
|||
; :roles #{:db-admin}}]]
|
||||
```
|
||||
|
||||
See also [nested parameter definitions for coercions](../ring/coercion.md#nested-parameter-definitions)
|
||||
|
||||
## Route Data Fragments
|
||||
|
||||
Just like [fragments in React.js](https://reactjs.org/docs/fragments.html), we can create routing tree fragments by using empty path `""`. This allows us to add route data without accumulating to path.
|
||||
|
|
@ -117,7 +120,7 @@ Accumulated route data:
|
|||
["/api-docs" ::api-docs]]
|
||||
["/api/ping" ::ping]
|
||||
["/api/pong" ::pong]]))
|
||||
|
||||
|
||||
(r/routes router)
|
||||
; [["/swagger.json" {:no-doc true, :name ::swagger}]
|
||||
; ["/api-docs" {:no-doc true, :name ::api-docs}]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
# Route Syntax
|
||||
|
||||
Routes are defined as vectors of String path and optional (non-sequential) route argument child routes.
|
||||
Routes are defined as vectors of:
|
||||
- path (a string)
|
||||
- optional route data: usually a map, but see [Route Data](./route_data.md)
|
||||
- any number of child routes
|
||||
|
||||
Routes can be wrapped in vectors and lists and `nil` routes are ignored.
|
||||
|
||||
|
|
@ -11,43 +14,38 @@ Paths can have path-parameters (`:id`) or catch-all-parameters (`*path`). Parame
|
|||
Simple route:
|
||||
|
||||
```clj
|
||||
["/ping"]
|
||||
["/ping" {:handler ping}]
|
||||
```
|
||||
|
||||
Two routes:
|
||||
Two routes with more data:
|
||||
|
||||
```clj
|
||||
[["/ping"]
|
||||
["/pong"]]
|
||||
[["/ping" {:handler ping
|
||||
:cost 300}]
|
||||
["/pong" {:handler pong
|
||||
:tags #{:game}}]]
|
||||
```
|
||||
|
||||
Routes with route arguments:
|
||||
Routes with path parameters (see also [Coercion](../coercion/coercion.md) and [Ring Coercion](../ring/coercion.md)):
|
||||
|
||||
```clj
|
||||
[["/ping" ::ping]
|
||||
["/pong" {:name ::pong}]]
|
||||
```
|
||||
|
||||
Routes with path parameters:
|
||||
|
||||
```clj
|
||||
[["/users/:user-id"]
|
||||
["/api/:version/ping"]]
|
||||
[["/users/:user-id" {:handler get-user}]
|
||||
["/api/:version/ping" {:handler ping-version}]]
|
||||
```
|
||||
|
||||
```clj
|
||||
[["/users/{user-id}"]
|
||||
["/files/file-{number}.pdf"]]
|
||||
[["/users/{user-id}" {:handler get-user}]
|
||||
["/files/file-{number}.pdf" {:handler get-pdf}]]
|
||||
```
|
||||
|
||||
Route with catch-all parameter:
|
||||
|
||||
```clj
|
||||
["/public/*path"]
|
||||
["/public/*path" {:handler get-file}]
|
||||
```
|
||||
|
||||
```clj
|
||||
["/public/{*path}"]
|
||||
["/public/{*path}" {:handler get-file}]
|
||||
```
|
||||
|
||||
Nested routes:
|
||||
|
|
@ -55,9 +53,9 @@ Nested routes:
|
|||
```clj
|
||||
["/api"
|
||||
["/admin" {:middleware [::admin]}
|
||||
["" ::admin]
|
||||
["/db" ::db]]
|
||||
["/ping" ::ping]]
|
||||
["" {:name ::admin}]
|
||||
["/db" {:name ::db}]]
|
||||
["/ping" {:name ::ping}]]
|
||||
```
|
||||
|
||||
Same routes flattened:
|
||||
|
|
@ -77,31 +75,31 @@ Reitit does not apply any encoding to your paths. If you need that, you must enc
|
|||
Normal path-parameters (`:id`) can start anywhere in the path string, but have to end either to slash `/` (currently hardcoded) or to an end of path string:
|
||||
|
||||
```clj
|
||||
[["/api/:version"]
|
||||
["/files/file-:number"]
|
||||
["/user/:user-id/orders"]]
|
||||
[["/api/:version" {...}]
|
||||
["/files/file-:number" {...}]
|
||||
["/user/:user-id/orders" {...}]]
|
||||
```
|
||||
|
||||
Bracket path-parameters can start and stop anywhere in the path-string, the following character is used as a terminator.
|
||||
|
||||
```clj
|
||||
[["/api/{version}"]
|
||||
["/files/{name}.{extension}"]
|
||||
["/user/{user-id}/orders"]]
|
||||
[["/api/{version}" {...}]
|
||||
["/files/{name}.{extension}" {...}]
|
||||
["/user/{user-id}/orders" {...}]]
|
||||
```
|
||||
|
||||
Having multiple terminators after a bracket path-path parameter with identical path prefix will cause a compile-time error at router creation:
|
||||
|
||||
```clj
|
||||
[["/files/file-{name}.pdf"] ;; terminator \.
|
||||
["/files/file-{name}-{version}.pdf"]] ;; terminator \-
|
||||
[["/files/file-{name}.pdf" {...}] ;; terminator \.
|
||||
["/files/file-{name}-{version}.pdf" {...}]] ;; terminator \-
|
||||
```
|
||||
|
||||
### Slash Free Routing
|
||||
|
||||
```clj
|
||||
[["broker.{customer}.{device}.{*data}"]
|
||||
["events.{target}.{type}"]]
|
||||
[["broker.{customer}.{device}.{*data}" {...}]
|
||||
["events.{target}.{type}" {...}]]
|
||||
```
|
||||
|
||||
### Generating routes
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ The [clojure.spec](https://clojure.org/guides/spec) library specifies the struct
|
|||
|
||||
For simple specs (core predicates, `spec-tools.core/spec`, `s/and`, `s/or`, `s/coll-of`, `s/keys`, `s/map-of`, `s/nillable` and `s/every`), the transformation is inferred using [spec-walker](https://github.com/metosin/spec-tools#spec-walker) and is automatic. To support all specs (like regex-specs), specs need to be wrapped into [Spec Records](https://github.com/metosin/spec-tools/blob/master/README.md#spec-records).
|
||||
|
||||
There are [CLJ-2116](https://dev.clojure.org/jira/browse/CLJ-2116) and [CLJ-2251](https://dev.clojure.org/jira/browse/CLJ-2251) that would help solve this elegantly. Go vote 'em up.
|
||||
There are [CLJ-2116](https://clojure.atlassian.net/browse/CLJ-2116) and [CLJ-2251](https://clojure.atlassian.net/browse/CLJ-2251) that would help solve this elegantly. Go vote 'em up.
|
||||
|
||||
## Example
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Data-spec Coercion
|
||||
|
||||
[Data-specs](https://github.com/metosin/spec-tools#data-specs) is alternative, macro-free syntax to define `clojure.spec`s. As a bonus, supports the [runtime transformations via conforming](https://dev.clojure.org/jira/browse/CLJ-2116) out-of-the-box.
|
||||
[Data-specs](https://github.com/metosin/spec-tools#data-specs) is alternative, macro-free syntax to define `clojure.spec`s. As a bonus, supports the [runtime transformations via conforming](https://clojure.atlassian.net/browse/CLJ-2116) out-of-the-box.
|
||||
|
||||
```clj
|
||||
(require '[reitit.coercion.spec])
|
||||
|
|
|
|||
|
|
@ -73,9 +73,10 @@ 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}}
|
||||
:response {:default reitit.coercion.malli/default-transformer-provider
|
||||
:formats {"application/json" reitit.coercion.malli/json-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)
|
||||
|
|
@ -84,10 +85,40 @@ Using `create` with options to create the coercion instead of `coercion`:
|
|||
:validate true
|
||||
;; top-level short-circuit to disable request & response coercion
|
||||
:enabled true
|
||||
;; strip-extra-keys (effects only predefined transformers)
|
||||
;; 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,6 +25,14 @@ 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!
|
||||
|
|
@ -32,29 +40,20 @@ We use [Break Versioning][breakver]. Remember our promise: patch-level bumps nev
|
|||
[breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md
|
||||
|
||||
```bash
|
||||
# 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
|
||||
# create a release commit
|
||||
./scripts/set-version "1.0.0"
|
||||
|
||||
# create a release commit and a tag
|
||||
git add -u
|
||||
# !!! update the changelog
|
||||
|
||||
git add -u
|
||||
git commit -m "Release 1.0.0"
|
||||
git tag 1.0.0
|
||||
|
||||
# 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
|
||||
# push the commit
|
||||
git push
|
||||
git push --tags
|
||||
|
||||
# !!! check that tests pass on CI
|
||||
```
|
||||
|
||||
* Remembor to update the changelog!
|
||||
* 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>
|
||||
* Announce the release at least on #reitit in Clojurians.
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ history events
|
|||
- Stateful wrapper for easy use of history integration
|
||||
- Optional [controller extension](./controllers.md)
|
||||
|
||||
You likely won't use `reitit.frontend` directly in your apps and instead you
|
||||
will use the API documented in the browser integration docs, which wraps these
|
||||
lower level functions.
|
||||
|
||||
## Core functions
|
||||
|
||||
`reitit.frontend` provides some useful functions wrapping core functions:
|
||||
|
|
@ -23,7 +27,8 @@ enabled.
|
|||
|
||||
`match-by-name` and `match-by-name!` with optional `path-paramers` and
|
||||
logging errors to `console.warn` instead of throwing errors to prevent
|
||||
React breaking due to errors.
|
||||
React breaking due to errors. These can also [encode query-parameters](./coercion.md)
|
||||
using schema from match data.
|
||||
|
||||
## Next
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ There are also secondary functions following HTML5 History API:
|
|||
`push-state` to navigate to new route adding entry to the history and
|
||||
`replace-state` to change route without leaving previous entry in browser history.
|
||||
|
||||
See [coercion notes](./coercion.md) to see how frontend route parameters
|
||||
can be decoded and encoded.
|
||||
|
||||
## Fragment router
|
||||
|
||||
Fragment is simple integration which stores the current route in URL fragment,
|
||||
|
|
@ -62,7 +65,7 @@ event handler for page change events.
|
|||
|
||||
## History manipulation
|
||||
|
||||
Reitit doesn't include functions to manipulate the history stack, i.e.
|
||||
Reitit doesn't include functions to manipulate the history stack, i.e.,
|
||||
go back or forwards, but calling History API functions directly should work:
|
||||
|
||||
```
|
||||
|
|
|
|||
59
doc/frontend/coercion.md
Normal file
59
doc/frontend/coercion.md
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# Frontend coercion
|
||||
|
||||
The Reitit frontend leverages [coercion](../coercion/coercion.md) for path,
|
||||
query, and fragment parameters. The coercion uses the input schema defined
|
||||
in the match data under `:parameters`.
|
||||
|
||||
## Behavior of Coercion
|
||||
|
||||
1. **Route Matching**
|
||||
When matching a route from a path, the resulting match will include the
|
||||
coerced values (if coercion is enabled) under `:parameters`. If coercion is
|
||||
disabled, the parsed string values are stored in the same location.
|
||||
The original un-coerced values are always available under `:path-params`,
|
||||
`:query-params`, and `:fragment` (a single string).
|
||||
|
||||
2. **Creating Links and Navigating**
|
||||
When generating a URL (`href`) or navigating (`push-state`, `replace-state`, `navigate`)
|
||||
to a route, coercion can be
|
||||
used to encode query-parameter values into strings. This happens before
|
||||
Reitit performs basic URL encoding on the values. This feature is
|
||||
especially useful for handling the encoding of specific types, such as
|
||||
keywords or dates, into strings.
|
||||
|
||||
3. **Updating current query parameters**
|
||||
When using `set-query` to modify current query parameters, Reitit frontend
|
||||
first tries to find a match for the current path so the match can be used to
|
||||
first decode query parameters and then to encode them. If the current path
|
||||
doesn't match the routing tree, `set-query` keeps all the query parameter
|
||||
values as strings.
|
||||
|
||||
## Notes
|
||||
|
||||
- **Value Encoding Support**: Only Malli supports value encoding.
|
||||
- **Limitations**: Path parameters and fragment values are not encoded using
|
||||
the match schema.
|
||||
|
||||
## Example
|
||||
|
||||
```cljs
|
||||
(def router (r/router ["/"
|
||||
["" ::frontpage]
|
||||
["bar"
|
||||
{:name ::bar
|
||||
:coercion rcm/coercion
|
||||
:parameters {:query [:map
|
||||
[:q {:optional true}
|
||||
[:keyword
|
||||
{:decode/string (fn [s] (keyword (subs s 2)))
|
||||
:encode/string (fn [k] (str "__" (name k)))}]]]}}]]))
|
||||
|
||||
(rfe/href ::bar {} {:q :hello})
|
||||
;; Result "/bar?q=__hello", the :q value is first encoded
|
||||
|
||||
(rfe/push-state ::bar {} {:q :world})
|
||||
;; Result "/bar?q=__world"
|
||||
;; The current match will contain both the original value and parsed & decoded parameters:
|
||||
;; {:query-params {:q "__world"}
|
||||
;; :parameters {:query {:q :world}}}
|
||||
```
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# Default Interceptors
|
||||
|
||||
```clj
|
||||
[metosin/reitit-interceptors "0.7.0-alpha7"]
|
||||
[metosin/reitit-interceptors "0.9.2"]
|
||||
```
|
||||
|
||||
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.7.0-alpha7"]
|
||||
[metosin/reitit-http "0.9.2"]
|
||||
```
|
||||
|
||||
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.7.0-alpha7"]
|
||||
[metosin/reitit-pedestal "0.9.2"]
|
||||
```
|
||||
|
||||
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.7.0-alpha7"]
|
||||
; [metosin/reitit "0.7.0-alpha7"]
|
||||
; [metosin/reitit-pedestal "0.9.2"]
|
||||
; [metosin/reitit "0.9.2"]
|
||||
|
||||
(require '[io.pedestal.http :as server])
|
||||
(require '[reitit.pedestal :as pedestal])
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Sieppari
|
||||
|
||||
```clj
|
||||
[metosin/reitit-sieppari "0.7.0-alpha7"]
|
||||
[metosin/reitit-sieppari "0.9.2"]
|
||||
```
|
||||
|
||||
[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.7.0-alpha7"]
|
||||
[metosin/reitit-interceptors "0.9.2"]
|
||||
```
|
||||
|
||||
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:
|
||||
|
|
|
|||
BIN
doc/images/reitit.png
Normal file
BIN
doc/images/reitit.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 127 KiB |
|
|
@ -37,7 +37,7 @@ Coercion can be attached to route data under `:coercion` key. There can be multi
|
|||
|
||||
Parameters are defined in route data under `:parameters` key. It's value should be a map of parameter `:type` -> Coercion Schema.
|
||||
|
||||
Responses are defined in route data under `:responses` key. It's value should be a map of http status code to a map which can contain `:body` key with Coercion Schema as value.
|
||||
Responses are defined in route data under `:responses` key. It's value should be a map of http status code to a map which can contain `:body` key with Coercion Schema as value. Additionally, the key `:default` specifies the coercion for other status codes.
|
||||
|
||||
Below is an example with [Plumatic Schema](https://github.com/plumatic/schema). It defines schemas for `:query`, `:body` and `:path` parameters and for http 200 response `:body`.
|
||||
|
||||
|
|
@ -54,7 +54,8 @@ Handlers can access the coerced parameters via the `:parameters` key in the requ
|
|||
:parameters {:query {:x s/Int}
|
||||
:body {:y s/Int}
|
||||
:path {:z s/Int}}
|
||||
:responses {200 {:body {:total PositiveInt}}}
|
||||
:responses {200 {:body {:total PositiveInt}}
|
||||
:default {:body {:error s/Str}}}
|
||||
:handler (fn [{:keys [parameters]}]
|
||||
(let [total (+ (-> parameters :query :x)
|
||||
(-> parameters :body :y)
|
||||
|
|
@ -63,6 +64,36 @@ Handlers can access the coerced parameters via the `:parameters` key in the requ
|
|||
:body {:total total}}))})
|
||||
```
|
||||
|
||||
|
||||
### Nested parameter definitions
|
||||
|
||||
Parameters are accumulated recursively along the route tree, just like
|
||||
other [route data](../basics/route_data.md). There is special case
|
||||
handling for merging eg. malli `:map` schemas.
|
||||
|
||||
```clj
|
||||
(def router
|
||||
(reitit.ring/router
|
||||
["/api" {:get {:parameters {:query [:map [:api-key :string]]}}}
|
||||
["/project/:project-id" {:get {:parameters {:path [:map [:project-id :int]]}}}
|
||||
["/task/:task-id" {:get {:parameters {:path [:map [:task-id :int]]
|
||||
:query [:map [:details :boolean]]}
|
||||
:handler (fn [req] (prn req))}}]]]
|
||||
{:data {:coercion reitit.coercion.malli/coercion}}))
|
||||
```
|
||||
|
||||
```clj
|
||||
(-> (r/match-by-path router "/api/project/1/task/2") :result :get :data :parameters)
|
||||
; {:query [:map
|
||||
; {:closed true}
|
||||
; [:api-key :string]
|
||||
; [:details :boolean]],
|
||||
; :path [:map
|
||||
; {:closed true}
|
||||
; [:project-id :int]
|
||||
; [:task-id :int]]}
|
||||
```
|
||||
|
||||
## 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`:
|
||||
|
|
@ -171,11 +202,32 @@ is:
|
|||
"application/edn" {:schema {:x s/Int}}
|
||||
:default {:schema {:ww s/Int}}}}}
|
||||
:handler ...}}]]
|
||||
{:data {:middleware [rrc/coerce-exceptions-middleware
|
||||
rrc/coerce-request-middleware
|
||||
rrc/coerce-response-middleware]}})))
|
||||
{: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]}})))
|
||||
```
|
||||
|
||||
The resolution logic for response coercers is:
|
||||
1. Get the response status, or `:default` from the `:responses` map
|
||||
2. From this map, get use the first of these to coerce:
|
||||
1. `:content <content-type> :schema`
|
||||
2. `:content :default :schema`
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ To demonstrate the two approaches, below is the response coercion middleware wri
|
|||
coercion (-> match :data :coercion)
|
||||
opts (-> match :data :opts)]
|
||||
(if (and coercion responses)
|
||||
(let [coercers (response-coercers coercion responses opts)]
|
||||
(coerce-response coercers request response))
|
||||
(let [coercer (response-coercer coercion responses opts)]
|
||||
(coercer request response))
|
||||
response)))
|
||||
([request respond raise]
|
||||
(let [method (:request-method request)
|
||||
|
|
@ -37,8 +37,8 @@ To demonstrate the two approaches, below is the response coercion middleware wri
|
|||
coercion (-> match :data :coercion)
|
||||
opts (-> match :data :opts)]
|
||||
(if (and coercion responses)
|
||||
(let [coercers (response-coercers coercion responses opts)]
|
||||
(handler request #(respond (coerce-response coercers request %))))
|
||||
(let [coercer (response-coercer coercion responses opts)]
|
||||
(handler request #(respond (coercer request %))))
|
||||
(handler request respond raise))))))
|
||||
```
|
||||
|
||||
|
|
@ -60,13 +60,13 @@ To demonstrate the two approaches, below is the response coercion middleware wri
|
|||
:spec ::rs/responses
|
||||
:compile (fn [{:keys [coercion responses]} opts]
|
||||
(if (and coercion responses)
|
||||
(let [coercers (coercion/response-coercers coercion responses opts)]
|
||||
(let [coercer (coercion/response-coercer coercion responses opts)]
|
||||
(fn [handler]
|
||||
(fn
|
||||
([request]
|
||||
(coercion/coerce-response coercers request (handler request)))
|
||||
(coercer request (handler request)))
|
||||
([request respond raise]
|
||||
(handler request #(respond (coercion/coerce-response coercers request %)) raise)))))))})
|
||||
(handler request #(respond (coercer request %)) raise)))))))})
|
||||
```
|
||||
|
||||
It has 50% less code, it's much easier to reason about and is much faster.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
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.7.0-alpha7"]
|
||||
[metosin/reitit-middleware "0.9.2"]
|
||||
```
|
||||
|
||||
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,8 +17,6 @@ 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.7.0-alpha7"]
|
||||
[metosin/reitit-middleware "0.9.2"]
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
The `:middleware` syntax in `reitit-ring` also supports Keywords. Keywords are looked up from the Middleware Registry, which is a map of `keyword => IntoMiddleware`. Middleware registry should be stored under key `:reitit.middleware/registry` in the router options. If a middleware keyword isn't found in the registry, router creation fails fast with a descriptive error message.
|
||||
|
||||
## Examples
|
||||
## Examples
|
||||
|
||||
Application using middleware defined in the Middleware Registry:
|
||||
|
||||
|
|
@ -52,6 +52,20 @@ 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,6 +5,7 @@
|
|||
Reitit can generate [OpenAPI 3.1.0](https://spec.openapis.org/oas/v3.1.0)
|
||||
documentation. The feature works similarly to [Swagger documentation](swagger.md).
|
||||
|
||||
The main example is [examples/openapi](../../examples/openapi).
|
||||
The
|
||||
[ring-malli-swagger](../../examples/ring-malli-swagger)
|
||||
and
|
||||
|
|
@ -19,12 +20,12 @@ The following route data keys contribute to the generated swagger specification:
|
|||
| key | description |
|
||||
| ---------------|-------------|
|
||||
| :openapi | map of any openapi data. Can contain keys like `:deprecated`.
|
||||
| :openapi/request-content-types | vector of supported request content types. Defaults to `["application/json"]`. Only needed if you use the [:request :content :default] coercion.
|
||||
| :openapi/response-content-types | vector of supported response content types. Defaults to `["application/json"]`. Only needed if you use the [:response nnn :content :default] coercion.
|
||||
| :no-doc | optional boolean to exclude endpoint from api docs
|
||||
| :tags | optional set of string or keyword tags for an endpoint api docs
|
||||
| :summary | optional short string summary of an endpoint
|
||||
| :description | optional long description of an endpoint. Supports http://spec.commonmark.org/
|
||||
| :openapi/request-content-types | See the Per-content-type-coercions section below.
|
||||
| :openapi/response-content-types |See the Per-content-type-coercions section below. vector of supported response content types. Defaults to `["application/json"]`. Only needed if you use the [:response nnn :content :default] coercion.
|
||||
|
||||
Coercion keys also contribute to the docs:
|
||||
|
||||
|
|
@ -34,52 +35,6 @@ Coercion keys also contribute to the docs:
|
|||
| :request | optional description of body parameters, possibly per content-type
|
||||
| :responses | optional descriptions of responses, in a format defined by coercion
|
||||
|
||||
## Annotating schemas
|
||||
|
||||
You can use malli properties, schema-tools data or spec-tools data to
|
||||
annotate your models with examples, descriptions and defaults that
|
||||
show up in the OpenAPI spec.
|
||||
|
||||
Malli:
|
||||
|
||||
```clj
|
||||
["/plus"
|
||||
{:post
|
||||
{:parameters
|
||||
{:body [:map
|
||||
[:x
|
||||
{:title "X parameter"
|
||||
:description "Description for X parameter"
|
||||
:json-schema/default 42}
|
||||
int?]
|
||||
[:y int?]]}}}]
|
||||
```
|
||||
|
||||
Schema:
|
||||
|
||||
```clj
|
||||
["/plus"
|
||||
{:post
|
||||
{:parameters
|
||||
{:body {:x (schema-tools.core/schema s/Num {:description "Description for X parameter"
|
||||
:openapi/example 13
|
||||
:openapi/default 42})
|
||||
:y int?}}}}]
|
||||
```
|
||||
|
||||
Spec:
|
||||
|
||||
```clj
|
||||
["/plus"
|
||||
{:post
|
||||
{:parameters
|
||||
{:body (spec-tools.data-spec/spec ::foo
|
||||
{:x (schema-tools.core/spec {:spec int?
|
||||
:description "Description for X parameter"
|
||||
:openapi/example 13
|
||||
:openapi/default 42})
|
||||
:y int?}}}}}]
|
||||
```
|
||||
|
||||
## Per-content-type coercions
|
||||
|
||||
|
|
@ -91,8 +46,8 @@ openapi example](../../examples/openapi).
|
|||
```clj
|
||||
["/pizza"
|
||||
{:get {:summary "Fetch a pizza | Multiple content-types, multiple examples"
|
||||
:responses {200 {:content {"application/json" {:description "Fetch a pizza as json"
|
||||
:schema [:map
|
||||
:responses {200 {:description "Fetch a pizza as json or EDN"
|
||||
:content {"application/json" {:schema [:map
|
||||
[:color :keyword]
|
||||
[:pineapple :boolean]]
|
||||
:examples {:white {:description "White pizza with pineapple"
|
||||
|
|
@ -101,31 +56,28 @@ openapi example](../../examples/openapi).
|
|||
:red {:description "Red pizza"
|
||||
:value {:color :red
|
||||
:pineapple false}}}}
|
||||
"application/edn" {:description "Fetch a pizza as edn"
|
||||
:schema [:map
|
||||
"application/edn" {:schema [:map
|
||||
[:color :keyword]
|
||||
[:pineapple :boolean]]
|
||||
:examples {:red {:description "Red pizza with pineapple"
|
||||
:value (pr-str {:color :red :pineapple true})}}}}}}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Custom OpenAPI data
|
||||
|
||||
The `:openapi` route data key can be used to add top-level or
|
||||
route-level information to the generated OpenAPI spec. This is useful
|
||||
for providing `"securitySchemes"` or other OpenAPI keys that are not
|
||||
generated automatically by reitit.
|
||||
|
||||
See [the openapi example](../../examples/openapi) for a working
|
||||
example of `"securitySchemes"`.
|
||||
The special `:default` content types map to the content types supported by the Muuntaja
|
||||
instance. You can override these by using the `:openapi/request-content-types`
|
||||
and `:openapi/response-content-types` keys, which must contain vector of
|
||||
supported content types. If there is no Muuntaja instance, and these keys are
|
||||
not defined, the content types will default to `["application/json"]`.
|
||||
|
||||
## OpenAPI spec
|
||||
|
||||
Serving the OpenAPI specification is handled by `reitit.openapi/create-openapi-handler`. It takes no arguments and returns a ring handler which collects at request-time data from all routes and returns an OpenAPI specification as Clojure data, to be encoded by a response formatter.
|
||||
Serving the OpenAPI specification is handled by
|
||||
`reitit.openapi/create-openapi-handler`. It takes no arguments and returns a
|
||||
ring handler which collects at request-time data from all routes and returns an
|
||||
OpenAPI specification as Clojure data, to be encoded by a response formatter.
|
||||
|
||||
You can use the `:openapi` route data key of the `create-openapi-handler` route to populate the top level of the OpenAPI spec.
|
||||
You can use the `:openapi` route data key of the `create-openapi-handler` route
|
||||
to populate the top level of the OpenAPI spec.
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -140,6 +92,123 @@ If you need to post-process the generated spec, just wrap the handler with a cus
|
|||
|
||||
## Swagger-ui
|
||||
|
||||
[Swagger-UI](https://github.com/swagger-api/swagger-ui) is a user interface to visualize and interact with the Swagger specification. To make things easy, there is a pre-integrated version of the swagger-ui as a separate module.
|
||||
[Swagger-UI](https://github.com/swagger-api/swagger-ui) is a user interface to visualize and interact with the Swagger specification. To make things easy, there is a pre-integrated version of the swagger-ui as a separate module. See `reitit.swagger-ui/create-swagger-ui-handle`
|
||||
|
||||
Note: you need Swagger-UI 5 for OpenAPI 3.1 support. As of 2023-03-10, a v5.0.0-alpha.0 is out.
|
||||
## Finetuning the OpenAPI output
|
||||
|
||||
There are a number of ways you can specify extra data that gets
|
||||
included in the OpenAPI spec.
|
||||
|
||||
### Custom OpenAPI data
|
||||
|
||||
The `:openapi` route data key can be used to add top-level or
|
||||
route-level information to the generated OpenAPI spec.
|
||||
|
||||
A straightforward use case is adding `"externalDocs"`:
|
||||
|
||||
```clj
|
||||
["/account"
|
||||
{:get {:summary "Fetch an account | Recursive schemas using malli registry, link to external docs"
|
||||
:openapi {:externalDocs {:description "The reitit repository"
|
||||
:url "https://github.com/metosin/reitit"}}
|
||||
...}}]
|
||||
```
|
||||
|
||||
In a more complex use case is providing `"securitySchemes"`. See
|
||||
[the openapi example](../../examples/openapi) for a working example of
|
||||
`"securitySchemes"`. See also the
|
||||
[OpenAPI docs](https://spec.openapis.org/oas/v3.1.0.html#security-scheme-object)
|
||||
|
||||
### Annotating schemas
|
||||
|
||||
You can use malli properties, schema-tools data or spec-tools data to
|
||||
annotate your models with examples, descriptions and defaults that
|
||||
show up in the OpenAPI spec.
|
||||
|
||||
This approach lets you add additional keys to the
|
||||
[OpenAPI Schema Objects](https://spec.openapis.org/oas/v3.1.0.html#schema-object).
|
||||
The most common ones are default and example values for parameters.
|
||||
|
||||
Malli:
|
||||
|
||||
```clj
|
||||
["/plus"
|
||||
{:post
|
||||
{:parameters
|
||||
{:body [:map
|
||||
[:x
|
||||
{:title "X parameter"
|
||||
:description "Description for X parameter"
|
||||
:json-schema/deprecated true
|
||||
:json-schema/default 42}
|
||||
int?]
|
||||
[:y int?]]}}}]
|
||||
```
|
||||
|
||||
Schema:
|
||||
|
||||
```clj
|
||||
["/plus"
|
||||
{:post
|
||||
{:parameters
|
||||
{:body {:x (schema-tools.core/schema s/Num {:description "Description for X parameter"
|
||||
:openapi/deprecated true
|
||||
:openapi/example 13
|
||||
:openapi/default 42})
|
||||
:y int?}}}}]
|
||||
```
|
||||
|
||||
Spec:
|
||||
|
||||
```clj
|
||||
["/plus"
|
||||
{:post
|
||||
{:parameters
|
||||
{:body (spec-tools.data-spec/spec ::foo
|
||||
{:x (schema-tools.core/spec {:spec int?
|
||||
:description "Description for X parameter"
|
||||
:openapi/deprecated true
|
||||
:openapi/example 13
|
||||
:openapi/default 42})
|
||||
:y int?}}}}}]
|
||||
```
|
||||
|
||||
### Adding examples
|
||||
|
||||
Adding request/response examples have been mentioned above a couple of times
|
||||
above. Here's a summary of the different ways to do it:
|
||||
|
||||
1. Add an example to the schema object using a `:openapi/example`
|
||||
(schema, spec) or `:json-schema/example` (malli) key in your
|
||||
schema/spec/malli model metadata. See the examples above.
|
||||
2. Use `:example` (a single example) or `:examples` (named examples)
|
||||
with per-content-type coercion.
|
||||
|
||||
**Caveat!** When adding examples for query parameters (or headers),
|
||||
you must add the examples to the individual parameters, not the map
|
||||
schema surrounding them. This is due to limitations in how OpenAPI
|
||||
represents query parameters.
|
||||
|
||||
```clj
|
||||
;; Wrong!
|
||||
{:parameters {:query [:map
|
||||
{:json-schema/example {:a 1}}
|
||||
[:a :int]]}}
|
||||
;; Right!
|
||||
{:parameters {:query [:map
|
||||
[:a {:json-schema/example 1} :int]]}}
|
||||
```
|
||||
|
||||
### Named schemas
|
||||
|
||||
OpenAPI supports reusable schema objects that can be referred to with
|
||||
the `"$ref": "#/components/schemas/Foo"` json-schema syntax. This is
|
||||
useful when you have multiple endpoints that use the same schema. It
|
||||
can also make OpenAPI-based code nicer for consumers of your API.
|
||||
These schemas are also rendered in their own section in Swagger UI.
|
||||
|
||||
Reusable schema objects are generated for Malli `:ref`s and vars. The
|
||||
[openapi example](../../examples/openapi) showcases this.
|
||||
|
||||
Currently (as of 0.7.2), reusable schema objects are **not** generated
|
||||
for Plumatic Schema or Spec.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
Read more about the [Ring Concepts](https://github.com/ring-clojure/ring/wiki/Concepts).
|
||||
|
||||
```clj
|
||||
[metosin/reitit-ring "0.7.0-alpha7"]
|
||||
[metosin/reitit-ring "0.9.2"]
|
||||
```
|
||||
|
||||
## `reitit.ring/router`
|
||||
|
|
@ -141,7 +141,7 @@ Name-based reverse routing:
|
|||
|
||||
# Middleware
|
||||
|
||||
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:
|
||||
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:
|
||||
|
||||
1. normal ring middleware function `handler -> request -> response`
|
||||
2. vector of middleware function `[handler args*] -> request -> response` and it's arguments
|
||||
|
|
@ -194,11 +194,56 @@ Top-level middleware, applied before any routing is done:
|
|||
(def app
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
["/api" {:middleware [[mw :api]]}
|
||||
["/api" {:middleware [[wrap :api]]}
|
||||
["/get" {:get handler}]])
|
||||
nil
|
||||
{:middleware [[mw :top]]}))
|
||||
{:middleware [[wrap :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!
|
||||
|
|
|
|||
|
|
@ -54,14 +54,16 @@ This way, they are only served if none of the actual routes have matched.
|
|||
|
||||
`reitit.ring/create-file-handler` and `reitit.ring/create-resource-handler` take optionally an options map to configure how the files are being served.
|
||||
|
||||
| key | description |
|
||||
| -------------------|-------------|
|
||||
| :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:`
|
||||
| :root | optional resource root, defaults to `\"public\"`
|
||||
| :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router.
|
||||
| :loader | optional class loader to resolve the resources
|
||||
| :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]`
|
||||
| :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)
|
||||
| key | description |
|
||||
| --------------------|-------------|
|
||||
| :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:`
|
||||
| :root | optional resource root, defaults to `\"public\"`
|
||||
| :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router.
|
||||
| :loader | optional class loader to resolve the resources
|
||||
| :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]`
|
||||
| :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)
|
||||
|
||||
|
||||
### TODO
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Swagger Support
|
||||
|
||||
```
|
||||
[metosin/reitit-swagger "0.7.0-alpha7"]
|
||||
[metosin/reitit-swagger "0.9.2"]
|
||||
```
|
||||
|
||||
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.7.0-alpha7"]
|
||||
[metosin/reitit-swagger-ui "0.9.2"]
|
||||
```
|
||||
|
||||
`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:
|
||||
|
|
@ -286,7 +286,18 @@ Example with:
|
|||
; ("/common/ping" "/one/ping" "/two/ping" "/two/deep/ping")
|
||||
```
|
||||
|
||||
### TODO
|
||||
## Reusable schema definitions
|
||||
|
||||
Swagger supports having reusable schema definitions under the
|
||||
`"definitions"` key. These can be reused in different parts of
|
||||
swagger.json using the `"$ref": "#/definitions/Foo"` syntax. These
|
||||
definitions are also rendered in their own section in Swagger UI.
|
||||
|
||||
Reusable schema objects are generated for Malli `:ref`s and vars.
|
||||
Currently (as of 0.7.2), reusable schema objects are **not** generated
|
||||
for Plumatic Schema or Spec.
|
||||
|
||||
## TODO
|
||||
|
||||
* ClojureScript
|
||||
* example for [Macchiato](https://github.com/macchiato-framework)
|
||||
|
|
|
|||
|
|
@ -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.7.0-alpha7"]
|
||||
[metosin/reitit-middleware "0.9.2"]
|
||||
```
|
||||
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,13 @@
|
|||
## frontend-prompt
|
||||
## frontend-re-frame
|
||||
## frontend
|
||||
|
||||
Frontend example with clojure.spec coercion.
|
||||
|
||||
## frontend-malli
|
||||
|
||||
Frontend example with Malli coercion.
|
||||
|
||||
## http-swagger
|
||||
|
||||
Coercion with Spec and Swagger generation.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
(defproject ring-example "0.1.0-SNAPSHOT"
|
||||
(defproject buddy-auth "0.1.0-SNAPSHOT"
|
||||
:description "Reitit Buddy Auth App"
|
||||
:dependencies [[org.clojure/clojure "1.10.1"]
|
||||
[ring/ring-jetty-adapter "1.8.1"]
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
[buddy "2.0.0"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -1,23 +1,25 @@
|
|||
(defproject frontend "0.1.0-SNAPSHOT"
|
||||
(defproject frontend-auth "0.1.0-SNAPSHOT"
|
||||
:description "FIXME: write description"
|
||||
:url "http://example.com/FIXME"
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
|
||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring-server "0.5.0"]
|
||||
[reagent "0.8.1"]
|
||||
[ring "1.7.1"]
|
||||
[reagent "1.2.0"]
|
||||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.10.439"]
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
[metosin/reitit-schema "0.7.0-alpha7"]
|
||||
[metosin/reitit-frontend "0.7.0-alpha7"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
[metosin/reitit-schema "0.9.2"]
|
||||
[metosin/reitit-frontend "0.9.2"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
[fipp "0.6.14"]]
|
||||
|
||||
:plugins [[lein-cljsbuild "1.1.7"]
|
||||
[lein-figwheel "0.5.18"]]
|
||||
:plugins [[lein-cljsbuild "1.1.8"]
|
||||
[lein-figwheel "0.5.20"]]
|
||||
|
||||
:source-paths []
|
||||
:resource-paths ["resources" "target/cljsbuild"]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
(ns frontend.core
|
||||
(:require [reagent.core :as r]
|
||||
[reagent.dom :as rd]
|
||||
[reitit.frontend :as rf]
|
||||
[reitit.frontend.easy :as rfe]
|
||||
[reitit.frontend.controllers :as rfc]
|
||||
|
|
@ -147,6 +148,6 @@
|
|||
(assoc state :match (assoc new-match :controllers (rfc/apply-controllers (:controllers (:match state)) new-match)))
|
||||
(assoc state :match new-match))))))
|
||||
{:use-fragment true})
|
||||
(r/render [main-view] (.getElementById js/document "app")))
|
||||
(rd/render [main-view] (.getElementById js/document "app")))
|
||||
|
||||
(init!)
|
||||
|
|
|
|||
|
|
@ -1,23 +1,25 @@
|
|||
(defproject frontend "0.1.0-SNAPSHOT"
|
||||
(defproject frontend-controllers "0.1.0-SNAPSHOT"
|
||||
:description "FIXME: write description"
|
||||
:url "http://example.com/FIXME"
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
|
||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring-server "0.5.0"]
|
||||
[reagent "0.8.1"]
|
||||
[ring "1.7.1"]
|
||||
[reagent "1.2.0"]
|
||||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.10.439"]
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
[metosin/reitit-schema "0.7.0-alpha7"]
|
||||
[metosin/reitit-frontend "0.7.0-alpha7"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
[metosin/reitit-schema "0.9.2"]
|
||||
[metosin/reitit-frontend "0.9.2"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
[fipp "0.6.14"]]
|
||||
|
||||
:plugins [[lein-cljsbuild "1.1.7"]
|
||||
[lein-figwheel "0.5.18"]]
|
||||
:plugins [[lein-cljsbuild "1.1.8"]
|
||||
[lein-figwheel "0.5.20"]]
|
||||
|
||||
:source-paths []
|
||||
:resource-paths ["resources" "target/cljsbuild"]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
(ns frontend.core
|
||||
(:require [reagent.core :as r]
|
||||
[reagent.dom :as rd]
|
||||
[reitit.frontend :as rf]
|
||||
[reitit.frontend.easy :as rfe]
|
||||
[reitit.frontend.controllers :as rfc]
|
||||
|
|
@ -88,6 +89,6 @@
|
|||
(if new-match
|
||||
(assoc new-match :controllers (rfc/apply-controllers (:controllers old-match) new-match))))))
|
||||
{:use-fragment true})
|
||||
(r/render [current-page] (.getElementById js/document "app")))
|
||||
(rd/render [current-page] (.getElementById js/document "app")))
|
||||
|
||||
(init!)
|
||||
|
|
|
|||
|
|
@ -1,24 +1,26 @@
|
|||
(defproject frontend "0.1.0-SNAPSHOT"
|
||||
(defproject frontend-links "0.1.0-SNAPSHOT"
|
||||
:description "FIXME: write description"
|
||||
:url "http://example.com/FIXME"
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
|
||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring-server "0.5.0"]
|
||||
[reagent "0.8.1"]
|
||||
[ring "1.7.1"]
|
||||
[reagent "1.2.0"]
|
||||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.10.520"]
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
[metosin/reitit-spec "0.7.0-alpha7"]
|
||||
[metosin/reitit-frontend "0.7.0-alpha7"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
[metosin/reitit-spec "0.9.2"]
|
||||
[metosin/reitit-frontend "0.9.2"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
[fipp "0.6.14"]]
|
||||
|
||||
:plugins [[lein-cljsbuild "1.1.7"]
|
||||
[lein-figwheel "0.5.18"]
|
||||
[cider/cider-nrepl "0.21.1"]]
|
||||
:plugins [[lein-cljsbuild "1.1.8"]
|
||||
[lein-figwheel "0.5.20"]
|
||||
[cider/cider-nrepl "0.47.1"]]
|
||||
|
||||
:repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
(:require [clojure.string :as string]
|
||||
[fipp.edn :as fedn]
|
||||
[reagent.core :as r]
|
||||
[reagent.dom :as rd]
|
||||
[reitit.coercion.spec :as rss]
|
||||
[reitit.frontend :as rf]
|
||||
[reitit.frontend.easy :as rfe]
|
||||
|
|
@ -137,7 +138,7 @@
|
|||
(fn [m] (reset! current-match m))
|
||||
;; set to false to enable HistoryAPI
|
||||
{:use-fragment true})
|
||||
(r/render [current-page] (.getElementById js/document "app")))
|
||||
(rd/render [current-page] (.getElementById js/document "app")))
|
||||
|
||||
(init!)
|
||||
|
||||
|
|
|
|||
13
examples/frontend-malli/README.md
Normal file
13
examples/frontend-malli/README.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# reitit-frontend example
|
||||
|
||||
## Usage
|
||||
|
||||
```clj
|
||||
> lein figwheel
|
||||
```
|
||||
|
||||
Go with browser to http://localhost:3449
|
||||
|
||||
## License
|
||||
|
||||
Copyright © Metosin Oy and collaborators
|
||||
1
examples/frontend-malli/checkouts/reitit-core
Symbolic link
1
examples/frontend-malli/checkouts/reitit-core
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../../modules/reitit-core
|
||||
1
examples/frontend-malli/checkouts/reitit-frontend
Symbolic link
1
examples/frontend-malli/checkouts/reitit-frontend
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../../modules/reitit-frontend
|
||||
1
examples/frontend-malli/checkouts/reitit-schema
Symbolic link
1
examples/frontend-malli/checkouts/reitit-schema
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../../modules/reitit-schema
|
||||
57
examples/frontend-malli/project.clj
Normal file
57
examples/frontend-malli/project.clj
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
(defproject frontend-malli "0.1.0-SNAPSHOT"
|
||||
:description "FIXME: write description"
|
||||
:url "http://example.com/FIXME"
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
|
||||
:dependencies [[org.clojure/clojure "1.10.1"]
|
||||
[ring-server "0.5.0"]
|
||||
[reagent "1.2.0"]
|
||||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
[metosin/reitit-malli "0.9.2"]
|
||||
[metosin/reitit-frontend "0.9.2"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
[fipp "0.6.23"]]
|
||||
|
||||
:plugins [[lein-cljsbuild "1.1.8"]
|
||||
[lein-figwheel "0.5.20"]]
|
||||
|
||||
:source-paths []
|
||||
:resource-paths ["resources" "target/cljsbuild"]
|
||||
|
||||
:profiles {:dev {:dependencies [[binaryage/devtools "1.0.2"]]}}
|
||||
|
||||
:cljsbuild
|
||||
{:builds
|
||||
[{:id "app"
|
||||
:figwheel true
|
||||
:source-paths ["src"]
|
||||
:watch-paths ["src" "checkouts/reitit-frontend/src"]
|
||||
:compiler {:main "frontend.core"
|
||||
:asset-path "/js/out"
|
||||
:output-to "target/cljsbuild/public/js/app.js"
|
||||
:output-dir "target/cljsbuild/public/js/out"
|
||||
:source-map true
|
||||
:optimizations :none
|
||||
:pretty-print true
|
||||
:preloads [devtools.preload]
|
||||
:aot-cache true}}
|
||||
{:id "min"
|
||||
:source-paths ["src"]
|
||||
:compiler {:output-to "target/cljsbuild/public/js/app.js"
|
||||
:output-dir "target/cljsbuild/public/js"
|
||||
:source-map "target/cljsbuild/public/js/app.js.map"
|
||||
:optimizations :advanced
|
||||
:pretty-print false
|
||||
:aot-cache true}}]}
|
||||
|
||||
:figwheel {:http-server-root "public"
|
||||
:server-port 3449
|
||||
:nrepl-port 7002
|
||||
;; Server index.html for all routes for HTML5 routing
|
||||
:ring-handler backend.server/handler})
|
||||
10
examples/frontend-malli/resources/public/index.html
Normal file
10
examples/frontend-malli/resources/public/index.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Reitit frontend example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
10
examples/frontend-malli/src/backend/server.clj
Normal file
10
examples/frontend-malli/src/backend/server.clj
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
(ns backend.server
|
||||
(:require [ring.util.response :as resp]
|
||||
[ring.middleware.content-type :as content-type]))
|
||||
|
||||
(def handler
|
||||
(-> (fn [request]
|
||||
(or (resp/resource-response (:uri request) {:root "public"})
|
||||
(-> (resp/resource-response "index.html" {:root "public"})
|
||||
(resp/content-type "text/html"))))
|
||||
content-type/wrap-content-type))
|
||||
84
examples/frontend-malli/src/frontend/core.cljs
Normal file
84
examples/frontend-malli/src/frontend/core.cljs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
(ns frontend.core
|
||||
(:require [reagent.core :as r]
|
||||
[reagent.dom :as rd]
|
||||
[reitit.frontend :as rf]
|
||||
[reitit.frontend.easy :as rfe]
|
||||
[reitit.coercion.malli :as rsm]
|
||||
[fipp.edn :as fedn]))
|
||||
|
||||
(defn home-page []
|
||||
[:div
|
||||
[:h2 "Welcome to frontend"]
|
||||
|
||||
[:button
|
||||
{:type "button"
|
||||
:on-click #(rfe/push-state ::item {:id 3})}
|
||||
"Item 3"]
|
||||
|
||||
[:button
|
||||
{:type "button"
|
||||
:on-click #(rfe/replace-state ::item {:id 4})}
|
||||
"Replace State Item 4"]])
|
||||
|
||||
(defn about-page []
|
||||
[:div
|
||||
[:h2 "About frontend"]
|
||||
[:ul
|
||||
[:li [:a {:href "http://google.com"} "external link"]]
|
||||
[:li [:a {:href (rfe/href ::foobar)} "Missing route"]]
|
||||
[:li [:a {:href (rfe/href ::item)} "Missing route params"]]]
|
||||
|
||||
[:div
|
||||
{:content-editable true
|
||||
:suppressContentEditableWarning true}
|
||||
[:p "Link inside contentEditable element is ignored."]
|
||||
[:a {:href (rfe/href ::frontpage)} "Link"]]])
|
||||
|
||||
(defn item-page [match]
|
||||
(let [{:keys [path query]} (:parameters match)
|
||||
{:keys [id]} path]
|
||||
[:div
|
||||
[:h2 "Selected item " id]
|
||||
(if (:foo query)
|
||||
[:p "Optional foo query param: " (:foo query)])]))
|
||||
|
||||
(defonce match (r/atom nil))
|
||||
|
||||
(defn current-page []
|
||||
[:div
|
||||
[:ul
|
||||
[:li [:a {:href (rfe/href ::frontpage)} "Frontpage"]]
|
||||
[:li [:a {:href (rfe/href ::about)} "About"]]
|
||||
[:li [:a {:href (rfe/href ::item {:id 1})} "Item 1"]]
|
||||
[:li [:a {:href (rfe/href ::item {:id 2} {:foo "bar"})} "Item 2"]]]
|
||||
(if @match
|
||||
(let [view (:view (:data @match))]
|
||||
[view @match]))
|
||||
[:pre (with-out-str (fedn/pprint @match))]])
|
||||
|
||||
(def routes
|
||||
[["/"
|
||||
{:name ::frontpage
|
||||
:view home-page}]
|
||||
|
||||
["/about"
|
||||
{:name ::about
|
||||
:view about-page}]
|
||||
|
||||
["/item/:id"
|
||||
{:name ::item
|
||||
:view item-page
|
||||
:parameters {:path [:map
|
||||
[:id :int]]
|
||||
:query [:map
|
||||
[:foo {:optional true} :keyword]]}}]])
|
||||
|
||||
(defn init! []
|
||||
(rfe/start!
|
||||
(rf/router routes {:data {:coercion rsm/coercion}})
|
||||
(fn [m] (reset! match m))
|
||||
;; set to false to enable HistoryAPI
|
||||
{:use-fragment true})
|
||||
(rd/render [current-page] (.getElementById js/document "app")))
|
||||
|
||||
(init!)
|
||||
|
|
@ -1,24 +1,27 @@
|
|||
(defproject frontend "0.1.0-SNAPSHOT"
|
||||
(defproject frontend-prompt "0.1.0-SNAPSHOT"
|
||||
:description "FIXME: write description"
|
||||
:url "http://example.com/FIXME"
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
|
||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring-server "0.5.0"]
|
||||
[reagent "0.8.1"]
|
||||
[ring "1.7.1"]
|
||||
[reagent "1.2.0"]
|
||||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.10.520"]
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
[metosin/reitit-spec "0.7.0-alpha7"]
|
||||
[metosin/reitit-frontend "0.7.0-alpha7"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
[metosin/reitit-spec "0.9.2"]
|
||||
[metosin/reitit-frontend "0.9.2"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
[fipp "0.6.14"]]
|
||||
[fipp "0.6.23"]]
|
||||
|
||||
:plugins [[lein-cljsbuild "1.1.8"]
|
||||
[lein-figwheel "0.5.20"]
|
||||
[cider/cider-nrepl "0.47.1"]]
|
||||
|
||||
:plugins [[lein-cljsbuild "1.1.7"]
|
||||
[lein-figwheel "0.5.18"]
|
||||
[cider/cider-nrepl "0.21.1"]]
|
||||
|
||||
:repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
(ns frontend.core
|
||||
(:require [fipp.edn :as fedn]
|
||||
[reagent.core :as r]
|
||||
[reagent.dom :as rd]
|
||||
[reitit.coercion.spec :as rss]
|
||||
[reitit.frontend :as rf]
|
||||
[reitit.frontend.easy :as rfe]))
|
||||
|
|
@ -63,6 +64,6 @@
|
|||
on-navigate
|
||||
;; set to false to enable HistoryAPI
|
||||
{:use-fragment true})
|
||||
(r/render [current-page] (.getElementById js/document "app")))
|
||||
(rd/render [current-page] (.getElementById js/document "app")))
|
||||
|
||||
(init!)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
(defproject frontend-re-frame "0.1.0-SNAPSHOT"
|
||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||
[org.clojure/clojurescript "1.10.520"]
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
[reagent "0.8.1"]
|
||||
[re-frame "0.10.6"]]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
[reagent "1.2.0"]
|
||||
[re-frame "0.10.6"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]]
|
||||
|
||||
:plugins [[lein-cljsbuild "1.1.7"]
|
||||
[lein-figwheel "0.5.18"]
|
||||
[cider/cider-nrepl "0.21.1"]]
|
||||
:plugins [[lein-cljsbuild "1.1.8"]
|
||||
[lein-figwheel "0.5.20"]
|
||||
[cider/cider-nrepl "0.47.1"]]
|
||||
|
||||
:repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}
|
||||
:min-lein-version "2.5.3"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
(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]
|
||||
[reitit.frontend :as rf]
|
||||
|
|
@ -143,7 +144,7 @@
|
|||
(re-frame/dispatch-sync [::initialize-db])
|
||||
(dev-setup)
|
||||
(init-routes!) ;; Reset routes on figwheel reload
|
||||
(reagent/render [router-component {:router router}]
|
||||
(rd/render [router-component {:router router}]
|
||||
(.getElementById js/document "app")))
|
||||
|
||||
(init)
|
||||
|
|
|
|||
|
|
@ -4,15 +4,17 @@
|
|||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
|
||||
:dependencies [[org.clojure/clojure "1.10.1"]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring-server "0.5.0"]
|
||||
[reagent "0.10.0"]
|
||||
[ring "1.8.1"]
|
||||
[reagent "1.2.0"]
|
||||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.10.773"]
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
[metosin/reitit-spec "0.7.0-alpha7"]
|
||||
[metosin/reitit-frontend "0.7.0-alpha7"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
[metosin/reitit-spec "0.9.2"]
|
||||
[metosin/reitit-frontend "0.9.2"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
[fipp "0.6.23"]]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
(ns frontend.core
|
||||
(:require [reagent.core :as r]
|
||||
[reagent.dom :as rd]
|
||||
[reitit.frontend :as rf]
|
||||
[reitit.frontend.easy :as rfe]
|
||||
[reitit.coercion.spec :as rss]
|
||||
|
|
@ -77,6 +78,6 @@
|
|||
(fn [m] (reset! match m))
|
||||
;; set to false to enable HistoryAPI
|
||||
{:use-fragment true})
|
||||
(r/render [current-page] (.getElementById js/document "app")))
|
||||
(rd/render [current-page] (.getElementById js/document "app")))
|
||||
|
||||
(init!)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
(defproject ring-example "0.1.0-SNAPSHOT"
|
||||
(defproject http-swagger "0.1.0-SNAPSHOT"
|
||||
:description "Reitit Http App with Swagger"
|
||||
:dependencies [[org.clojure/clojure "1.11.1"]
|
||||
[ring/ring-jetty-adapter "1.7.1"]
|
||||
[aleph "0.4.7-alpha5"]
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[aleph "0.7.1"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
[metosin/ring-swagger-ui "5.9.0"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
(defproject ring-example "0.1.0-SNAPSHOT"
|
||||
(defproject http "0.1.0-SNAPSHOT"
|
||||
:description "Reitit Ring App with Swagger"
|
||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||
[org.clojure/core.async "0.4.490"]
|
||||
[funcool/promesa "1.9.0"]
|
||||
[manifold "0.1.8"]
|
||||
[ring/ring-jetty-adapter "1.7.1"]
|
||||
[metosin/reitit "0.7.0-alpha7"]]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[org.clojure/core.async "1.6.681"]
|
||||
[funcool/promesa "11.0.678"]
|
||||
[manifold "0.4.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.2"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
(defproject just-coercion-with-ring "0.1.0-SNAPSHOT"
|
||||
:description "Reitit coercion with vanilla ring"
|
||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||
[ring/ring-jetty-adapter "1.7.1"]
|
||||
[metosin/reitit "0.7.0-alpha7"]])
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.2"]])
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
(defproject openapi "0.1.0-SNAPSHOT"
|
||||
:description "Reitit OpenAPI example"
|
||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||
[metosin/jsonista "0.2.6"]
|
||||
[ring/ring-jetty-adapter "1.7.1"]
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[metosin/jsonista "0.3.8"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
[metosin/ring-swagger-ui "5.9.0"]]
|
||||
:repl-options {:init-ns example.server}
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.3.2"]]}})
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}})
|
||||
|
|
|
|||
|
|
@ -12,8 +12,28 @@
|
|||
[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
|
||||
[:map
|
||||
[:amount :double]
|
||||
[:from :string]])
|
||||
|
||||
(def AccountId
|
||||
[:map
|
||||
[:bank :string]
|
||||
[:id :string]])
|
||||
|
||||
(def Account
|
||||
[:map
|
||||
[:bank :string]
|
||||
[:id :string]
|
||||
[:balance :double]
|
||||
[:transactions [:vector #'Transaction]]])
|
||||
|
||||
|
||||
|
||||
(def app
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
|
|
@ -30,47 +50,60 @@
|
|||
|
||||
["/pizza"
|
||||
{:get {:summary "Fetch a pizza | Multiple content-types, multiple examples"
|
||||
:responses {200 {:content {"application/json" {:description "Fetch a pizza as json"
|
||||
:schema [:map
|
||||
: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 {:color :white
|
||||
:value {:format :json
|
||||
:color :white
|
||||
:pineapple true}}
|
||||
:red {:description "Red pizza"
|
||||
:value {:color :red
|
||||
:value {:format :json
|
||||
:color :red
|
||||
:pineapple false}}}}
|
||||
"application/edn" {:description "Fetch a pizza as edn"
|
||||
:schema [:map
|
||||
"application/edn" {:schema [:map
|
||||
[:format [:enum :edn]]
|
||||
[:color :keyword]
|
||||
[:pineapple :boolean]]
|
||||
:examples {:red {:description "Red pizza with pineapple"
|
||||
:value (pr-str {:color :red :pineapple true})}}}}}}
|
||||
:value (pr-str {:format :edn :color :red :pineapple true})}}}}}}
|
||||
:handler (fn [_request]
|
||||
{:status 200
|
||||
:body {:color :red
|
||||
:pineapple true}})}
|
||||
:post {:summary "Create a pizza | Multiple content-types, multiple examples"
|
||||
:request {:content {"application/json" {:description "Create a pizza using json"
|
||||
:schema [:map
|
||||
(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}}]))}
|
||||
: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
|
||||
[:color :keyword]
|
||||
[:pineapple :boolean]]
|
||||
:examples {:purple {:value {:color :purple
|
||||
:pineapple false}}}}
|
||||
"application/edn" {:description "Create a pizza using EDN"
|
||||
:schema [:map
|
||||
"application/edn" {:schema [:map
|
||||
[:color :keyword]
|
||||
[:pineapple :boolean]]
|
||||
:examples {:purple {:value (pr-str {:color :purple
|
||||
:pineapple false})}}}}}
|
||||
;; Need to list content types explicitly because we use :default in :responses
|
||||
:openapi/response-content-types ["application/json" "application/edn"]
|
||||
:responses {200 {:content {:default {:description "Success"
|
||||
:schema [:map [:success :boolean]]
|
||||
:example {:success true}}}}}
|
||||
:responses {200 {:description "Success"
|
||||
:content {:default {:schema [:map [:success :boolean]]
|
||||
:example {:success true}}}}
|
||||
:default {:description "Not success"
|
||||
:content {:default {:schema [:map [:error :string]]
|
||||
:example {:error "error"}}}}}
|
||||
:handler (fn [_request]
|
||||
{:status 200
|
||||
:body {:success true}})}}]
|
||||
(if (< (Math/random) 0.5)
|
||||
{:status 200
|
||||
:body {:success true}}
|
||||
{:status 500
|
||||
:body {:error "an error happened"}}))}}]
|
||||
|
||||
|
||||
["/contact"
|
||||
|
|
@ -81,6 +114,10 @@
|
|||
:json-schema/default 30
|
||||
:json-schema/example 10}
|
||||
int?]
|
||||
[:charset {:title "Which charset to use?"
|
||||
:optional true
|
||||
:json-schema/deprecated true}
|
||||
string?]
|
||||
[:email {:title "Email address to search for"
|
||||
:json-schema/format "email"}
|
||||
string?]]}
|
||||
|
|
@ -91,8 +128,51 @@
|
|||
[:email {:json-schema/example "heidi@alps.ch"}
|
||||
string?]]]}}}}
|
||||
:handler (fn [_request]
|
||||
[{:name "Heidi"
|
||||
:email "heidi@alps.ch"}])}}]
|
||||
{:status 200
|
||||
:body [{:name "Heidi"
|
||||
:email "heidi@alps.ch"}]})}}]
|
||||
|
||||
["/account"
|
||||
{:get {:summary "Fetch an account | Recursive schemas using malli registry, link to external docs"
|
||||
:parameters {:query #'AccountId}
|
||||
:responses {200 {:content {:default {:schema #'Account}}}}
|
||||
:openapi {:externalDocs {:description "The reitit repository"
|
||||
:url "https://github.com/metosin/reitit"}}
|
||||
:handler (fn [_request]
|
||||
{:status 200
|
||||
:body {:bank "MiniBank"
|
||||
:id "0001"
|
||||
:balance 13.5
|
||||
:transactions [{:from "0002"
|
||||
:amount 20.0}
|
||||
{:from "0003"
|
||||
:amount -6.5}]}})}}]
|
||||
|
||||
["/complex"
|
||||
{:post {:summary "Complex schema with :multi, :enum, :tuple etc."
|
||||
:request {:content
|
||||
{:default
|
||||
{:schema [:map
|
||||
[:vector-of-tuples [:vector [:tuple :string :int]]]
|
||||
[:regex [:re "[0-9]+"]]
|
||||
[:enum [:enum 1 3 5 42]]
|
||||
[:multi [:multi {:dispatch :type}
|
||||
[:literal [:map
|
||||
[:type [:= :literal]]
|
||||
[:value [:or :int :string]]]]
|
||||
[:reference [:map
|
||||
[:type [:= :reference]]
|
||||
[:description :string]
|
||||
[:ref :uuid]]]]]]
|
||||
:example {:vector-of-tuples [["a" 1] ["b" 2]]
|
||||
:regex "01234"
|
||||
:enum 5
|
||||
:multi {:type :literal
|
||||
:value "x"}}}}}
|
||||
:responses {200 {:content {:default {:schema [:map]}}}}
|
||||
:handler (fn [request]
|
||||
{:status 200
|
||||
:body (:body request)})}}]
|
||||
|
||||
["/secure"
|
||||
{:tags #{"secure"}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
(defproject pedestal-malli-swagger-example "0.1.0-SNAPSHOT"
|
||||
:description "Reitit-http with pedestal"
|
||||
:dependencies [[org.clojure/clojure "1.11.1"]
|
||||
[io.pedestal/pedestal.service "0.5.5"]
|
||||
[io.pedestal/pedestal.jetty "0.5.5"]
|
||||
[metosin/reitit-malli "0.7.0-alpha7"]
|
||||
[metosin/reitit-pedestal "0.7.0-alpha7"]
|
||||
[metosin/reitit "0.7.0-alpha7"]]
|
||||
: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.9.2"]
|
||||
[metosin/reitit-pedestal "0.9.2"]
|
||||
[metosin/reitit "0.9.2"]]
|
||||
:repl-options {:init-ns server})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(ns example.server
|
||||
(ns server
|
||||
(:require [clojure.java.io :as io]
|
||||
[io.pedestal.http.route]
|
||||
[reitit.interceptor]
|
||||
|
|
@ -161,4 +161,4 @@
|
|||
(->> (reset! server)))
|
||||
(println "server running in port 3000"))
|
||||
|
||||
#_(start server config)
|
||||
#_(start server config)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
(defproject ring-example "0.1.0-SNAPSHOT"
|
||||
:description "Reitit-http with pedestal"
|
||||
:dependencies [[org.clojure/clojure "1.11.1"]
|
||||
[io.pedestal/pedestal.service "0.5.5"]
|
||||
[io.pedestal/pedestal.jetty "0.5.5"]
|
||||
[metosin/reitit-pedestal "0.7.0-alpha7"]
|
||||
[metosin/reitit "0.7.0-alpha7"]]
|
||||
: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.9.2"]
|
||||
[metosin/reitit "0.9.2"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
(defproject ring-example "0.1.0-SNAPSHOT"
|
||||
(defproject pedestal-example "0.1.0-SNAPSHOT"
|
||||
:description "Reitit-http with pedestal"
|
||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||
[io.pedestal/pedestal.service "0.5.5"]
|
||||
[io.pedestal/pedestal.jetty "0.5.5"]
|
||||
[metosin/reitit-pedestal "0.7.0-alpha7"]
|
||||
[metosin/reitit "0.7.0-alpha7"]]
|
||||
: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.9.2"]
|
||||
[metosin/reitit "0.9.2"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(defproject ring-example "0.1.0-SNAPSHOT"
|
||||
:description "Reitit Ring App"
|
||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||
[ring/ring-jetty-adapter "1.7.1"]
|
||||
[metosin/reitit "0.7.0-alpha7"]]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.2"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
(defproject ring-integrant-example "0.1.0-SNAPSHOT"
|
||||
:description "Reitit Ring App with Integrant"
|
||||
:dependencies [[org.clojure/clojure "1.10.1"]
|
||||
[ring/ring-jetty-adapter "1.7.1"]
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
[integrant "0.7.0"]]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
[integrant "0.8.1"]]
|
||||
:main example.server
|
||||
:repl-options {:init-ns user}
|
||||
:profiles {:dev {:dependencies [[integrant/repl "0.3.1"]]
|
||||
:profiles {:dev {:dependencies [[integrant/repl "0.3.3"]]
|
||||
:source-paths ["dev"]}})
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
(defproject ring-example "0.1.0-SNAPSHOT"
|
||||
(defproject ring-malli-lite-swagger "0.1.0-SNAPSHOT"
|
||||
:description "Reitit Ring App with Swagger"
|
||||
:dependencies [[org.clojure/clojure "1.11.1"]
|
||||
[metosin/jsonista "0.2.6"]
|
||||
[ring/ring-jetty-adapter "1.7.1"]
|
||||
[metosin/reitit "0.7.0-alpha7"]]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[metosin/jsonista "0.3.8"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.2"]]
|
||||
:repl-options {:init-ns example.server}
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.3.2"]]}})
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}})
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@
|
|||
:error-keys #{#_:type :coercion :in :schema :value :errors :humanized #_:transformed}
|
||||
;; schema identity function (default: close all map schemas)
|
||||
:compile mu/closed-schema
|
||||
;; strip-extra-keys (effects only predefined transformers)
|
||||
;; strip-extra-keys (affects only predefined transformers)
|
||||
:strip-extra-keys true
|
||||
;; add/set default values
|
||||
:default-values true
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
(defproject ring-example "0.1.0-SNAPSHOT"
|
||||
(defproject ring-malli-swagger "0.1.0-SNAPSHOT"
|
||||
:description "Reitit Ring App with Swagger"
|
||||
:dependencies [[org.clojure/clojure "1.11.1"]
|
||||
[metosin/jsonista "0.2.6"]
|
||||
[ring/ring-jetty-adapter "1.7.1"]
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[metosin/jsonista "0.3.8"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
[metosin/ring-swagger-ui "5.9.0"]]
|
||||
:repl-options {:init-ns example.server}
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.3.2"]]}})
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}})
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@
|
|||
:error-keys #{#_:type :coercion :in :schema :value :errors :humanized #_:transformed}
|
||||
;; schema identity function (default: close all map schemas)
|
||||
:compile mu/closed-schema
|
||||
;; strip-extra-keys (effects only predefined transformers)
|
||||
;; strip-extra-keys (affects only predefined transformers)
|
||||
:strip-extra-keys true
|
||||
;; add/set default values
|
||||
:default-values true
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
(defproject ring-example "0.1.0-SNAPSHOT"
|
||||
:description "Reitit Ring App with Swagger"
|
||||
:dependencies [[org.clojure/clojure "1.11.1"]
|
||||
[ring/ring-jetty-adapter "1.7.1"]
|
||||
[metosin/reitit "0.7.0-alpha7"]
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.2"]
|
||||
[metosin/ring-swagger-ui "5.9.0"]]
|
||||
:repl-options {:init-ns example.server}
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.3.2"]]}})
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}})
|
||||
|
|
|
|||
18
karma.conf.js
Normal file
18
karma.conf.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
module.exports = function (config) {
|
||||
config.set({
|
||||
browsers: ['ChromeHeadless'],
|
||||
// The directory where the output file lives
|
||||
basePath: 'target/karma',
|
||||
// The file itself
|
||||
files: ['ci.js'],
|
||||
frameworks: ['cljs-test'],
|
||||
reporters: ['progress'],
|
||||
plugins: ['karma-cljs-test', 'karma-chrome-launcher'],
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
client: {
|
||||
args: ["shadow.test.karma.init"],
|
||||
singleRun: true
|
||||
},
|
||||
})
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-core "0.7.0-alpha7"
|
||||
(defproject metosin/reitit-core "0.9.2"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
@ -10,5 +10,5 @@
|
|||
:parent-project {:path "../../project.clj"
|
||||
:inherit [:deploy-repositories :managed-dependencies]}
|
||||
:java-source-paths ["java-src"]
|
||||
:javac-options ["-Xlint:unchecked" "-target" "1.8" "-source" "1.8"]
|
||||
:javac-options ["-Xlint:unchecked" "-target" "11" "-source" "11"]
|
||||
:dependencies [[meta-merge]])
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
(ns reitit.coercion
|
||||
(:require [clojure.walk :as walk]
|
||||
(:require [#?(:clj reitit.walk :cljs clojure.walk) :as walk]
|
||||
[reitit.core :as r]
|
||||
[reitit.impl :as impl])
|
||||
#?(:clj
|
||||
(:import (java.io Writer))))
|
||||
|
|
@ -19,7 +20,8 @@
|
|||
(-open-model [this model] "Returns a new model which allows extra keys in maps")
|
||||
(-encode-error [this error] "Converts error in to a serializable format")
|
||||
(-request-coercer [this type model] "Returns a `value format => value` request coercion function")
|
||||
(-response-coercer [this model] "Returns a `value format => value` response coercion function"))
|
||||
(-response-coercer [this model] "Returns a `value format => value` response coercion function")
|
||||
(-query-string-coercer [this model] "Returns a `value => value` query string coercion function"))
|
||||
|
||||
#?(:clj
|
||||
(defmethod print-method ::coercion [coercion ^Writer w]
|
||||
|
|
@ -128,29 +130,6 @@
|
|||
(request-coercion-failed! result coercion value in request serialize-failed-result)
|
||||
result)))))))
|
||||
|
||||
(defn extract-response-format-default [request _]
|
||||
(-> request :muuntaja/response :format))
|
||||
|
||||
(defn response-coercer [coercion {:keys [content body]} {:keys [extract-response-format serialize-failed-result]
|
||||
:or {extract-response-format extract-response-format-default}}]
|
||||
(if coercion
|
||||
(let [format->coercer (some->> (concat (when body
|
||||
[[:default (-response-coercer coercion body)]])
|
||||
(for [[format {:keys [schema]}] content, :when schema]
|
||||
[format (-response-coercer coercion schema)]))
|
||||
(filter second) (seq) (into (array-map)))]
|
||||
(when format->coercer
|
||||
(fn [request response]
|
||||
(let [format (extract-response-format request response)
|
||||
value (:body response)
|
||||
coercer (or (format->coercer format)
|
||||
(format->coercer :default)
|
||||
-identity-coercer)
|
||||
result (coercer value format)]
|
||||
(if (error? result)
|
||||
(response-coercion-failed! result coercion value request response serialize-failed-result)
|
||||
result)))))))
|
||||
|
||||
(defn encode-error [data]
|
||||
(-> data
|
||||
(dissoc :request :response)
|
||||
|
|
@ -163,12 +142,6 @@
|
|||
(impl/fast-assoc acc k (coercer request)))
|
||||
{} coercers))
|
||||
|
||||
(defn coerce-response [coercers request response]
|
||||
(if response
|
||||
(if-let [coercer (or (coercers (:status response)) (coercers :default))]
|
||||
(impl/fast-assoc response :body (coercer request response))
|
||||
response)))
|
||||
|
||||
(defn request-coercers
|
||||
([coercion parameters opts]
|
||||
(some->> (for [[k v] parameters, :when v]
|
||||
|
|
@ -179,10 +152,45 @@
|
|||
rcs (request-coercers coercion parameters (cond-> opts route-request (assoc ::skip #{:body})))]
|
||||
(if (and crc rcs) (into crc (vec rcs)) (or crc rcs)))))
|
||||
|
||||
(defn response-coercers [coercion responses opts]
|
||||
(some->> (for [[status model] responses]
|
||||
[status (response-coercer coercion model opts)])
|
||||
(filter second) (seq) (into {})))
|
||||
(defn extract-response-format-default [request response]
|
||||
(or (get-in response [:headers "Content-Type"])
|
||||
(:muuntaja/content-type response)
|
||||
(-> request :muuntaja/response :format)))
|
||||
|
||||
(defn -format->coercer [coercion {:keys [content body]} _opts]
|
||||
(->> (concat (when body
|
||||
[[:default (-response-coercer coercion body)]])
|
||||
(for [[format {:keys [schema]}] content, :when schema]
|
||||
[format (-response-coercer coercion schema)]))
|
||||
(filter second) (into (array-map))))
|
||||
|
||||
(defn response-coercer [coercion responses {:keys [extract-response-format serialize-failed-result]
|
||||
:or {extract-response-format extract-response-format-default}
|
||||
:as opts}]
|
||||
(when coercion
|
||||
(let [status->format->coercer
|
||||
(into {}
|
||||
(for [[status model] responses]
|
||||
(do
|
||||
(when-not (or (= :default status) (int? status))
|
||||
(throw (ex-info "Response status must be int or :default" {:status status})))
|
||||
[status (-format->coercer coercion model opts)])))]
|
||||
(when-not (every? empty? (vals status->format->coercer)) ;; fast path: return nil if there are no models to coerce
|
||||
(fn [request response]
|
||||
(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)))]
|
||||
(if-not coercer
|
||||
response
|
||||
(let [value (:body response)
|
||||
coerced (coercer (:body response) format)
|
||||
result (if (error? coerced)
|
||||
(response-coercion-failed! coerced coercion value request response serialize-failed-result)
|
||||
coerced)]
|
||||
(impl/fast-assoc response :body result)))))))))
|
||||
|
||||
(defn -compile-parameters [data coercion]
|
||||
(impl/path-update data [[[:parameters any?] #(-compile-model coercion % nil)]]))
|
||||
|
|
@ -219,3 +227,33 @@
|
|||
[match]
|
||||
(if-let [coercers (-> match :result :coerce)]
|
||||
(coerce-request coercers match)))
|
||||
|
||||
(defn coerce-query-params
|
||||
"Uses an input schema and coercion implementation from the given match to
|
||||
encode query-parameters map.
|
||||
|
||||
If no match, no input schema or coercion implementation, just returns the
|
||||
original parameters map."
|
||||
[match query-params]
|
||||
(when query-params
|
||||
(let [coercion (-> match :data :coercion)
|
||||
schema (when coercion
|
||||
(-compile-model coercion (-> match :data :parameters :query) nil))
|
||||
coercer (when (and schema coercion)
|
||||
(-query-string-coercer coercion schema))]
|
||||
(if coercer
|
||||
(let [result (coercer query-params :default)]
|
||||
(if (error? result)
|
||||
(throw (ex-info (str "Query parameters coercion failed")
|
||||
result))
|
||||
result))
|
||||
query-params))))
|
||||
|
||||
(defn match->path
|
||||
"Create routing path from given match and optional query-parameters map.
|
||||
|
||||
Query-parameters are encoded using the input schema and coercion implementation."
|
||||
([match]
|
||||
(r/match->path match))
|
||||
([match query-params]
|
||||
(r/match->path match (coerce-query-params match query-params))))
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@
|
|||
:cljs function)
|
||||
(expand [this _] {:handler this})
|
||||
|
||||
#?(:clj clojure.lang.Var
|
||||
:cljs cljs.core.Var)
|
||||
(expand [this _] {:handler this})
|
||||
|
||||
nil
|
||||
(expand [_ _]))
|
||||
|
||||
|
|
@ -64,10 +68,12 @@
|
|||
(:template match) (:required match) path-params)))))
|
||||
|
||||
(defn match->path
|
||||
"Create routing path from given match and optional query-parameters map."
|
||||
([match]
|
||||
(match->path match nil))
|
||||
([match query-params]
|
||||
(some-> match :path (cond-> (seq query-params) (str "?" (impl/query-string query-params))))))
|
||||
(some-> match :path (cond-> (seq query-params)
|
||||
(str "?" (impl/query-string query-params))))))
|
||||
|
||||
;;
|
||||
;; Different routers
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
(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))))))))
|
||||
|
|
@ -23,23 +23,39 @@
|
|||
|
||||
(defn -path-vals [m path-map]
|
||||
(letfn [(-path-vals [l p m]
|
||||
(reduce
|
||||
(fn [l [k v]]
|
||||
(reduce-kv
|
||||
(fn [l k v]
|
||||
(let [p' (conj p k)
|
||||
f (-match p' path-map)]
|
||||
(cond
|
||||
f (conj l [p' (f v)])
|
||||
(and (map? v) (seq v)) (-path-vals l p' v)
|
||||
(and (map? v) (not (record? v)) (seq v)) (-path-vals l p' v)
|
||||
:else (conj l [p' v]))))
|
||||
l m))]
|
||||
(-path-vals [] [] m)))
|
||||
|
||||
(defn -copy-meta [to from]
|
||||
(letfn [(-with-meta [x m]
|
||||
(try (with-meta x m) (catch #?(:clj Exception, :cljs js/Error) _ x)))
|
||||
(-copy [l p m]
|
||||
(reduce-kv
|
||||
(fn [l k v]
|
||||
(let [p' (conj p k)
|
||||
m' (when (empty? (meta v)) (meta (get-in from p')))]
|
||||
(cond
|
||||
m' (update-in l p' -with-meta m')
|
||||
(and (map? v) (not (record? v)) (seq v)) (-copy l p' v)
|
||||
:else l)))
|
||||
l m))]
|
||||
(-copy to [] to)))
|
||||
|
||||
(defn -assoc-in-path-vals [c]
|
||||
(reduce (partial apply assoc-in) {} c))
|
||||
|
||||
(defn path-update [m path-map]
|
||||
(-> (-path-vals m path-map)
|
||||
(-assoc-in-path-vals)))
|
||||
(-assoc-in-path-vals)
|
||||
(-copy-meta m)))
|
||||
|
||||
(defn accumulator? [x]
|
||||
(-> x meta ::accumulator))
|
||||
|
|
@ -182,9 +198,8 @@
|
|||
(:path route)))
|
||||
|
||||
(defn throw-on-missing-path-params [template required path-params]
|
||||
(when-not (every? #(contains? path-params %) required)
|
||||
(let [defined (-> path-params keys set)
|
||||
missing (set/difference required defined)]
|
||||
(let [missing (set (remove #(get path-params %) required))]
|
||||
(when-not (empty? missing)
|
||||
(ex/fail!
|
||||
(str "missing path-params for route " template " -> " missing)
|
||||
{:path-params path-params, :required required}))))
|
||||
|
|
|
|||
|
|
@ -37,8 +37,11 @@
|
|||
;; Default data
|
||||
;;
|
||||
|
||||
(defn -multi? [x]
|
||||
(instance? #?(:clj clojure.lang.MultiFn :cljs cljs.core.MultiFn) x))
|
||||
|
||||
(s/def ::name keyword?)
|
||||
(s/def ::handler fn?)
|
||||
(s/def ::handler (s/or :fn fn? :var var? :multi -multi?))
|
||||
(s/def ::no-doc boolean?)
|
||||
(s/def ::conflicting boolean?)
|
||||
(s/def ::default-data
|
||||
|
|
|
|||
58
modules/reitit-core/src/reitit/walk.clj
Normal file
58
modules/reitit-core/src/reitit/walk.clj
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
(ns ^:no-doc reitit.walk)
|
||||
|
||||
(defprotocol IKeywordize
|
||||
(-keywordize [coll]))
|
||||
|
||||
(defn keywordize-keys [m] (-keywordize m))
|
||||
|
||||
(defn- keywordize-kv
|
||||
[m k v]
|
||||
(assoc! m (if (string? k) (keyword k) (-keywordize k)) (-keywordize v)))
|
||||
|
||||
(defn- -keywordize-kvreducible
|
||||
[m]
|
||||
(persistent! (reduce-kv keywordize-kv (transient (empty m)) m)))
|
||||
|
||||
(def ^:private keywordize-xf (map keywordize-keys))
|
||||
|
||||
(defn- -keywordize-default
|
||||
[coll]
|
||||
(into (empty coll) keywordize-xf coll))
|
||||
|
||||
(doseq [type [clojure.lang.PersistentHashSet
|
||||
clojure.lang.PersistentVector
|
||||
clojure.lang.PersistentQueue
|
||||
clojure.lang.PersistentStructMap
|
||||
clojure.lang.PersistentTreeSet]]
|
||||
(extend type IKeywordize {:-keywordize -keywordize-default}))
|
||||
|
||||
(doseq [type [clojure.lang.PersistentArrayMap
|
||||
clojure.lang.PersistentHashMap
|
||||
clojure.lang.PersistentTreeMap]]
|
||||
(extend type IKeywordize {:-keywordize -keywordize-kvreducible}))
|
||||
|
||||
(defn- -keywordize-map
|
||||
[m]
|
||||
(let [f (fn [[k v]] (if (string? k) [(keyword k) v] [k v]))]
|
||||
(into {} (map f) m)))
|
||||
|
||||
(doseq [type [clojure.lang.IPersistentMap]]
|
||||
(extend type IKeywordize {:-keywordize -keywordize-map}))
|
||||
|
||||
(extend-protocol IKeywordize
|
||||
Object
|
||||
(-keywordize [x] x)
|
||||
nil
|
||||
(-keywordize [_] nil)
|
||||
clojure.lang.MapEntry
|
||||
(-keywordize [e] (clojure.lang.MapEntry/create
|
||||
(-keywordize (.key e))
|
||||
(-keywordize (.val e))))
|
||||
clojure.lang.ISeq
|
||||
(-keywordize [coll] (map -keywordize coll))
|
||||
clojure.lang.PersistentList
|
||||
(-keywordize [coll] (apply list (map -keywordize coll)))
|
||||
clojure.lang.PersistentList$EmptyList
|
||||
(-keywordize [x] x)
|
||||
clojure.lang.IRecord
|
||||
(-keywordize [r] (reduce (fn [r x] (conj r (-keywordize x))) r r)))
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-dev "0.7.0-alpha7"
|
||||
(defproject metosin/reitit-dev "0.9.2"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-frontend "0.7.0-alpha7"
|
||||
(defproject metosin/reitit-frontend "0.9.2"
|
||||
:description "Reitit: Clojurescript frontend routing core"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
(defn query-params
|
||||
"Given goog.Uri, read query parameters into a Clojure map."
|
||||
[^goog.Uri uri]
|
||||
(let [q (.getQueryData uri)]
|
||||
(let [^goog.Uri.QueryData q (.getQueryData uri)]
|
||||
(->> q
|
||||
(.getKeys)
|
||||
(map (juxt keyword #(query-param q %)))
|
||||
|
|
@ -40,12 +40,18 @@
|
|||
(defn
|
||||
^{:see-also ["reitit.core/match->path"]}
|
||||
match->path
|
||||
"Create routing path from given match and optional query-string map and
|
||||
optional fragment string."
|
||||
[match query-params fragment]
|
||||
(when-let [path (r/match->path match query-params)]
|
||||
(cond-> path
|
||||
(and fragment (seq fragment)) (str "#" (impl/form-encode fragment)))))
|
||||
"Create routing path from given match and optional query-parameters map and
|
||||
optional fragment string.
|
||||
|
||||
Query-parameters are encoded using the input schema and coercion implementation."
|
||||
([match]
|
||||
(match->path match nil nil))
|
||||
([match query-params]
|
||||
(match->path match query-params nil))
|
||||
([match query-params fragment]
|
||||
(when-let [path (coercion/match->path match query-params)]
|
||||
(cond-> path
|
||||
(and fragment (seq fragment)) (str "#" (impl/form-encode fragment))))))
|
||||
|
||||
(defn match-by-path
|
||||
"Given routing tree and current path, return match with possibly
|
||||
|
|
@ -54,7 +60,7 @@
|
|||
:on-coercion-error - a sideeffecting fn of `match exception -> nil`"
|
||||
([router path] (match-by-path router path nil))
|
||||
([router path {:keys [on-coercion-error]}]
|
||||
(let [uri (.parse goog.Uri path)
|
||||
(let [^goog.Uri uri (.parse goog.Uri path)
|
||||
coerce! (if on-coercion-error
|
||||
(fn [match]
|
||||
(try (coercion/coerce! match)
|
||||
|
|
|
|||
|
|
@ -48,9 +48,10 @@
|
|||
The URL is formatted using Reitit frontend history handler, so using it with
|
||||
anchor element href will correctly trigger route change event.
|
||||
|
||||
Note: currently collections in query-parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them
|
||||
differently, convert the collections to strings first."
|
||||
By default currently collections in query parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\". To encode them differently, you can
|
||||
either use Malli coercion to encode values, or just turn the values to strings
|
||||
before calling the function."
|
||||
([name]
|
||||
(rfh/href @history name nil nil nil))
|
||||
([name path-params]
|
||||
|
|
@ -69,9 +70,10 @@
|
|||
|
||||
Will also trigger on-navigate callback on Reitit frontend History handler.
|
||||
|
||||
Note: currently collections in query parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them
|
||||
differently, convert the collections to strings first.
|
||||
By default currently collections in query parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\". To encode them differently, you can
|
||||
either use Malli coercion to encode values, or just turn the values to strings
|
||||
before calling the function.
|
||||
|
||||
See also:
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/History/pushState"
|
||||
|
|
@ -93,9 +95,10 @@
|
|||
|
||||
Will also trigger on-navigate callback on Reitit frontend History handler.
|
||||
|
||||
Note: currently collections in query-parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them
|
||||
differently, convert the collections to strings first.
|
||||
By default currently collections in query parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\". To encode them differently, you can
|
||||
either use Malli coercion to encode values, or just turn the values to strings
|
||||
before calling the function.
|
||||
|
||||
See also:
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState"
|
||||
|
|
@ -122,9 +125,10 @@
|
|||
|
||||
Will also trigger on-navigate callback on Reitit frontend History handler.
|
||||
|
||||
Note: currently collections in query-parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them
|
||||
differently, convert the collections to strings first.
|
||||
By default currently collections in query parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\". To encode them differently, you can
|
||||
either use Malli coercion to encode values, or just turn the values to strings
|
||||
before calling the function.
|
||||
|
||||
See also:
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
|
||||
|
|
@ -142,8 +146,11 @@
|
|||
New query params can be given as a map, or a function taking
|
||||
the old params and returning the new modified params.
|
||||
|
||||
Note: The query parameter values aren't coereced, so the
|
||||
update fn will see string values for all query params."
|
||||
The current path is matched against the routing tree, and the match data
|
||||
(schema, coercion) is used to encode the query parameters.
|
||||
If the current path doesn't match any route, the query parameters
|
||||
are parsed from the path without coercion and new values
|
||||
are also stored without coercion encoding."
|
||||
([new-query-or-update-fn]
|
||||
(rfh/set-query @history new-query-or-update-fn))
|
||||
([new-query-or-update-fn {:keys [replace] :as opts}]
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@
|
|||
(defn- event-target
|
||||
"Read event's target from composed path to get shadow dom working,
|
||||
fallback to target property if not available"
|
||||
[event]
|
||||
[^goog.events.BrowserEvent event]
|
||||
(let [original-event (.getBrowserEvent event)]
|
||||
(if (exists? (.-composedPath original-event))
|
||||
(aget (.composedPath original-event) 0)
|
||||
|
|
@ -76,9 +76,9 @@
|
|||
should be ignored. This logic will ignore the event
|
||||
if anchor href matches the route tree, and in this case
|
||||
the page location is updated using History API."
|
||||
[router e el uri]
|
||||
[router e el ^goog.Uri uri]
|
||||
(let [current-domain (if (exists? js/location)
|
||||
(.getDomain (.parse goog.Uri js/location)))]
|
||||
(.getDomain ^goog.Uri (.parse goog.Uri js/location)))]
|
||||
(and (or (and (not (.hasScheme uri)) (not (.hasDomain uri)))
|
||||
(= current-domain (.getDomain uri)))
|
||||
(not (.-altKey e))
|
||||
|
|
@ -110,7 +110,7 @@
|
|||
ignore-anchor-click (fn [e]
|
||||
;; Returns the next matching ancestor of event target
|
||||
(when-let [el (closest-by-tag (event-target e) "a")]
|
||||
(let [uri (.parse goog.Uri (.-href el))]
|
||||
(let [^goog.Uri uri (.parse goog.Uri (.-href el))]
|
||||
(when (ignore-anchor-click-predicate router e el uri)
|
||||
(.preventDefault e)
|
||||
(let [path (str (.getPath uri)
|
||||
|
|
@ -187,9 +187,10 @@
|
|||
The URL is formatted using Reitit frontend history handler, so using it with
|
||||
anchor element href will correctly trigger route change event.
|
||||
|
||||
Note: currently collections in query parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them
|
||||
differently, convert the collections to strings first."
|
||||
By default currently collections in query parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\". To encode them differently, you can
|
||||
either use Malli coercion to encode values, or just turn the values to strings
|
||||
before calling the function."
|
||||
([history name]
|
||||
(href history name nil))
|
||||
([history name path-params]
|
||||
|
|
@ -208,9 +209,10 @@
|
|||
|
||||
Will also trigger on-navigate callback on Reitit frontend History handler.
|
||||
|
||||
Note: currently collections in query-parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them
|
||||
differently, convert the collections to strings first.
|
||||
By default currently collections in query parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\". To encode them differently, you can
|
||||
either use Malli coercion to encode values, or just turn the values to strings
|
||||
before calling the function.
|
||||
|
||||
See also:
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/History/pushState"
|
||||
|
|
@ -236,9 +238,10 @@
|
|||
|
||||
Will also trigger on-navigate callback on Reitit frontend History handler.
|
||||
|
||||
Note: currently collections in query-parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them
|
||||
differently, convert the collections to strings first.
|
||||
By default currently collections in query parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\". To encode them differently, you can
|
||||
either use Malli coercion to encode values, or just turn the values to strings
|
||||
before calling the function.
|
||||
|
||||
See also:
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState"
|
||||
|
|
@ -264,9 +267,10 @@
|
|||
|
||||
Will also trigger on-navigate callback on Reitit frontend History handler.
|
||||
|
||||
Note: currently collections in query-parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them
|
||||
differently, convert the collections to strings first.
|
||||
By default currently collections in query parameters are encoded as field-value
|
||||
pairs separated by &, i.e. \"?a=1&a=2\". To encode them differently, you can
|
||||
either use Malli coercion to encode values, or just turn the values to strings
|
||||
before calling the function.
|
||||
|
||||
See also:
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
|
||||
|
|
@ -289,13 +293,22 @@
|
|||
New query params can be given as a map, or a function taking
|
||||
the old params and returning the new modified params.
|
||||
|
||||
Note: The query parameter values aren't coereced, so the
|
||||
update fn will see string values for all query params."
|
||||
The current path is matched against the routing tree, and the match data
|
||||
(schema, coercion) is used to encode the query parameters.
|
||||
If the current path doesn't match any route, the query parameters
|
||||
are parsed from the path without coercion and new values
|
||||
are also stored without coercion encoding."
|
||||
([history new-query-or-update-fn]
|
||||
(set-query history new-query-or-update-fn nil))
|
||||
([history new-query-or-update-fn {:keys [replace] :as opts}]
|
||||
(let [current-path (-get-path history)
|
||||
new-path (rf/set-query-params current-path new-query-or-update-fn)]
|
||||
match (rf/match-by-path (:router history) current-path)
|
||||
new-path (if match
|
||||
(let [query-params (if (fn? new-query-or-update-fn)
|
||||
(new-query-or-update-fn (:query (:parameters match)))
|
||||
new-query-or-update-fn)]
|
||||
(rf/match->path match query-params (:fragment (:parameters match))))
|
||||
(rf/set-query-params current-path new-query-or-update-fn))]
|
||||
(if replace
|
||||
(.replaceState js/window.history nil "" (-href history new-path))
|
||||
(.pushState js/window.history nil "" (-href history new-path)))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-http "0.7.0-alpha7"
|
||||
(defproject metosin/reitit-http "0.9.2"
|
||||
:description "Reitit: HTTP routing with interceptors"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@
|
|||
(interceptor/queue executor))
|
||||
router-opts (-> (r/options router)
|
||||
(assoc ::interceptor/queue (partial interceptor/queue executor))
|
||||
(dissoc :data) ; data is already merged into routes
|
||||
(dissoc :data :path) ; data and path already take effect in routes
|
||||
(cond-> (seq interceptors)
|
||||
(update-in [:data :interceptors] (partial into (vec interceptors)))))
|
||||
router (reitit.http/router (r/routes router) router-opts) ;; will re-compile the interceptors
|
||||
|
|
|
|||
|
|
@ -41,11 +41,11 @@
|
|||
(not responses) {}
|
||||
;; mount
|
||||
:else
|
||||
(if-let [coercers (coercion/response-coercers coercion responses opts)]
|
||||
(if-let [coercer (coercion/response-coercer coercion responses opts)]
|
||||
{:leave (fn [ctx]
|
||||
(let [request (:request ctx)
|
||||
response (:response ctx)
|
||||
response (coercion/coerce-response coercers request response)]
|
||||
response (coercer request response)]
|
||||
(assoc ctx :response response)))}
|
||||
{})))})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-interceptors "0.7.0-alpha7"
|
||||
(defproject metosin/reitit-interceptors "0.9.2"
|
||||
: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.7.0-alpha7"
|
||||
(defproject metosin/reitit-malli "0.9.2"
|
||||
:description "Reitit: Malli coercion"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
(mt/transformer
|
||||
(if strip-extra-keys (mt/strip-extra-keys-transformer))
|
||||
transformer
|
||||
(if default-values (mt/default-value-transformer))))))
|
||||
(if default-values (mt/default-value-transformer (if (map? default-values) default-values {})))))))
|
||||
|
||||
(def string-transformer-provider (-provider (mt/string-transformer)))
|
||||
(def json-transformer-provider (-provider (mt/json-transformer)))
|
||||
|
|
@ -76,6 +76,22 @@
|
|||
(assoc error :transformed transformed))))
|
||||
value))))))))
|
||||
|
||||
(defn- -query-string-coercer
|
||||
"Create coercer for query-parameters, always allows extra params and does
|
||||
encoding using string-transformer."
|
||||
[schema string-transformer-provider options]
|
||||
(let [;; Always allow extra paramaters on query-parameters encoding
|
||||
open-schema (mu/open-schema schema)
|
||||
;; Do not remove extra keys
|
||||
string-transformer (if (satisfies? TransformationProvider string-transformer-provider)
|
||||
(-transformer string-transformer-provider (assoc options :strip-extra-keys false))
|
||||
string-transformer-provider)
|
||||
encoder (m/encoder open-schema options string-transformer)]
|
||||
(fn [value format]
|
||||
(if encoder
|
||||
(encoder value)
|
||||
value))))
|
||||
|
||||
;;
|
||||
;; public api
|
||||
;;
|
||||
|
|
@ -97,9 +113,11 @@
|
|||
:validate true
|
||||
;; top-level short-circuit to disable request & response coercion
|
||||
:enabled true
|
||||
;; strip-extra-keys (effects only predefined transformers)
|
||||
;; strip-extra-keys (affects only predefined transformers)
|
||||
:strip-extra-keys true
|
||||
;; add/set default values
|
||||
;; 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
|
||||
|
|
@ -112,6 +130,9 @@
|
|||
([opts]
|
||||
(let [{:keys [transformers lite compile options error-keys encode-error] :as opts} (merge default-options opts)
|
||||
show? (fn [key] (contains? error-keys key))
|
||||
;; Query-string-coercer needs to construct transfomer without strip-extra-keys so it will
|
||||
;; use the transformer-provider directly.
|
||||
string-transformer-provider (:default (:string transformers))
|
||||
transformers (walk/prewalk #(if (satisfies? TransformationProvider %) (-transformer % opts) %) transformers)
|
||||
compile (if lite (fn [schema options]
|
||||
(compile (binding [l/*options* options] (l/schema schema)) options))
|
||||
|
|
@ -122,7 +143,15 @@
|
|||
(-get-options [_] opts)
|
||||
(-get-model-apidocs [this specification model options]
|
||||
(case specification
|
||||
:openapi (json-schema/transform model (merge opts options))
|
||||
:openapi (if (= :parameter (:type options))
|
||||
;; For :parameters we need to output an object schema with actual :properties.
|
||||
;; The caller will iterate through the properties and add them individually to the openapi doc.
|
||||
;; Thus, we deref to get the actual [:map ..] instead of some ref-schema.
|
||||
(let [should-be-map (m/deref model)]
|
||||
(when-not (= :map (m/type should-be-map))
|
||||
(println "WARNING: Unsupported schema for OpenAPI (expected :map schema)" (select-keys options [:in :parameter]) should-be-map))
|
||||
(json-schema/transform should-be-map (merge opts options)))
|
||||
(json-schema/transform model (merge opts options)))
|
||||
(throw
|
||||
(ex-info
|
||||
(str "Can't produce Malli apidocs for " specification)
|
||||
|
|
@ -159,7 +188,8 @@
|
|||
(-open-model [_ schema] schema)
|
||||
(-encode-error [_ error]
|
||||
(cond-> error
|
||||
(show? :humanized) (assoc :humanized (me/humanize error {:wrap :message}))
|
||||
(show? :humanized) (assoc :humanized (me/humanize error (cond-> {:wrap :message}
|
||||
options (merge options))))
|
||||
(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))))
|
||||
|
|
@ -168,6 +198,8 @@
|
|||
(-request-coercer [_ type schema]
|
||||
(-coercer schema type transformers :decode opts))
|
||||
(-response-coercer [_ schema]
|
||||
(-coercer schema :response transformers :encode opts))))))
|
||||
(-coercer schema :response transformers :encode opts))
|
||||
(-query-string-coercer [_ schema]
|
||||
(-query-string-coercer schema string-transformer-provider opts))))))
|
||||
|
||||
(def coercion (create default-options))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-middleware "0.7.0-alpha7"
|
||||
(defproject metosin/reitit-middleware "0.9.2"
|
||||
:description "Reitit, common middleware bundled"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@
|
|||
(recur (.getSuperclass sk) (conj ks sk))
|
||||
ks)))
|
||||
|
||||
(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)
|
||||
|
|
@ -26,7 +29,7 @@
|
|||
(get handlers ex-class)
|
||||
(some
|
||||
(partial get handlers)
|
||||
(descendants type))
|
||||
(descendants-safe type))
|
||||
(some
|
||||
(partial get handlers)
|
||||
(super-classes ex-class))
|
||||
|
|
@ -66,6 +69,7 @@
|
|||
"Default safe handler for any exception."
|
||||
[^Exception e _]
|
||||
{:status 500
|
||||
:headers {}
|
||||
:body {:type "exception"
|
||||
:class (.getName (.getClass e))}})
|
||||
|
||||
|
|
@ -74,6 +78,7 @@
|
|||
[status]
|
||||
(fn [e _]
|
||||
{:status status
|
||||
:headers {}
|
||||
:body (coercion/encode-error (ex-data e))}))
|
||||
|
||||
(defn http-response-handler
|
||||
|
|
|
|||
|
|
@ -58,7 +58,10 @@
|
|||
"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."
|
||||
parameters into `[:parameters :multipart]` under request.
|
||||
|
||||
Note! You want to have multipart-middleware after coerce-request-middleware,
|
||||
because coerce-request-middleware overwrites `:parameters`."
|
||||
([]
|
||||
(create-multipart-middleware nil))
|
||||
([options]
|
||||
|
|
@ -69,5 +72,8 @@
|
|||
"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."
|
||||
parameters into `[:parameters :multipart]` under request.
|
||||
|
||||
Note! You want to have multipart-middleware after coerce-request-middleware,
|
||||
because coerce-request-middleware overwrites `:parameters`."
|
||||
(create-multipart-middleware))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject fi.metosin/reitit-openapi "0.7.0-alpha7"
|
||||
(defproject fi.metosin/reitit-openapi "0.9.2"
|
||||
:description "Reitit: OpenAPI-support"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
@ -9,4 +9,5 @@
|
|||
:plugins [[lein-parent "0.3.9"]]
|
||||
:parent-project {:path "../../project.clj"
|
||||
:inherit [:deploy-repositories :managed-dependencies]}
|
||||
:dependencies [[metosin/reitit-core]])
|
||||
:dependencies [[metosin/reitit-core]
|
||||
[metosin/muuntaja]])
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
[clojure.spec.alpha :as s]
|
||||
[clojure.string :as str]
|
||||
[meta-merge.core :refer [meta-merge]]
|
||||
[muuntaja.core :as m]
|
||||
[reitit.coercion :as coercion]
|
||||
[reitit.core :as r]
|
||||
[reitit.trie :as trie]))
|
||||
|
|
@ -75,18 +76,30 @@
|
|||
(defn- openapi-path [path opts]
|
||||
(-> path (trie/normalize opts) (str/replace #"\{\*" "{")))
|
||||
|
||||
(def ^:private form-content-type "application/x-www-form-urlencoded")
|
||||
|
||||
(defn -get-apidocs-openapi
|
||||
[coercion {:keys [request parameters responses openapi/request-content-types openapi/response-content-types]
|
||||
:or {request-content-types ["application/json"]
|
||||
response-content-types ["application/json"]}}]
|
||||
(let [{:keys [body multipart]} parameters
|
||||
parameters (dissoc parameters :request :body :multipart)
|
||||
[coercion {:keys [request muuntaja parameters responses openapi/request-content-types openapi/response-content-types]} definitions]
|
||||
(let [{:keys [body form multipart]} parameters
|
||||
parameters (dissoc parameters :request :body :form :multipart)
|
||||
->content (fn [data schema]
|
||||
(merge
|
||||
{:schema schema}
|
||||
(select-keys data [:description :examples])
|
||||
(select-keys data [:example :examples])
|
||||
(:openapi data)))
|
||||
->schema-object #(coercion/-get-model-apidocs coercion :openapi %1 %2)]
|
||||
->schema-object (fn [model opts]
|
||||
(let [result (coercion/-get-model-apidocs
|
||||
coercion :openapi model
|
||||
(assoc opts :malli.json-schema/definitions-path "#/components/schemas/"))]
|
||||
(when-let [d (:definitions result)]
|
||||
(vswap! definitions merge d))
|
||||
(dissoc result :definitions)))
|
||||
request-content-types (or request-content-types
|
||||
(when muuntaja (m/decodes muuntaja))
|
||||
["application/json"])
|
||||
response-content-types (or response-content-types
|
||||
(when muuntaja (m/encodes muuntaja))
|
||||
["application/json"])]
|
||||
(merge
|
||||
(when (seq parameters)
|
||||
{:parameters
|
||||
|
|
@ -97,11 +110,11 @@
|
|||
(merge {:in (name in)
|
||||
:name k
|
||||
:required (required? k)
|
||||
:schema schema}
|
||||
(select-keys schema [:description])))
|
||||
:schema (dissoc schema :description :deprecated)}
|
||||
(select-keys schema [:description :deprecated])))
|
||||
(into []))})
|
||||
(when body
|
||||
;; body uses a single schema to describe every :requestBody
|
||||
;; :body uses a single schema to describe every :requestBody
|
||||
;; the schema-object transformer should be able to transform into distinct content-types
|
||||
{:requestBody {:content (into {}
|
||||
(map (fn [content-type]
|
||||
|
|
@ -111,26 +124,34 @@
|
|||
[content-type {:schema schema}])))
|
||||
request-content-types)}})
|
||||
|
||||
(when form
|
||||
;; :form is similar to any other body, but the content type must be application/x-www-form-urlencoded
|
||||
{:requestBody {:content {form-content-type {:schema (->schema-object form
|
||||
{:in :requestBody
|
||||
:type :schema
|
||||
:content-type form-content-type})}}}})
|
||||
|
||||
(when request
|
||||
;; request allow to different :requestBody per content-type
|
||||
;; :request allows different :requestBody per content-type
|
||||
{:requestBody
|
||||
{:content (merge
|
||||
(select-keys request [:description])
|
||||
(when-let [{:keys [schema] :as data} (coercion/get-default request)]
|
||||
(into {}
|
||||
(map (fn [content-type]
|
||||
(let [schema (->schema-object schema {:in :requestBody
|
||||
:type :schema
|
||||
:content-type content-type})]
|
||||
[content-type (->content data schema)])))
|
||||
request-content-types))
|
||||
(into {}
|
||||
(map (fn [[content-type {:keys [schema] :as data}]]
|
||||
(let [schema (->schema-object schema {:in :requestBody
|
||||
:type :schema
|
||||
:content-type content-type})]
|
||||
[content-type (->content data schema)])))
|
||||
(:content request)))}})
|
||||
(merge
|
||||
(select-keys request [:description])
|
||||
{:content (merge
|
||||
(when-let [{:keys [schema] :as data} (coercion/get-default request)]
|
||||
(into {}
|
||||
(map (fn [content-type]
|
||||
(let [schema (->schema-object schema {:in :requestBody
|
||||
:type :schema
|
||||
:content-type content-type})]
|
||||
[content-type (->content data schema)])))
|
||||
request-content-types))
|
||||
(into {}
|
||||
(map (fn [[content-type {:keys [schema] :as data}]]
|
||||
(let [schema (->schema-object schema {:in :requestBody
|
||||
:type :schema
|
||||
:content-type content-type})]
|
||||
[content-type (->content data schema)])))
|
||||
(dissoc (:content request) :default)))})})
|
||||
(when multipart
|
||||
{:requestBody
|
||||
{:content
|
||||
|
|
@ -184,27 +205,32 @@
|
|||
:x-id ids}))
|
||||
accept-route (fn [route]
|
||||
(-> route second :openapi :id (or ::default) (trie/into-set) (set/intersection ids) seq))
|
||||
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))
|
||||
(select-keys data [:tags :summary :description])
|
||||
(strip-top-level-keys openapi))]))
|
||||
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-path (fn [[p _ c]]
|
||||
(if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))]
|
||||
(if-let [endpoint (some->> c (keep (partial transform-endpoint p)) (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)]
|
||||
{:status 200
|
||||
:body (meta-merge openapi {:paths paths})}))
|
||||
:body (cond-> (meta-merge openapi {:paths paths})
|
||||
(seq @definitions) (assoc-in [:components :schemas] @definitions))}))
|
||||
([req res raise]
|
||||
(try
|
||||
(res (create-openapi req))
|
||||
(catch #?(:clj Exception :cljs :default) e
|
||||
(catch Exception e
|
||||
(raise e))))))
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-pedestal "0.7.0-alpha7"
|
||||
(defproject metosin/reitit-pedestal "0.9.2"
|
||||
:description "Reitit + Pedestal Integration"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue