Compare commits

...

838 commits

Author SHA1 Message Date
Joel Kaasinen
7c1544c3ce
doc: document match-by-name :url-encode? option
Some checks failed
testsuite / Clojure 11 (Java 11) (push) Has been cancelled
testsuite / Clojure 11 (Java 17) (push) Has been cancelled
testsuite / Clojure 11 (Java 21) (push) Has been cancelled
testsuite / Clojure 11 (Java 25) (push) Has been cancelled
testsuite / Clojure 12 (Java 11) (push) Has been cancelled
testsuite / Clojure 12 (Java 17) (push) Has been cancelled
testsuite / Clojure 12 (Java 21) (push) Has been cancelled
testsuite / Clojure 12 (Java 25) (push) Has been cancelled
testsuite / ClojureScript (push) Has been cancelled
testsuite / Lint cljdoc.edn (push) Has been cancelled
testsuite / Check cljdoc analysis (push) Has been cancelled
for #778 #519
2026-02-06 13:25:33 +02:00
Joel Kaasinen
0724c0c5a0
doc: update CHANGELOG.md 2026-02-06 13:20:51 +02:00
Joel Kaasinen
7051c99e99
Merge pull request #778 from lucacervello/master
feat: add url-encode? options to path-params
2026-02-06 13:13:49 +02:00
Luca Cervello
4c8a69c616 refactor: remove repetitions 2026-02-06 12:01:44 +01:00
Joel Kaasinen
e3306e1876
Merge pull request #774 from metosin/feat/753-doc-parameter-coercion
Some checks failed
testsuite / Check cljdoc analysis (push) Has been cancelled
testsuite / Clojure 11 (Java 11) (push) Has been cancelled
testsuite / Clojure 11 (Java 17) (push) Has been cancelled
testsuite / Clojure 11 (Java 21) (push) Has been cancelled
testsuite / Clojure 11 (Java 25) (push) Has been cancelled
testsuite / Clojure 12 (Java 11) (push) Has been cancelled
testsuite / Clojure 12 (Java 17) (push) Has been cancelled
testsuite / Clojure 12 (Java 21) (push) Has been cancelled
testsuite / Clojure 12 (Java 25) (push) Has been cancelled
testsuite / ClojureScript (push) Has been cancelled
testsuite / Lint cljdoc.edn (push) Has been cancelled
doc: document nuances of reitit.coercion/default-parameter-coercion
2026-01-23 14:38:39 +02:00
Joel Kaasinen
e6e1bfd5c4
doc: clarify malli body coercion doc 2026-01-23 14:25:19 +02:00
Joel Kaasinen
8391fafbe2
Merge pull request #776 from metosin/feat/337-fix-external-redirect
fix: redirect-trailing-slash-handler won't make external redirects
2026-01-23 14:16:11 +02:00
Luca Cervello
97bfafa907 feat: add url-encode? options to path-params 2026-01-23 11:20:38 +01:00
Joel Kaasinen
71a777b4fa
fix: redirect-trailing-slash-handler won't make external redirects
A redirect header like

Location: //malicious.com/foo

Would result in a redirect to https://malicious.com/foo . We never
want Locations like that out of redirect-trailing-slash-handler.

Fixes #337
2026-01-23 09:54:26 +02:00
Joel Kaasinen
a6b68cc3d6
doc: document nuances of reitit.coercion/default-parameter-coercion
for #753
2026-01-23 09:00:47 +02:00
Joel Kaasinen
248200aad3
Merge pull request #770 from lucacervello/master
Some checks failed
testsuite / Clojure 11 (Java 11) (push) Has been cancelled
testsuite / Clojure 11 (Java 17) (push) Has been cancelled
testsuite / Clojure 11 (Java 21) (push) Has been cancelled
testsuite / Clojure 11 (Java 25) (push) Has been cancelled
testsuite / Clojure 12 (Java 11) (push) Has been cancelled
testsuite / Clojure 12 (Java 17) (push) Has been cancelled
testsuite / Clojure 12 (Java 21) (push) Has been cancelled
testsuite / Clojure 12 (Java 25) (push) Has been cancelled
testsuite / ClojureScript (push) Has been cancelled
testsuite / Lint cljdoc.edn (push) Has been cancelled
testsuite / Check cljdoc analysis (push) Has been cancelled
Allow colons in bracket parameter syntax
2026-01-09 13:00:33 +02:00
Joel Kaasinen
bf18586d75
examples/openapi: fix /complex example 2026-01-09 11:28:04 +02:00
Joel Kaasinen
373ea9bb62
Release 0.10.0
Some checks are pending
testsuite / Clojure 11 (Java 11) (push) Waiting to run
testsuite / Clojure 11 (Java 17) (push) Waiting to run
testsuite / Clojure 11 (Java 21) (push) Waiting to run
testsuite / Clojure 11 (Java 25) (push) Waiting to run
testsuite / Clojure 12 (Java 11) (push) Waiting to run
testsuite / Clojure 12 (Java 17) (push) Waiting to run
testsuite / Clojure 12 (Java 21) (push) Waiting to run
testsuite / Clojure 12 (Java 25) (push) Waiting to run
testsuite / ClojureScript (push) Waiting to run
testsuite / Lint cljdoc.edn (push) Waiting to run
testsuite / Check cljdoc analysis (push) Waiting to run
2026-01-09 10:07:15 +02:00
Joel Kaasinen
334a42e03d
Merge pull request #773 from metosin/bump-deps
chore: bump deps
2026-01-09 10:06:04 +02:00
Joel Kaasinen
5ac4e65284
doc: mention Java 25 2026-01-09 10:01:28 +02:00
Joel Kaasinen
dbac18546b
chore: don't run CI twice on pull requests
both the push and pull_request triggers matched
2026-01-09 09:58:35 +02:00
Joel Kaasinen
69b23c49b9
chore: upgrade clojure; add clj11, clj12 and java 25 to ci matrix 2026-01-09 09:55:05 +02:00
Joel Kaasinen
e1d5789f40
chore: bump deps 2026-01-09 09:40:53 +02:00
Joel Kaasinen
d27454efdc
doc: update CHANGELOG.md 2026-01-09 09:36:54 +02:00
Joel Kaasinen
1d4473e1f4
Merge pull request #772 from metosin/fix/768-ancestors
fix create-exception-middleware for hierarchical keywords
2026-01-09 09:35:14 +02:00
Joel Kaasinen
75faf709e2
fix: create-exception-middleware for deep hierarchies
The code was not finding the closest ancestor to the error type,
because `ancestors` is not ordered. Now the code does a DFS to find a
nearest ancestor. If the nearest ancestor is non-unique, an arbitrary
one is picked.
2026-01-09 09:26:21 +02:00
Joel Kaasinen
2c87d90bda
fix: create-exception-middleware for hierarchical keywords
Previously, the code was searching among the descendants, not the
ancestors, of the error type for an error handler. The test also got
this wrong, perhaps due to a mistake in the parameter order of derive.
2026-01-09 08:37:49 +02:00
Joel Kaasinen
8907dfc5f5
Merge pull request #771 from mthl/lint
Fix linting issues and run Clj-kondo in CI
2026-01-09 08:12:44 +02:00
Mathieu Lirzin
63429a2d1e
chore: Run Linter on CI 2026-01-01 18:40:21 +01:00
Mathieu Lirzin
8721c7ae37
refactor: Implement all Executor protocol method 2026-01-01 18:40:21 +01:00
Mathieu Lirzin
e3180e4d6a
refactor: Reify protocol instead of interface 2026-01-01 18:40:21 +01:00
Mathieu Lirzin
2597d14125
refactor: Ignore :missing-protocol-method linter 2026-01-01 18:40:21 +01:00
Mathieu Lirzin
54a040f136
test: Use atom in reloading-ring-handler-test
This removes usage of inline defs.
2026-01-01 18:40:21 +01:00
Mathieu Lirzin
e4c53a64e2
test: Add missing protocol method implementation 2026-01-01 18:40:21 +01:00
Mathieu Lirzin
218f05972e
test: Remove unused def 2026-01-01 18:40:21 +01:00
Mathieu Lirzin
e342ac5401
test: Comment unused values 2026-01-01 18:40:21 +01:00
Mathieu Lirzin
e6137cb47a
refactor: Remove redundant let 2026-01-01 18:40:21 +01:00
Mathieu Lirzin
3191d9ee59
refactor: Remove unneeded and 2026-01-01 18:40:21 +01:00
Mathieu Lirzin
579eb28a50
refactor: Remove redundant str calls 2026-01-01 18:40:21 +01:00
Mathieu Lirzin
20735730c9
chore: Lint defspec as deftest 2026-01-01 18:40:20 +01:00
Mathieu Lirzin
aa6c1ac460
test: Use .clj extension instead of .cljc
Those tests are only working on Clojure.
2026-01-01 18:40:20 +01:00
Mathieu Lirzin
c113bded4e
refactor: Remove unused required namespaces 2026-01-01 18:40:17 +01:00
Luca Cervello
ed6397cd05 feat: allow colons in bracket parameter syntax
closes #748
2025-12-30 10:05:20 +01:00
Joel Kaasinen
c3a152a44e
doc: update CHANGELOG.md
Some checks failed
testsuite / Clojure (Java 11) (push) Has been cancelled
testsuite / Clojure (Java 17) (push) Has been cancelled
testsuite / Clojure (Java 21) (push) Has been cancelled
testsuite / ClojureScript (push) Has been cancelled
testsuite / Lint cljdoc.edn (push) Has been cancelled
testsuite / Check cljdoc analysis (push) Has been cancelled
2025-11-17 11:01:37 +02:00
Joel Kaasinen
c0bc789863
Merge pull request #767 from metosin/humanize-opts
feat: support humanize options
2025-11-17 10:59:23 +02:00
Joel Kaasinen
78aba57d2d
doc: document configuring malli registry
Some checks failed
testsuite / Clojure (Java 11) (push) Has been cancelled
testsuite / Clojure (Java 17) (push) Has been cancelled
testsuite / Clojure (Java 21) (push) Has been cancelled
testsuite / ClojureScript (push) Has been cancelled
testsuite / Lint cljdoc.edn (push) Has been cancelled
testsuite / Check cljdoc analysis (push) Has been cancelled
2025-11-14 14:58:52 +02:00
Joel Kaasinen
451b286f1d
doc: add brief docs for configuring humanized error messages 2025-11-14 14:16:48 +02:00
Joel Kaasinen
eb06404f1e
feat: fold malli :humanize-opts into :options 2025-11-14 14:06:43 +02:00
Joel Kaasinen
af7313bd9b
test: add test for overriding malli registry 2025-11-14 14:05:36 +02:00
Joel Kaasinen
ea58100fec
test: add test for malli coercion :humanize-opts 2025-11-14 13:51:23 +02:00
ertugrulcetin
a4576cc622
feat: support humanize options 2025-11-14 13:15:50 +02:00
Joel Kaasinen
9d88d92241
Merge pull request #766 from metosin/spec-and-or
Some checks failed
testsuite / Clojure (Java 11) (push) Has been cancelled
testsuite / Clojure (Java 17) (push) Has been cancelled
testsuite / Clojure (Java 21) (push) Has been cancelled
testsuite / ClojureScript (push) Has been cancelled
testsuite / Lint cljdoc.edn (push) Has been cancelled
testsuite / Check cljdoc analysis (push) Has been cancelled
Bump spec-tools, test openapi + s/keys + or
2025-11-14 11:49:34 +02:00
Joel Kaasinen
d16aac673e
test: test openapi + s/keys + or 2025-11-14 11:30:51 +02:00
Joel Kaasinen
dede2db213
chore: bump spec-tools 2025-11-14 11:22:03 +02:00
Joel Kaasinen
1dc961f661
Merge pull request #764 from metosin/483-doc-allow-symlinks
Some checks failed
testsuite / Clojure (Java 11) (push) Has been cancelled
testsuite / Clojure (Java 17) (push) Has been cancelled
testsuite / Clojure (Java 21) (push) Has been cancelled
testsuite / ClojureScript (push) Has been cancelled
testsuite / Lint cljdoc.edn (push) Has been cancelled
testsuite / Check cljdoc analysis (push) Has been cancelled
doc: document allow-symlinks? option
2025-10-31 13:41:31 +02:00
Joel Kaasinen
2ce9850de6
doc: document allow-symlinks? option
... for create-resource-handler and create-file-handler

fixes #483
2025-10-31 11:43:25 +02:00
Joel Kaasinen
0bc30e9361
doc: update CHANGELOG.md 2025-10-31 10:45:36 +02:00
Joel Kaasinen
9b26d5c0fd
Merge pull request #763 from metosin/remove-dead-code
refactor: remove unused reitit.dependency ns
2025-10-31 10:44:38 +02:00
Joel Kaasinen
e671f78741
Merge pull request #762 from metosin/745-coerce-response-content-type
Some checks are pending
testsuite / Clojure (Java 11) (push) Waiting to run
testsuite / Clojure (Java 17) (push) Waiting to run
testsuite / Clojure (Java 21) (push) Waiting to run
testsuite / ClojureScript (push) Waiting to run
testsuite / Lint cljdoc.edn (push) Waiting to run
testsuite / Check cljdoc analysis (push) Waiting to run
improve & document response coercion content-type selection
2025-10-31 09:48:00 +02:00
Joel Kaasinen
55f8d98bde
test: improve per-content-type coercion tests
- The :headers "Content-Type" case in per-content-type-test was
  unrealistic. Ring would've thrown an exception at the non-string
  :body.
- Test response Content-Type in muuntaja-per-content-type-coercion-test
2025-10-31 09:38:56 +02:00
Joel Kaasinen
342bae3ffe
refactor: remove unused reitit.dependency ns
leftover from #33 #210
2025-10-30 15:32:27 +02:00
Joel Kaasinen
ae52000b29
doc: update CHANGELOG.md 2025-10-29 10:57:59 +02:00
Joel Kaasinen
39c5ae86a4
doc: return random content-type from openapi example /pizza 2025-10-29 10:54:16 +02:00
Joel Kaasinen
7fb9c27e46
feat: use request Content-Type or :muuntaja/content-type to coerce
Previously, `extract-response-format-default` was only looking at
(-> request :muuntaja/response :format). This led to wrong behaviour
when there were separate schemas for separate response content types
and an explicitly picked content-type for the response.
2025-10-29 10:54:10 +02:00
Joel Kaasinen
f4da07c222
Release 0.9.2
Some checks failed
testsuite / Clojure (Java 11) (push) Has been cancelled
testsuite / Clojure (Java 17) (push) Has been cancelled
testsuite / Clojure (Java 21) (push) Has been cancelled
testsuite / ClojureScript (push) Has been cancelled
testsuite / Lint cljdoc.edn (push) Has been cancelled
testsuite / Check cljdoc analysis (push) Has been cancelled
2025-10-28 14:57:54 +02:00
Joel Kaasinen
d25dca19f6 chore: disable release signing harder
Some checks failed
testsuite / Clojure (Java 11) (push) Has been cancelled
testsuite / Clojure (Java 17) (push) Has been cancelled
testsuite / Clojure (Java 21) (push) Has been cancelled
testsuite / ClojureScript (push) Has been cancelled
testsuite / Lint cljdoc.edn (push) Has been cancelled
testsuite / Check cljdoc analysis (push) Has been cancelled
2025-10-24 16:14:11 +03:00
Joel Kaasinen
1b02662c78 chore: disable release signing
now that we have automated releases, there's no point
2025-10-24 16:08:19 +03:00
Joel Kaasinen
36af88b65e doc: update release instructions 2025-10-24 16:08:08 +03:00
Joel Kaasinen
152c598858 Release 0.9.2-rc1 2025-10-24 15:52:47 +03:00
Joel Kaasinen
6d9632e85e chore: automated release pipeline 2025-10-24 15:45:42 +03:00
Joel Kaasinen
5ff8ba2e3e
Merge pull request #761 from metosin/bump-deps
bump deps
2025-10-24 15:45:27 +03:00
Joel Kaasinen
c684c83c99 chore: update openapi-schema-validator, fix test
validator now disallows having both "example" and "examples"
2025-10-24 15:38:29 +03:00
Joel Kaasinen
defebb0f1f chore: upgrade dependencies
... and pin pedestal versions since we don't work with 0.7/0.8
2025-10-24 15:31:39 +03:00
Joel Kaasinen
54c0935078 doc: updating deps 2025-10-24 15:26:20 +03:00
Joel Kaasinen
cde050f964 doc: remove old TODO 2025-10-24 14:24:38 +03:00
Joel Kaasinen
560c6d7969 doc: update CHANGELOG.md 2025-10-24 14:16:59 +03:00
Joel Kaasinen
abe95bfc17
Merge pull request #760 from metosin/fix/758-match-by-name-bang-nil
fix: match-by-name! should throw when match-by-name is PartialMatch
2025-10-24 14:15:37 +03:00
Joel Kaasinen
d2f44b8015 fix: match-by-name! should throw when match-by-name is PartialMatch
If a path param was nil, match-by-name (via impl/path-for) was
treating the parameter as missing, but match-by-name!
(via impl/throw-on-missing-path-params) was treating it as present.

That is:

(reitit/match-by-name router :page {:id "1"}) ;; => Match
(reitit/match-by-name router :page) ;; => PartialMatch
(reitit/match-by-name router :page {:id nil}) ;; => PartialMatch

(reitit/match-by-name! router :page {:id "1"}) ;; => Match
(reitit/match-by-name! router :page) ;; => ExceptionInfo: missing path-params for route /pages/:id -> #{:id}
(reitit/match-by-name! router :page {:id nil}) ;; => nil  !!!

fixes #758
2025-10-24 09:58:03 +03:00
Joel Kaasinen
ef9dd495be doc: update CHANGELOG.md
Some checks failed
testsuite / Clojure (Java 11) (push) Has been cancelled
testsuite / Clojure (Java 17) (push) Has been cancelled
testsuite / Clojure (Java 21) (push) Has been cancelled
testsuite / ClojureScript (push) Has been cancelled
testsuite / Lint cljdoc.edn (push) Has been cancelled
testsuite / Check cljdoc analysis (push) Has been cancelled
2025-10-13 15:41:31 +03:00
Joel Kaasinen
9509e40dae
Merge pull request #756 from metosin/feat/defaults-for-optional-keys
setting default values for optional keys in malli coercion
2025-10-13 15:38:45 +03:00
Joel Kaasinen
67918a3f9c feat: reuse :default-values config key instead of adding a new one 2025-10-13 15:18:29 +03:00
Joel Kaasinen
45951aa82e doc: update CHANGELOG.md
Some checks are pending
testsuite / Clojure (Java 11) (push) Waiting to run
testsuite / Clojure (Java 17) (push) Waiting to run
testsuite / Clojure (Java 21) (push) Waiting to run
testsuite / ClojureScript (push) Waiting to run
testsuite / Lint cljdoc.edn (push) Waiting to run
testsuite / Check cljdoc analysis (push) Waiting to run
2025-10-13 09:17:33 +03:00
Joel Kaasinen
1cdca2e3d5
Merge pull request #739 from Ramblurr/fix/top-level-mw-registry
Apply router options to top-level middleware chain
2025-10-13 09:14:36 +03:00
Joel Kaasinen
2f22838820 doc: using middleware from registry at ring-handler level 2025-10-13 09:09:11 +03:00
Joel Kaasinen
d809291553 test: ring-handler middleware from registry inside router 2025-10-13 09:01:21 +03:00
Joel Kaasinen
4e572e86d6 Merge remote-tracking branch 'origin/master' into fix/top-level-mw-registry 2025-10-13 08:50:38 +03:00
Joel Kaasinen
10700e0ca2
Merge pull request #757 from metosin/doc/multipart-middleware-order
Some checks failed
testsuite / Clojure (Java 11) (push) Has been cancelled
testsuite / Clojure (Java 17) (push) Has been cancelled
testsuite / Clojure (Java 21) (push) Has been cancelled
testsuite / ClojureScript (push) Has been cancelled
testsuite / Lint cljdoc.edn (push) Has been cancelled
testsuite / Check cljdoc analysis (push) Has been cancelled
doc: multipart-middleware should be after coerce-request-middleware
2025-10-10 13:04:57 +03:00
Joel Kaasinen
3e0f1d3188 doc: multipart-middleware should be after coerce-request-middleware 2025-10-10 12:50:24 +03:00
Joel Kaasinen
f26dc1ab19 feat: :default-values-for-optional-keys for malli coercion 2025-10-10 08:51:05 +03:00
Joel Kaasinen
01766ee211 doc: update CHANGELOG.md
Some checks are pending
testsuite / Clojure (Java 11) (push) Waiting to run
testsuite / Clojure (Java 17) (push) Waiting to run
testsuite / Clojure (Java 21) (push) Waiting to run
testsuite / ClojureScript (push) Waiting to run
testsuite / Lint cljdoc.edn (push) Waiting to run
testsuite / Check cljdoc analysis (push) Waiting to run
2025-10-09 15:30:55 +03:00
Joel Kaasinen
79627aea7b
Merge pull request #755 from metosin/feat/multimethod-as-handler
feat: allow multimethods as :handlers in validation
2025-10-09 15:26:37 +03:00
Joel Kaasinen
4d284385ec
Merge pull request #754 from metosin/openapi-error-reporting
feat: better error reporting while building openapi docs
2025-10-09 15:26:27 +03:00
Joel Kaasinen
31fa0376cf feat: better error reporting while building openapi docs
Include the path and method when openapi generation goes wrong.
2025-10-09 15:19:56 +03:00
Joel Kaasinen
05bc331397 feat: allow multimethods as :handlers in validation
fixes #749
2025-10-07 15:50:51 +03:00
Juho Teperi
7520d20f12 No need to use specific Java version for releases 2025-05-27 14:35:50 +03:00
Juho Teperi
210f20e714 Release 0.9.1 2025-05-27 14:04:31 +03:00
Juho Teperi
aad054ef39
Merge pull request #743 from metosin/fix-responses
fix: response coercion for unlisted http statuses, when no default
2025-05-27 14:03:20 +03:00
Joel Kaasinen
653db7efa3 doc: update CHANGELOG.md 2025-05-27 13:08:59 +03:00
Joel Kaasinen
5025ca3a75 fix: response coercion for unlisted http statuses, when no default
fixes #742
2025-05-27 12:09:38 +03:00
Joel Kaasinen
55c30af979
Merge pull request #741 from metosin/readme-example
doc: improve middleware docs & examples in README.md & elsewhere
2025-05-26 12:56:11 +03:00
Joel Kaasinen
9ee8e364f3 doc: add a reason to use top-level mw 2025-05-26 10:27:00 +03:00
Joel Kaasinen
ad9cd31168 doc: mention one more reason to use top-level route data for mw 2025-05-26 10:21:40 +03:00
Joel Kaasinen
99267d2943 doc: mention that coercion uses top-level-route-data-middleware 2025-05-26 10:14:14 +03:00
Joel Kaasinen
ecf22d5c4a doc: elaborate on different ways of setting middleware 2025-05-26 10:06:36 +03:00
Joel Kaasinen
1803643f1f doc: link to ring.md#middleware from data_driven_middleware.md
Data Driven Middleware is the first doc title with "Middleware" in it,
so people will land in it while trying to figure out the basics of
middleware.
2025-05-26 08:52:05 +03:00
Joel Kaasinen
f247751409 doc: remove misleading link to open issue
the factoring hasn't happened, so no reason to talk about issue #134
in the docs until it does
2025-05-26 08:45:48 +03:00
Joel Kaasinen
48fdb692ef doc: improve ring example in README.md
- format-response-middleware wasn't doing anything, use
  format-middleware instead as that's what the users usually want
- add exception handling
- document middleware stack a bit
2025-05-26 08:44:53 +03:00
Juho Teperi
b9cef492f8 Release 0.9.0 2025-05-23 10:47:21 +03:00
Joel Kaasinen
524d0d4d57
Merge pull request #740 from metosin/openapi-multi-example
doc: add :multi, :enum etc to examples/openapi
2025-05-13 09:18:20 +03:00
Joel Kaasinen
20c28ecc7c doc: add :multi, :enum etc to examples/openapi 2025-05-13 08:23:02 +03:00
Joel Kaasinen
3793a7b23b
Merge pull request #732 from metosin/unslash
malli now uses . instead of ~1 in json-schema/swagger $refs
2025-05-12 14:59:03 +03:00
Joel Kaasinen
ef64fc50b8 doc: add note about malli update to CHANGELOG.md 2025-05-12 14:39:38 +03:00
Joel Kaasinen
15987d9952 test: bump openapi-schema-validator 2025-05-12 14:28:19 +03:00
Joel Kaasinen
2bf8aa98a7 test: malli now uses . instead of ~1 in json-schema/swagger $refs
update our assertions accordingly
2025-05-12 14:28:19 +03:00
Joel Kaasinen
832d9ebe95 deps: bump malli 2025-05-12 14:28:13 +03:00
Joel Kaasinen
4b7a596ece doc: update CHANGELOG.md 2025-05-05 11:28:36 +03:00
Casey Link
0454e8914f Apply router options to top-level middleware chain
Middleware supplied to the `ring-handler` function could not be resolved
from the middleware registry, because the router options (which contain
the registry) were not being propagated.

Fixes #738
2025-04-29 14:52:09 +02:00
Joel Kaasinen
7a77c9f86b
Merge pull request #735 from metosin/response-default
Resurrect :responses :default
2025-04-29 09:30:02 +03:00
Joel Kaasinen
a8b4bc0d2d feat: rework & document response coercer defaulting rules 2025-04-28 10:01:09 +03:00
Joel Kaasinen
9db58f21dc doc: update CHANGELOG.md 2025-04-25 15:15:11 +03:00
Joel Kaasinen
9797725ae8
Merge pull request #714 from mokshasoft/openapi3-parameter-deprecation
Place the openapi deprecation tag directly under parameters
2025-04-25 15:13:26 +03:00
Joel Kaasinen
3c6139950c doc: deprecated openapi parameters in docs & example 2025-04-25 15:07:52 +03:00
Joel Kaasinen
9534f6df8b feat: avoid duplicated :description for openapi parameters 2025-04-25 15:04:51 +03:00
Joel Kaasinen
a390180975 test: add description and deprecated to openapi-test 2025-04-25 15:03:40 +03:00
Joel Kaasinen
f038fe1941 test: corner cases in how :responses :default gets applied 2025-04-24 09:31:27 +03:00
Joel Kaasinen
8058cecae0 doc: document :responses :default 2025-04-11 11:45:12 +03:00
Joel Kaasinen
dd835e73a8 feat: allow :default response status code again
it is an old feature, but didn't have a test, so it was broken by #715

also add a test so we don't break it again
2025-04-11 10:30:52 +03:00
Joel Kaasinen
78cf477b88
Merge pull request #733 from evaogbe/static-mime-type
Add mime-types option to static handler
2025-04-08 08:24:22 +03:00
Joel Kaasinen
2d1d0cc5d2 doc: update CHANGELOG.md 2025-04-08 08:19:04 +03:00
Joel Kaasinen
75a5e816a9 reindent 2025-04-08 08:15:59 +03:00
Eva Ogbe
e16c95c5b3 Add mime-types option to static handler 2025-04-07 20:35:58 -04:00
Juho Teperi
91fa60324f Use same javac options for reiti-core project.clj 2025-03-28 16:13:47 +02:00
Juho Teperi
6dd9cb1b7c Prepare release 0.8.0 2025-03-28 16:06:12 +02:00
Juho Teperi
fa28489b63
Merge pull request #728 from metosin/update-deps
Update dependencies
2025-03-28 15:57:56 +02:00
Juho Teperi
9849ed5ebb Mention Java 11 requirement 2025-03-28 15:54:24 +02:00
Juho Teperi
a103411bf7 Update ring 2025-03-28 15:40:58 +02:00
Juho Teperi
a57662693c Drop Java 8 tests and support 2025-03-28 15:40:26 +02:00
Juho Teperi
44d54cc9f2
Merge pull request #730 from agorgl/index-redirect-default
Change default index-redirect? value to false
2025-03-28 15:27:14 +02:00
Loukas Agorgianitis
3342e77538
Change default index-redirect? value to false
Signed-off-by: Loukas Agorgianitis <loukas@agorgianitis.com>
2025-03-28 15:14:15 +02:00
Juho Teperi
8fb44467a0
Merge pull request #727 from agorgl/directory-index
Add option to disable index files served on paths that are not directories
2025-03-28 15:11:09 +02:00
Loukas Agorgianitis
c2feb5b983
Add option to disable index files served on paths that are not directories
Signed-off-by: Loukas Agorgianitis <loukas@agorgianitis.com>
2025-03-28 13:49:36 +02:00
Juho Teperi
9fb0b7233a Update dependencies 2025-03-27 13:58:30 +02:00
Joel Kaasinen
89c05932dc doc: update CHANGELOG.md 2025-03-26 11:10:21 +02:00
Joel Kaasinen
2f1defe3cd
Merge pull request #725 from agorgl/index-redirect
Add option to allow serving index files without redirect
2025-03-26 11:08:54 +02:00
Loukas Agorgianitis
f50feff63c
Add option to allow serving index files without redirect
Signed-off-by: Loukas Agorgianitis <loukas@agorgianitis.com>
2025-03-25 23:07:36 +02:00
Joel Kaasinen
34e6db0d3f
Merge pull request #726 from metosin/bump-gha
chore: bump actions/cache for cljs workflow
2025-03-24 08:46:15 +02:00
Joel Kaasinen
04cb70f1f7 chore: bump actions/cache for cljs workflow 2025-03-24 08:40:12 +02:00
Joel Kaasinen
58195eed68 doc: update CHANGELOG.md 2025-02-25 12:52:33 +02:00
Joel Kaasinen
d5d46d5b0b
Merge pull request #715 from filipesilva/coerce-response-int
fix: throw if response status is not int
2025-02-25 12:49:23 +02:00
Filipe Silva
f0fc440425 fix: throw if response status is not int
Fix #667
2025-02-25 10:12:47 +00:00
Juho Teperi
30fd739fa9 Update CHANGELOG 2025-02-04 11:23:47 +02:00
Joel Kaasinen
de0e1f4462
Merge pull request #718 from metosin/doc-param-merge
doc: document nested parameter definitions
2025-02-03 14:55:38 +02:00
Juho Teperi
34b6bb9349 Link to PR from changelog 2025-01-31 14:42:33 +02:00
Joel Kaasinen
3fcd6cfd73 doc: document nested parameter definitions
originally implemented in #626 for #422
2025-01-31 14:31:54 +02:00
Juho Teperi
e5483cb1fc Release 0.8.0-alpha1 2025-01-31 14:06:05 +02:00
Juho Teperi
481c653139
Merge pull request #716 from metosin/query-string-encoding
Use coercion to encode query-string values in match->path
2025-01-31 14:04:31 +02:00
Juho Teperi
5ca22193d0 Use defined :string :default transformer for query-string-coercer 2025-01-31 09:39:19 +02:00
Juho Teperi
4eb29d3ed9 Extend frontend docs 2025-01-28 15:46:37 +02:00
Juho Teperi
dfc5a4ef67 Remove todo comments 2025-01-28 15:12:46 +02:00
Juho Teperi
7e9116f77e Simplify Malli coercion for query-params to only encode 2025-01-28 15:09:31 +02:00
Juho Teperi
7ae118fbb5 Move query string coercion to coercion ns from core 2025-01-28 14:34:21 +02:00
Juho Teperi
ce6d9e26cd Update docstrings and changelog 2025-01-22 14:35:56 +02:00
Juho Teperi
7ae2bfafc2 Cleanup 2025-01-22 14:20:02 +02:00
Juho Teperi
1b37c87aa2 Test set-query without a match 2025-01-22 14:18:54 +02:00
Juho Teperi
f60a7ad902 Fixes 2025-01-22 14:05:45 +02:00
Juho Teperi
1ba77a7267 Apply query parameters encoding on rfe/set-query 2025-01-22 14:05:45 +02:00
Juho Teperi
21e5840f13 Ensure extra query-string params aren't removed by coercion 2025-01-22 14:05:45 +02:00
Juho Teperi
5f10465533 Another test case 2025-01-22 14:05:26 +02:00
Juho Teperi
dba8d159cc . 2025-01-22 14:05:26 +02:00
Juho Teperi
5829e1c656 Add reitit.frontend test case 2025-01-22 14:05:26 +02:00
Juho Teperi
1819fa5d75 Note 2025-01-22 14:05:26 +02:00
Juho Teperi
25dd0abcaf Use coercion to encode query-string values in match->path 2025-01-22 14:05:26 +02:00
Juho Teperi
a19b6034dd
Merge pull request #717 from metosin/shadow-cljs
Update CI tests and add shadow-cljs to run cljs tests
2025-01-22 14:05:10 +02:00
Juho Teperi
0256642f64 Add bb tasks for Cljs tests 2025-01-22 13:57:10 +02:00
Juho Teperi
b1d066246a Cleanup 2025-01-22 13:36:59 +02:00
Juho Teperi
b19dda8d5d Cleanup 2025-01-22 13:31:09 +02:00
Juho Teperi
0a7b50a730 Test fix, silence extra cljs warnings 2025-01-22 12:09:14 +02:00
Juho Teperi
bf82533028 Handle shadow-cljs infern warnings 2025-01-22 12:01:10 +02:00
Juho Teperi
0370750a3f Run some ring tests only on jvm 2025-01-22 11:56:35 +02:00
Juho Teperi
ea88b06206 Update CI tests and add shadow-cljs to run cljs tests 2025-01-22 11:54:38 +02:00
Jonas Claeson
f1e3ed88ea The deprecation tag should be directly under parameters 2025-01-17 14:45:17 +01:00
Tommi Reiman
ada41ec7dd
Merge pull request #712 from p-himik/p-himik/708-incorrect-ring-responses
Make exception middleware return proper Ring responses
2024-12-08 14:59:08 +02:00
Tommi Reiman
1abff4937c
Update exception_test.clj
ring.util.http-response should be enough
2024-12-08 14:54:14 +02:00
Eugene Pakhomov
cc1cd114e4 Make exception middleware return proper Ring responses 2024-12-07 23:47:05 +02:00
Joel Kaasinen
e86662561f
Merge pull request #711 from metosin/doc-named-schemas
document status of named definition support for openapi&swagger
2024-12-02 07:54:23 +02:00
Joel Kaasinen
059f93aee8 doc: fix typo, mention swagger ui handling of names schemas 2024-12-02 07:47:36 +02:00
Joel Kaasinen
fc132f3a92 doc: swagger reusable schema definitions supported only for malli 2024-11-28 09:56:02 +02:00
Joel Kaasinen
431242c926 doc: openapi named schemas only produced for malli 2024-11-28 09:51:31 +02:00
Joel Kaasinen
0f9414847a
Merge pull request #706 from metosin/openapi-query-warning-master
feat: add warning for unsupported openapi parameter schemas
2024-11-07 10:01:00 +02:00
Joel Kaasinen
86e04414ba
Merge pull request #703 from cloudpermit/handle_form_coercion_properly_in_openapi
Add OpenAPI :requestBody for :form request schema
2024-11-01 08:26:48 +02:00
Joel Kaasinen
c89b6bbe31 feat: add warning for unsupported openapi parameter schemas
for #705
2024-10-30 09:58:32 +02:00
Joel Kaasinen
f6b8054669
Merge pull request #704 from metosin/commercial-support
mention metosin commercial support in README.md
2024-10-10 16:27:27 +03:00
Joel Kaasinen
4f4d05fe65 doc: mention metosin commercial support in README.md 2024-10-10 10:31:26 +03:00
Markus Penttilä
702e7b8972 Add OpenAPI :requestBody for :form request schema
OpenAPI Specification 3 requires defining form parameters, i.e. classic
application/x-www-form-urlencoded type body as a :requestBody. They are
not supported as regular parameters like in OAS 2.
2024-10-09 22:28:57 -04:00
Tommi Reiman
d11deb3473
Merge pull request #701 from bsless/fix-interface-maps
Add dispatch for every implementation of IPersistentMap
2024-09-30 20:03:41 +01:00
Joel Kaasinen
e2c63d6579
Merge pull request #697 from dekelpilli/master
fix: fix bug where http ring handler would cause :path to be applied twice
2024-09-25 10:45:02 +03:00
Joel Kaasinen
86871a6a55
Merge pull request #702 from metosin/openapi-example-docs
clarify openapi docs plus minor fix
2024-09-16 15:51:39 +03:00
Joel Kaasinen
923bafdc9b doc: update changelog 2024-09-16 12:49:30 +03:00
Joel Kaasinen
afc8945d78 doc: improve openapi docs 2024-09-16 12:47:33 +03:00
Joel Kaasinen
610586f0d3 fix: OpenAPI :description belongs at Response level, not Media Type
also, support singular :example in addition to :examples
2024-09-16 12:46:57 +03:00
Joel Kaasinen
5a811421db
Merge pull request #699 from metosin/improve-syntax-docs
improve route syntax docs a bit
2024-09-16 08:20:02 +03:00
Ben Sless
c96b22bc5f Add dispatch for every implementation of IPersistentMap
Closes #700
2024-09-13 20:25:51 +03:00
Joel Kaasinen
21a967145f doc: link to name_based_routing.md from route_data.md 2024-09-11 11:25:46 +03:00
Joel Kaasinen
494aa29cdb doc: improve route_syntax.md
- don't use no-data no-child routes as examples, they confuse newbies
- spell out what route data can be
- link to other docs
2024-09-11 11:25:46 +03:00
Tommi Reiman
02d4f797ca
Update README.md
not monstrously big logo
2024-09-02 13:50:39 +03:00
Tommi Reiman
0158dbb54b smaller 2024-09-02 13:44:31 +03:00
Tommi Reiman
4aff73305f 0.7.2 2024-09-02 13:36:25 +03:00
Tommi Reiman
f5bb9ce70e
Update README.md 2024-09-02 13:36:03 +03:00
Martín Varela
337e6c39c7
Update README.md
Fix logo width
2024-09-02 13:30:41 +03:00
Martín Varela
3136948453
Update README.md
fix width
2024-09-02 13:29:58 +03:00
Martín Varela
c155ee7160 Transparent logo replacement 2024-09-02 13:28:55 +03:00
Tommi Reiman
ff99ab3ff9 rollback pedestal, works only on java11 2024-09-02 13:20:44 +03:00
Martín Varela
a40d9893fb
Update README.md
Point logo to the file on `master`
2024-09-02 13:12:20 +03:00
Martín Varela
ed78552a72
Merge pull request #698 from metosin/add-logo
Add logo to Readme
2024-09-02 13:11:09 +03:00
Martín Varela
dd1d507d90 Make logo even smaller and align right 2024-09-02 13:04:59 +03:00
Martín Varela
2ac6ee84df Make logo smaller 2024-09-02 13:02:58 +03:00
Martín Varela
400a2be0fe
Update README.md
fix logo link
2024-09-02 13:01:57 +03:00
Martín Varela
65ff2ee22d Added logo image (temp location) 2024-09-02 12:51:07 +03:00
Dekel Pilli
f1ec7bbe8e
fix: fix bug where http ring handler would cause :path to be applied twice 2024-09-02 15:40:00 +10:00
Tommi Reiman
f466271e15 pedestal-test fail with java8 2024-08-30 18:15:45 +03:00
Tommi Reiman
d926ef7591 0.7.2 2024-08-30 18:05:18 +03:00
Tommi Reiman
c9848bd6e4 CHANGELOG + deps 2024-08-30 18:04:22 +03:00
Tommi Reiman
8b0c8a3c18
Merge pull request #696 from metosin/post-693
Followup 693
2024-08-27 14:16:04 +03:00
Tommi Reiman
bffe360c6d
Merge pull request #506 from bsless/faster-keywordize
Faster keywordize
2024-08-27 14:11:48 +03:00
Tommi Reiman
734fca7d4a
Merge pull request #694 from whamtet/master
bugfix
2024-08-27 14:08:56 +03:00
Tommi Reiman
5a2ae56991 simplify 2024-08-27 14:06:15 +03:00
Tommi Reiman
d8a8bce272 move out of public api 2024-08-27 14:06:09 +03:00
Tommi Reiman
517ae8ffc3
Merge pull request #693 from bsless/faster-routes
Speed up routes and inline it in code ring handler
2024-08-27 13:58:57 +03:00
Ben Sless
78cc54d3a8 Add generative test for new keywordize 2024-08-26 17:07:51 +03:00
Matthew Molloy
c94ecf5ca7 bugfix 2024-08-26 09:00:30 +09:00
Ben Sless
4eab67a8db reduce-kv over treemap 2024-08-25 19:25:54 +03:00
Ben Sless
7dfc0e5fca Fix dynamism 2024-08-25 19:19:26 +03:00
Ben Sless
61783e4c81 Statically def transducer
Eliminates allocation and friendlier to JIT
2024-08-25 18:56:30 +03:00
Ben Sless
59642e51f1 Decrease code size and eliminate an allocation 2024-08-25 18:54:55 +03:00
Ben Sless
dcb7258caf Tailor keywordize implementation to concrete types
Even faster
2024-08-25 18:50:43 +03:00
Ben Sless
7ab6021630 Add faster keywordize-keys implementation for clj 2024-08-25 18:25:54 +03:00
Ben Sless
c48b6a3704 Speed up routes code path
Fixes #692
2024-08-25 16:20:49 +03:00
Ben Sless
a0467d52cd Inline call to routes 2024-08-25 16:20:15 +03:00
Tommi Reiman
5589328a3c 0.7.1 2024-06-30 18:58:46 +03:00
Tommi Reiman
af2fc137d5 update deps 2024-06-30 18:58:13 +03:00
Tommi Reiman
be35375387 prepare for release 2024-06-30 18:37:23 +03:00
Tommi Reiman
d88b693e26
Merge pull request #688 from metosin/issue-679
Issue 679
2024-06-30 18:36:46 +03:00
Tommi Reiman
e8c3035254 . 2024-06-30 18:31:14 +03:00
Tommi Reiman
aec024a943 fix 2024-06-30 18:29:52 +03:00
Tommi Reiman
49e8d887da fixes #679 2024-06-30 17:55:47 +03:00
Tommi Reiman
ee67a746d4 reduce-kv 2024-06-30 17:55:23 +03:00
Tommi Reiman
72dadb3c76
Merge pull request #687 from metosin/fix-686
Fix for #686
2024-06-29 16:32:47 +03:00
Tommi Reiman
e0cc8b4cd8 Changelog 2024-06-29 16:27:47 +03:00
Tommi Reiman
c792a6b794 updated deps 2024-06-29 16:27:10 +03:00
Tommi Reiman
129de37d3e . 2024-06-29 16:23:35 +03:00
Tommi Reiman
77f0798c06 . 2024-06-29 16:22:31 +03:00
Tommi Reiman
eb4c231c23 format 2024-06-29 16:21:35 +03:00
Tommi Reiman
2da94f733d don't merge records 2024-06-29 16:21:28 +03:00
Tommi Reiman
d136975154 format 2024-06-29 16:20:44 +03:00
Tommi Reiman
411bf39c7d
Merge pull request #685 from PEZ/rf-match-to-path-optional-arities
Add arities 1 and 2 to rf/match->path
2024-06-21 12:05:23 +03:00
Peter Strömberg
2f3fc21c84 Add arities 1 and 2 to rf/match->path
To adhere to the docstring's info about parameter 2 and 3 being optional
2024-06-21 09:59:33 +02:00
Joel Kaasinen
4e85cb14c5
Merge pull request #681 from alexhall/openapi-readme
Add reitit-openapi to readme, clarify group-ids
2024-05-17 09:05:28 +03:00
Alex Hall
2da5d8bb7d Add reitit-openapi to readme, clarify group-ids 2024-05-10 20:30:20 -04:00
Tommi Reiman
4d4307ef57 . 2024-04-30 12:00:06 +03:00
Tommi Reiman
fbfd7ad5f3 README 2024-04-30 11:58:41 +03:00
Tommi Reiman
509b4dd232 CHANGELOG for 0.7.0 2024-04-30 11:51:29 +03:00
Tommi Reiman
877c45af90 bump up version 2024-04-30 11:51:18 +03:00
Tommi Reiman
bed41fa46c test with all LTS versions 2024-04-30 11:15:15 +03:00
Tommi Reiman
2be0dbbb2a 0.7.0-alpha8 2024-04-30 11:12:39 +03:00
Tommi Reiman
710eb69e8c 0.7.0-alpha8 2024-04-30 11:11:25 +03:00
Joel Kaasinen
275b789ca9
Merge pull request #677 from metosin/improve-swagger-var-tests
test: add response schema to reitit.swagger-test/malli-var-test
2024-04-26 08:59:23 +03:00
Joel Kaasinen
18550cd297 test: add response schema to reitit.swagger-test/malli-var-test 2024-04-22 09:08:37 +03:00
Joel Kaasinen
15790f3028
Merge pull request #676 from metosin/update-validator
update openapi-schema-validator, fix openapi requestBody description
2024-04-22 08:48:59 +03:00
Joel Kaasinen
3296323059 test: use string refs in openapi-malli-tests
... in order to pass the validator, which disallows "/"
2024-04-22 08:31:51 +03:00
Joel Kaasinen
037763561e fix: location of openapi :requestBody :description
:description should be under :requestBody, not under :content. Thanks
to openapi-schema-validator for noticing this.

Also fix examples in openapi-malli-tests to make the resulting schema
valid.
2024-04-22 08:31:51 +03:00
Joel Kaasinen
c84433352e deps: bump @seriousme/openapi-schema-validator 2024-04-22 08:31:51 +03:00
Joel Kaasinen
c8c8c0eb03
Merge pull request #673 from metosin/malli-vars
Generate correct OpenAPI $ref schemas for malli var and ref schemas
2024-04-22 08:31:22 +03:00
Joel Kaasinen
a06b2c98a7 doc: update CHANGELOG.md 2024-04-22 08:17:37 +03:00
Joel Kaasinen
3cb387747b doc: use malli vars in examples/openapi 2024-04-22 08:00:27 +03:00
Joel Kaasinen
57fc00a45e test: tests for openapi + malli refs/vars 2024-04-22 08:00:27 +03:00
Joel Kaasinen
ce52b26329 test: actually assert something in openapi-malli-tests
(is (= x) y) strikes again
2024-04-22 08:00:27 +03:00
Joel Kaasinen
337d94823a feat: support ref schemas in openapi parameters
e.g. {:parameters {:query #'MyVar}}
2024-04-22 08:00:27 +03:00
Joel Kaasinen
288b701d4e feat: openapi #/components/schemas
collect definitions when traversing the models, and put them in the
right place for openapi

depends on :malli.json-schema/definitions-path support
2024-04-22 08:00:27 +03:00
Joel Kaasinen
c2372473d0 test: test for malli vars + swagger 2024-04-22 08:00:27 +03:00
Joel Kaasinen
066c54b1d2
Merge pull request #675 from metosin/0.7.0-alpha8
0.7.0-alpha8
2024-04-22 07:55:36 +03:00
Tommi Reiman
7432ef9bb9 require java11 2024-04-20 16:42:36 +03:00
Tommi Reiman
fa24dcd29a update deps 2024-04-20 16:38:41 +03:00
Joel Kaasinen
bbaab0b8f8
Merge pull request #674 from metosin/bump-examples
Bump deps on examples
2024-04-19 16:53:24 +03:00
Martín Varela
81b9464f68 deps: bump deps for frontend example
Also fix frontend example to work with current reagent version
2024-04-19 16:25:45 +03:00
Joel Kaasinen
1b7fc0fc58 deps: bump the deps on some examples 2024-04-19 14:32:08 +03:00
Joel Kaasinen
13e8dd86e5 deps: bump deps on some examples 2024-04-19 14:32:08 +03:00
Joel Kaasinen
06c0fd8566 fix: ns name in examples/pedestal-malli-swagger, bump deps 2024-04-19 14:32:08 +03:00
Joel Kaasinen
cd6c23823c deps: bump the deps of some examples 2024-04-19 14:32:08 +03:00
Joel Kaasinen
ff76f5d888 deps: update deps on some examples 2024-04-19 14:32:08 +03:00
Joel Kaasinen
cc9863c95f deps: bump deps in examples/buddy-auth 2024-04-19 14:32:08 +03:00
Joel Kaasinen
59df1f995b deps: bump clojure version in examples 2024-04-19 14:32:08 +03:00
Joel Kaasinen
f41d555b62
Merge pull request #671 from metosin/fix-example
fix: remove unsupported coercions when generating swagger
2024-04-19 10:32:25 +03:00
Joel Kaasinen
a69cfdac41
Merge pull request #666 from velios/patch-1
Update ring-swagger-ui for support openapi 3.1.0 version
2024-04-19 10:21:08 +03:00
Joel Kaasinen
b6c5b69ffe
Merge pull request #659 from frenchy64/escaped-double-quote-doc
Fix formatting of #'router docstring
2024-04-19 10:18:32 +03:00
Joel Kaasinen
8ce2de3631 doc: reitit requires clojure 1.11
now that we're using update-vals
2024-04-19 10:16:13 +03:00
Joel Kaasinen
ff957661e5 doc: update CHANGELOG.md 2024-04-19 10:16:13 +03:00
Joel Kaasinen
01b476b342 fix: remove unsupported coercions when generating swagger
If we don't remove them, :responses :content gets passed out verbatim
in the swagger.json, breaking stuff.

In particular, fixes the swagger.json in
examples/reitit-malli-swagger. Reported broken in #669.
2024-04-19 10:16:13 +03:00
Joel Kaasinen
fbec1e2ecc
Merge pull request #668 from metosin/bump-muuntaja
deps: bump muuntaja
2024-03-15 12:06:11 +02:00
Joel Kaasinen
00b5487cc0 deps: bump muuntaja 2024-03-15 12:01:08 +02:00
Joel Kaasinen
fb2f4b2ee9 doc: update CHANGELOG.md 2024-03-15 10:36:15 +02:00
Joel Kaasinen
c67a748915
Merge pull request #585 from djblue/var-handler
Allow var handlers
2024-03-15 10:35:22 +02:00
Joel Kaasinen
d24b501281 doc: handlers can be vars 2024-03-15 09:31:48 +02:00
Joel Kaasinen
5d30a73bad feat: reitit.core/Expand for Vars 2024-03-15 09:21:51 +02:00
Joel Kaasinen
659e96e780 test: handler can be a var 2024-03-15 09:18:54 +02:00
Juho Teperi
2fe448c3d8 Add frontend-malli example 2024-02-26 11:12:54 +02:00
velios
c295e645c5
Update ring-swagger-ui dependency
The need for such a change is that reitit-openapi module creates openapi.js of 3.1.0 version, but current reitit-swagger-ui can't draw it, because dependency through root to ring-swagger-ui . ring-swagger-ui > 5.x.x version can draw openapi.js 3.1.0 version correctly
2024-02-24 13:41:31 +01:00
Martín Varela
ca434f9c05
Merge pull request #663 from metosin/openapi-exp
#636 Adds level-1 Muuntaja support for OpenAPI3
2024-02-09 12:19:15 +02:00
Martín Varela
0e8d635e44 fix: added muuntaja dependency for the openapi module 2024-02-09 12:14:35 +02:00
Martín Varela
6c9b280fa2 doc: add notice about OpenAPI support being clj only 2024-02-09 12:12:58 +02:00
Martín Varela
cb1c5e8748 made openapi clj, not cljc 2024-02-09 12:12:58 +02:00
Martín Varela
e7be6327d4 doc: make :default stand out as special 2024-02-09 11:52:00 +02:00
Martín Varela
d2c00026e6 doc: Update docs for fetching content types from Muuntaja instance 2024-02-09 11:49:44 +02:00
Martín Varela
ed280f9a33 feature: fetch openapi content types from muuntaja
(level 1 integration in #636)
2024-02-09 11:49:44 +02:00
Martín Varela
f1e6d37dcf fix: don't output :default in openapi request body 2024-02-09 11:49:44 +02:00
Martín Varela
98f3eb0a72
Merge pull request #664 from metosin/fix-swagger-tests
Fix: swagger tests and CI
2024-02-09 11:48:59 +02:00
Martín Varela
82c714d0cc fix: pin version for openapi-schema-validator on CI 2024-02-09 11:42:19 +02:00
Martín Varela
982ac3ec72 Fix: account for changed malli encoder behavior that was breaking ring-coercion-test 2024-02-09 11:37:34 +02:00
Martín Varela
f99a76886e Fix: fix swagger ring tests, malli keys are now strings, not keywords 2024-02-09 11:21:31 +02:00
Tommi Reiman
989ab72a58 use latest malli.
test fail, but they did so earlier. FIX
2024-01-16 14:59:53 +02:00
Ambrose Bonnaire-Sergeant
5444bad439 Fix formatting of #'router docstring
Escaped double quotes breaks the clojure.repl/doc output.
2024-01-12 13:50:18 -06:00
Tommi Reiman
7e00de835d
Merge pull request #615 from nimitmaru/patch-1
Update README.md - fixed link
2024-01-08 08:04:47 +02:00
Juho Teperi
620d0c2711
Merge pull request #655 from stig/fix-link-to-jira
Fix links to Jira in the documentation
2023-10-30 12:34:48 +02:00
Juho Teperi
8d2d295a60
Merge pull request #654 from stig/effect->affect
Correct "effects" to "affects" in comments & docs
2023-10-30 12:32:57 +02:00
Stig Brautaset
0fff06ec6b
Correct "effects" to "affects" in comments & docs
Their usage is commonly confused, but "affect" is usually a verb and
"effect" is usually a noun. In this case we want the verb. See also
https://www.merriam-webster.com/grammar/affect-vs-effect-usage-difference
2023-10-13 21:10:20 +01:00
Stig Brautaset
f4a8013388
Fix link to Jira in the documentation
The existing links didn't work for me, but the new links do.
2023-10-09 14:03:36 +01:00
Juho Teperi
15e0c95cb6 0.7.0-alpha7 2023-10-03 13:34:36 +03:00
Juho Teperi
53327b3147 Update ring-swagger-ui to latest v4 release 2023-10-03 13:33:29 +03:00
Juho Teperi
92a746f803
Merge pull request #653 from metosin/revert-group-id
Revert group id change
2023-10-03 13:19:37 +03:00
Juho Teperi
d4a85c40e6 Revert group id change
Per discussions in Slack, changing the group id causes too much problems
2023-10-03 13:06:23 +03:00
Juho Teperi
8115fb225f Update swagger-ui for examples using v5 2023-10-03 11:58:33 +03:00
Tommi Reiman
8eb3b75a80
Merge pull request #652 from vedang/fix/update-example-clj-version
Update Clojure version of all Swagger Examples to 1.11
2023-09-28 20:27:53 +03:00
Vedang Manerikar
bdadb0fa8d Update Clojure version of all Swagger Examples to 1.11
As of 497da675b9 (committed 1st Sept
2023), `swagger.cljc` uses the `update-vals` function, introduced in
Clojure 1.11.

Due to this, all the example projects fail to run since the
`project.clj` specifies Clojure version 1.10.

This commit updates the Clojure version for such examples, fixing the
problem.

Fixes: #651
2023-09-28 22:03:43 +05:30
Tommi Reiman
90f3708e16
Merge pull request #648 from metosin/fix-groups
README.md: fix latest version, cljdoc links
2023-09-11 17:01:17 +03:00
Joel Kaasinen
67a6658754 doc: fix lates version in README.md 2023-09-11 15:26:24 +03:00
Joel Kaasinen
c1a82ad8ab doc: fix group name in cljdoc links in README.md 2023-09-11 15:24:41 +03:00
Tommi Reiman
6a39dcd6ec 0.7.0-alpha6 2023-09-11 15:03:31 +03:00
Tommi Reiman
2778775565
Merge pull request #647 from metosin/change-groups
move all libs metosin/reitit => fi.metosin/reitit
2023-09-11 14:58:51 +03:00
Joel Kaasinen
8a22696a37 doc: update cljdoc badge with new group 2023-09-11 11:08:41 +03:00
Joel Kaasinen
f0f19c2dae doc: mention group change in CHANGELOG 2023-09-11 11:06:18 +03:00
Joel Kaasinen
35264dc130 doc: update all references metosin/reitit => fi.metosin/reitit 2023-09-11 11:04:36 +03:00
Joel Kaasinen
ccc2b5636e move all libs metosin/reitit => fi.metosin/reitit
metosin/ is a clojars legacy group, and new modules like
fi.metosin/openapi can't be created under it. Let's move everything
under fi.metosin
2023-09-11 11:04:36 +03:00
Joel Kaasinen
6360fa8ba0
Merge pull request #639 from metosin/fix-openapi-examples
fix, test and document openapi named examples
2023-09-11 11:04:16 +03:00
Joel Kaasinen
bad798d90d feat: allow vectors for openapi/swagger :tags
no need to insist on set, and many of our examples use vectors anyway
2023-09-11 07:59:17 +03:00
Joel Kaasinen
b4c0936207 Merge remote-tracking branch 'origin/master' into fix-openapi-examples 2023-09-11 07:53:15 +03:00
Joel Kaasinen
aee0caa5c8 fix: :content coercion and :tags in examples/http-swagger 2023-09-11 07:48:41 +03:00
Joel Kaasinen
b0b9f8cbee fix: enable validation in examples/openapi, make it pass 2023-09-11 07:45:25 +03:00
Joel Kaasinen
557c89acdd fix: enable validatio nin examples/ring-malli-swagger, make it pass 2023-09-11 07:40:36 +03:00
Joel Kaasinen
5352fd4f99 fix: swagger & openapi ::tags spec
`#{}` isn't a valid :kind predicate
2023-09-11 07:40:13 +03:00
Joel Kaasinen
b206fc79b3 fix: spec for openapi-feature 2023-09-11 07:12:00 +03:00
Tommi Reiman
721cf7f321
Merge pull request #646 from pfeodrippe/patch-1
Remove duplicated :operationId
2023-09-08 17:06:25 +03:00
Joel Kaasinen
abd84de68d doc: update CHANGELOG.md 2023-09-08 15:35:57 +03:00
Joel Kaasinen
7352358662 feat: rename openapi :content-types keyword, split for req & resp 2023-09-08 15:17:35 +03:00
Paulo Rafael Feodrippe
68adf50362
Remove duplicated :operationId 2023-09-07 16:39:39 -04:00
Joel Kaasinen
f2e6d335f0
Merge pull request #642 from metosin/rework-pr-589
Fix malli swagger defs w/ custom registries
2023-09-01 15:45:49 +03:00
Joel Kaasinen
ee462c9981 bump malli dep 2023-09-01 10:56:43 +03:00
Joel Kaasinen
497da675b9 refactor: use update-vals 2023-09-01 10:37:17 +03:00
Joel Kaasinen
df5f75eb89 Merge remote-tracking branch 'origin/master' into rework-pr-589 2023-09-01 10:33:20 +03:00
Joel Kaasinen
aef74c6e6a
Merge pull request #644 from metosin/bump-clojure
drop support for clojure 1.10
2023-09-01 10:32:54 +03:00
Joel Kaasinen
68371c2c05 drop support for clojure 1.10 2023-09-01 10:22:50 +03:00
Juho Teperi
cdbfef2d21
Merge pull request #640 from metosin/cljdoc-check-fix
Try skipping module without src folder
2023-08-30 09:55:23 +03:00
Joel Kaasinen
241c8367e3 feat: dissoc definitions from swagger methods
we only want the definitions on the very top level of the swagger doc
2023-08-30 09:38:33 +03:00
Joel Kaasinen
b316840ea0 fix: compile instead of -compile-model in malli.cljc
-compile-model now takes a vector of models, so
(-compile-model this model nil) fails. Just use compile directly like
master does.
2023-08-30 09:38:33 +03:00
Joel Kaasinen
f1d26791fc Merge branch 'master' into rework-pr-589 2023-08-30 08:29:06 +03:00
Joel Kaasinen
5d0bce1242 doc: clarify :content-types in example/openapi 2023-08-28 17:49:23 +03:00
Juho Teperi
0323409cd5 Try skipping module without src folder 2023-08-28 16:00:03 +03:00
Joel Kaasinen
7b41882e6c doc: remove redundant examples 2023-08-28 15:46:05 +03:00
Joel Kaasinen
38547e4ad2 doc: update openapi & coercion docs 2023-08-28 15:44:16 +03:00
Joel Kaasinen
c6541de1b5 doc: add examples/openapi 2023-08-28 15:41:06 +03:00
Joel Kaasinen
76a08a2322
Merge pull request #637 from metosin/fix-openapi-example
don't crash swagger generation on :content, fix examples
2023-08-28 13:49:02 +03:00
Joel Kaasinen
85ebb343ed refactor: remove dead code 2023-08-28 13:48:28 +03:00
Joel Kaasinen
d5021e549a fix: pick up openapi examples from [:responses _ :content :default] 2023-08-28 13:48:23 +03:00
Joel Kaasinen
4d1d469686 test: test multiple examples using new syntax 2023-08-28 13:48:23 +03:00
Joel Kaasinen
25aee5ed22 Merge remote-tracking branch 'origin/master' into fix-openapi-example 2023-08-28 13:01:58 +03:00
Joel Kaasinen
7b88125f5e
Merge pull request #638 from metosin/openapi-refactor
share openapi generation code between malli, spec & schema
2023-08-28 12:55:43 +03:00
Joel Kaasinen
55854f6652 doc: add openapi response content type to examples/ring-spec-swagger 2023-08-28 11:22:42 +03:00
Joel Kaasinen
e4c75c7354 fix: dissoc unsupported [:responses nnn :content] for swagger
otherwise swagger generation crashes when it tries to serialize
something like `{:content {"foo/bar" any?}}`

also fix examples/ring-malli-swagger
2023-08-28 11:18:00 +03:00
Joel Kaasinen
7b4127b0f1 fix: examples/http-swagger
broken by #628

fixes #634
2023-08-28 10:10:14 +03:00
Joel Kaasinen
8af89c05cb refactor: get rid of reitit.coercion/get-apidocs 2023-08-28 10:06:14 +03:00
Joel Kaasinen
233ac19914 refactor: remove dead code 2023-08-28 10:03:07 +03:00
Joel Kaasinen
6f111bce2e refactor: share -get-apidocs-openapi between malli, spec & schema 2023-08-28 09:24:10 +03:00
Joel Kaasinen
051452231a refactor: -get-model-apidocs for schema & spec
not used yet tho
2023-08-28 09:02:11 +03:00
Joel Kaasinen
f943b025cb refactor: no need to pass options into -get-apidocs-openapi 2023-08-28 08:59:58 +03:00
Joel Kaasinen
ee298ec362 refactor: Coercion.-get-model-apidocs, use it for malli openapi 2023-08-28 08:42:54 +03:00
Joel Kaasinen
803ed0933a refactor: parameterise -get-apidocs-openapi with ->schema-object 2023-08-24 11:05:43 +03:00
Tommi Reiman
b0c810a981
Merge pull request #628 from metosin/openapi-parameters
Openapi parameters
2023-08-24 09:25:46 +03:00
Tommi Reiman
05cbed815f review comment fixes 2023-08-24 08:38:18 +03:00
Tommi Reiman
adef7ad06e read openapi metadata into openapi description 2023-08-23 16:43:34 +03:00
Tommi Reiman
226ca889b6 openapi content tests 2023-08-18 17:17:56 +03:00
Tommi Reiman
d8e9819e0a fix responses & request 2023-08-18 16:47:20 +03:00
Tommi Reiman
81dfe45b72 fix example 2023-08-18 15:17:01 +03:00
Tommi Reiman
12f0970e39 fix example 2023-08-18 15:04:47 +03:00
Tommi Reiman
73422e8da0
Merge pull request #631 from allentiak/patch-1
fix CHANGELOG.md
2023-08-14 10:25:07 +03:00
Leandro Doctors
60719f03c7
fix CHANGELOG.md
"acculated" --> "caculated"? accumulated?
2023-07-06 23:06:12 +00:00
Juho Teperi
5acb1a7ffe Update changelog 2023-06-14 16:10:23 +03:00
Juho Teperi
e93f365a0b Fix changelog list formatting 2023-06-14 16:09:02 +03:00
Juho Teperi
4a182588b4 Release 0.7.0-alpha5 2023-06-14 16:06:20 +03:00
Juho Teperi
5b7b0a7b4e
Merge pull request #629 from metosin/cljdoc-check
Check cljdoc analysis on gha
2023-06-14 15:59:56 +03:00
Juho Teperi
a032abc910 Fix interceptors deps 2023-06-12 18:15:53 +03:00
Juho Teperi
cf5906030a Reitit-dev fixes 2023-06-12 17:57:51 +03:00
Juho Teperi
ac410507f2 Fix reitit-dev deps 2023-06-12 17:50:10 +03:00
Juho Teperi
2db9ee328c Check cljdoc analysis on gha 2023-06-12 17:47:18 +03:00
Tommi Reiman
0728154751 name the doseq-tests 2023-05-29 12:07:54 +03:00
Tommi Reiman
b1404ada6d top-level :request coercion & stuff 2023-05-28 16:49:08 +03:00
Tommi Reiman
93a4246682 allow default 2023-05-28 12:07:01 +03:00
Tommi Reiman
1b5287724e format 2023-05-27 19:02:10 +03:00
Tommi Reiman
d17c97780e wrap :content schemas in :schema 2023-05-27 19:01:17 +03:00
Tommi Reiman
499f84be21 fix warning 2023-05-27 18:08:39 +03:00
Tommi Reiman
77e2b567e6
Merge pull request #626 from metosin/power-merge-schemas
Introduce two-phase Schema compilation
2023-05-23 12:10:49 +03:00
Tommi Reiman
9f58bb22e3 kill ctrl 2023-05-22 20:28:20 +03:00
Tommi Reiman
3e6c3f589f review comments 2023-05-22 20:23:47 +03:00
Tommi Reiman
9ac713f0e5 doesn't work properly with spec 2023-05-22 09:16:57 +03:00
Tommi Reiman
ce06214014 welcome 2-phase schema compilation
1) use `:update-paths` to handle data in certain (loose) paths differently
  - accumulate schemas in all relevant routers into vector
  - we do not know the coercion here (ring/http have special handling of data, e.g. http-methods)

2) run coercion compiler for the model to merge the effective model
  - schema + malli = should work ok, spec = best effort

3) publish final schemas into compiled route data
2023-05-21 20:32:40 +03:00
Tommi Reiman
3f265888a4 cleanup 2023-05-21 20:16:45 +03:00
Tommi Reiman
550ea6da58 path-update 2023-05-21 20:13:43 +03:00
Tommi Reiman
4d0e40f135 ctrl.* is such test 2023-05-21 18:15:44 +03:00
Tommi Reiman
d45dd151b7 document meta-merge 2023-05-21 18:11:42 +03:00
Tommi Reiman
1827c1294b update deps 2023-05-21 15:58:59 +03:00
Tommi Reiman
b3383b0396 0.7.3-alpha4 2023-05-17 18:23:05 +03:00
Joel Kaasinen
2e555a1453
Merge pull request #625 from metosin/openapi-descriptions
new schema-tools version fixes #612 for spec
2023-05-17 17:59:20 +03:00
Joel Kaasinen
86af5d8724 doc: CHANGELOG.md 2023-05-17 16:09:27 +03:00
Joel Kaasinen
1983a4bb64 test: openapi parameter descriptions via schema 2023-05-17 16:06:45 +03:00
Joel Kaasinen
45f9f0f21e deps: upgrade schema-tools 2023-05-17 16:06:45 +03:00
Joel Kaasinen
6b378ffbf5
Merge pull request #624 from metosin/openapi-descriptions-malli
fix: openapi malli parameter descriptions
2023-05-17 12:24:28 +03:00
Joel Kaasinen
c443adbfca test: openapi parameter descriptions via spec 2023-05-17 08:25:44 +03:00
Joel Kaasinen
b56c15b64c fix: openapi malli parameter descriptions
... should come from the parameter type, not from the parent :map
2023-05-17 08:19:44 +03:00
Joel Kaasinen
2e8e9265d9 test: document current openapi3 description behaviour
malli works weirdly, others don't
2023-05-17 08:19:26 +03:00
Joel Kaasinen
4ac973ba31
Merge pull request #623 from metosin/doc-openapi
doc: openapi.md: annotating schemas
2023-05-17 07:57:32 +03:00
Joel Kaasinen
88d7caf013 doc: use alpha ring-swagger-ui in ring-spec-swagger
to support openapi 3
2023-05-16 17:02:25 +03:00
Joel Kaasinen
ed1230d1cf doc: openapi.md: annotating schemas 2023-05-16 17:02:06 +03:00
Wes Morgan
59812a350f
Update malli swagger test expectations for definitions
...which should only be at the top level now.
2023-05-09 10:33:54 -06:00
Juho Teperi
0b6ed62738 Release 0.7.0-alpha3 2023-05-05 12:57:31 +03:00
Juho Teperi
5adc5ffba0 Compile reitit.Trie with Java 1.8 target 2023-05-05 12:56:02 +03:00
Juho Teperi
e1f9cfb286 Fix versions 2023-05-05 11:18:38 +03:00
Juho Teperi
e204d4ff1b Release 0.7.0-alpha2 2023-05-04 15:20:31 +03:00
Juho Teperi
9f6565f097
Merge pull request #604 from metosin/fix-377-fragment-strings
Fix #377, navigate to routes with fragment string in frontend
2023-05-04 15:12:41 +03:00
Juho Teperi
b9f189b3f7 Fix Html5History missing fragment on initial load 2023-05-04 15:05:27 +03:00
Joel Kaasinen
526bac39c2
Merge pull request #618 from metosin/openapi-examples
more openapi examples, remove redundant ring-swagger example
2023-05-03 17:28:06 +03:00
Joel Kaasinen
253f379fd0 doc: use examples/ring-{malli,spec}-swagger in doc/ring/openapi.md 2023-05-03 17:17:25 +03:00
Joel Kaasinen
f14808b8a3 doc: update examples/ring-spec-swagger to include OpenAPI 3 2023-05-03 17:17:25 +03:00
Joel Kaasinen
28e9cc01e5 doc: update examples/ring-malli-swagger 2023-05-03 17:17:25 +03:00
Joel Kaasinen
bafc9b757f doc: remove examples/ring-swagger
... it's the same as ring-spec-swagger. Let's keep the pair
ring-spec-swagger and ring-malli-swagger.
2023-05-03 17:17:22 +03:00
Joel Kaasinen
bcbdd05a6f
Merge pull request #617 from metosin/openapi-docs
doc: mention reitit-openapi in cljdoc
2023-05-03 17:12:47 +03:00
Joel Kaasinen
8db7598141 doc: mention reitit-openapi in cljdoc 2023-05-03 16:40:49 +03:00
Tommi Reiman
f015b6669a fi 2023-05-03 15:41:11 +03:00
Tommi Reiman
3336880b01 0.7.0-alpha1 2023-05-03 10:25:01 +03:00
Joel Kaasinen
a3ab5714cb CHANGELOG 2023-05-02 14:55:19 +03:00
Nimit Maru
24669cf58f
Update README.md
ring router link bugfix
2023-05-01 17:46:34 -04:00
Wes Morgan
937768651e
Add malli swagger test w/ definitions 2023-04-20 12:39:08 -06:00
Joel Kaasinen
6d9d7a09b0
Merge pull request #610 from metosin/openapi-examples
document & test OpenAPI multiple examples
2023-04-19 11:09:03 +03:00
Joel Kaasinen
be0d066f5d doc: document OpenAPI3 multiple examples 2023-04-19 11:03:15 +03:00
Joel Kaasinen
5227e65029 doc: OpenAPI3 named examples in examples/http-swagger 2023-04-19 10:56:22 +03:00
Joel Kaasinen
ca9852a318 test: multiple named openapi examples 2023-04-19 10:49:23 +03:00
Joel Kaasinen
75ebeaf6cd test: test openapi examples support for malli, schema, spec 2023-04-18 15:04:14 +03:00
Wes Morgan
182524baac
Update swagger test expectations for latest malli changes 2023-04-17 11:35:28 -06:00
Wes Morgan
f237b0942e
Merge branch 'master' into fix/malli-swagger-defs 2023-04-17 09:44:36 -06:00
Joel Kaasinen
0c643409a6
Merge pull request #608 from metosin/openapi-recursive
openapi3 (mutual) recursion support
2023-04-17 09:15:33 +03:00
Joel Kaasinen
1a73ba952e test: openapi3 + malli + mutually recursive schemas 2023-04-17 08:45:24 +03:00
Tommi Reiman
4b33c6d203
Merge pull request #607 from metosin/bump-deps
chore: upgrade deps
2023-04-16 09:24:42 +03:00
Joel Kaasinen
3e78667244 doc: clean up upgraded deps in CHANGELOG.md
- don't mention test deps
- malli was 0.10.1 in previous release, not 0.10.4
2023-04-13 09:37:04 +03:00
Joel Kaasinen
33f7b1ccd7 chore: upgrade lein-parent plugin 2023-04-13 09:23:08 +03:00
Joel Kaasinen
6e7acaedee chore: upgrade deps 2023-04-13 09:23:08 +03:00
Joel Kaasinen
8174296fe7 chore: add lein-ancient plugin 2023-04-13 09:15:10 +03:00
Juho Teperi
56e6dbe8d8 Fix #377, navigate to routes with fragment string in frontend 2023-03-24 14:42:13 +02:00
Juho Teperi
2a3e382df1
Merge pull request #601 from metosin/feature/update-query
Fix #600: Add frontend function to update query-params for current path
2023-03-24 14:01:06 +02:00
Juho Teperi
e2217887e3 Comments about differences to reitit.impl 2023-03-24 13:59:02 +02:00
Juho Teperi
dad8f530a6 Add example and update docs 2023-03-24 11:32:22 +02:00
Juho Teperi
48bbdba8ed Implement navigate and set-query functions 2023-03-24 11:16:09 +02:00
Juho Teperi
f78116e346 Test fragment 2023-03-23 15:46:25 +02:00
Juho Teperi
a558365252 Cleanup 2023-03-23 15:41:48 +02:00
Juho Teperi
e3e93eaffb Add some tests 2023-03-23 15:36:48 +02:00
Juho Teperi
dd724f0d0e Fix #600: Add frontend function to update query-params for current path 2023-03-23 15:13:27 +02:00
Wes Morgan
ea3d031acc
Merge branch 'master' into fix/malli-swagger-defs 2023-03-19 13:36:02 -06:00
Tommi Reiman
fc12af4ea8
Merge pull request #598 from metosin/openapi-changelog
CHANGELOG for OpenAPI3
2023-03-19 21:17:20 +02:00
Joel Kaasinen
ade24cebc9 CHANGELOG for OpenAPI3 2023-03-19 20:03:24 +02:00
Tommi Reiman
76b5dfa1e0 fix CHANGELOG 2023-03-19 19:51:00 +02:00
Tommi Reiman
f1890048e1
Merge pull request #597 from metosin/deps
Update Deps
2023-03-19 19:49:32 +02:00
Tommi Reiman
74a56800e2 revert swagger-ui 2023-03-19 19:49:10 +02:00
Tommi Reiman
6a6492b547 malli 0.10.4 2023-03-19 15:52:58 +02:00
Tommi Reiman
2818378b11 Update Deps 2023-03-18 20:26:32 +02:00
Wes Morgan
0f9b624124
Merge branch 'master' into fix/malli-swagger-defs 2023-03-18 11:32:03 -06:00
Joel Kaasinen
bae6e6b8dd
Merge pull request #593 from metosin/openapi-multipart
OpenAPI3 multipart support
2023-03-17 15:51:33 +02:00
Joel Kaasinen
8272b651e3 doc: examples/ring-malli-swagger: share tags between swagger&openapi 2023-03-17 14:58:28 +02:00
Joel Kaasinen
d8e28e153b fix: swagger multipart support
1. For spec we were including some extra stuff in the parameter
specification:

{:description "",
 :in "formData",
 :name "file",
 :properties {"bytes" {:format "byte", :type "string"},
              "content-type" {:type "string"},
              "filename" {:type "string"}},
 :required ["filename" "content-type" "bytes"],
 :type "file"}

2. For malli the :type changed from "file" to "string" because of
openapi changes. Now openapi and swagger both get the right type.

3. Test for swagger multipart support
2023-03-17 14:54:46 +02:00
Tommi Reiman
389f4a29da format 2023-03-17 10:12:15 +02:00
Joel Kaasinen
9a99ed96b2 doc: link to examples/ring-malli-swagger from doc/ring/openapi.md 2023-03-16 10:19:30 +02:00
Joel Kaasinen
bf8d0ba1ef doc: don't say :multipart only works with spec 2023-03-16 10:18:28 +02:00
Joel Kaasinen
6c3db02163 doc: openapi content type for file download in examples/http-swagger 2023-03-16 10:10:10 +02:00
Joel Kaasinen
224acf930e doc: openapi3 in examples/ring-malli-swagger 2023-03-16 10:10:10 +02:00
Joel Kaasinen
b5c9ee274d feat: generate correct openapi for reitit.ring.malli 2023-03-16 09:57:02 +02:00
Joel Kaasinen
83b747c7c6 doc: mention :multipart in doc/ring/coercion.md 2023-03-16 09:39:34 +02:00
Joel Kaasinen
de2d810b7c test: multiple parts in multipart-test 2023-03-15 17:48:03 +02:00
Joel Kaasinen
acbcec1ed9 feat: openapi3 multipart support for schema 2023-03-15 17:48:03 +02:00
Joel Kaasinen
60f53d7e7c deps: upgrade schema-tools 2023-03-15 17:48:03 +02:00
Joel Kaasinen
1c65f533cf feat: openapi3 multipart support for malli 2023-03-15 17:48:03 +02:00
Joel Kaasinen
f322597c04 feat: openapi3 multipart support for spec 2023-03-15 17:48:03 +02:00
Joel Kaasinen
8bf4b5c6a6
Merge pull request #592 from metosin/openapi-fixes
misc. fixes for openapi3 support
2023-03-15 17:47:44 +02:00
Joel Kaasinen
d0ff64df57 test: simplify openapi_test.clj a bit more 2023-03-15 16:01:57 +02:00
Joel Kaasinen
bcd12d9f31 refactor: push openapi/openapi-spec call inside merge
to make reitit.coercion.spec match reitit.coercion.schema
2023-03-15 13:38:34 +02:00
Joel Kaasinen
41c4d78823 chore: reformat http-swagger example 2023-03-15 13:21:14 +02:00
Joel Kaasinen
5107658c25 fix: typo on examples/http-swagger 2023-03-15 13:20:02 +02:00
Joel Kaasinen
66e7c9e1c8 test: nicer handling of missing :additionalProperties for spec 2023-03-15 13:19:01 +02:00
Joel Kaasinen
0c82ce0e4d
Merge pull request #588 from metosin/openapi
Initial Openapi3 support
2023-03-15 09:34:16 +02:00
Joel Kaasinen
6f72acdf32 doc: fix example in openapi-feature docstring 2023-03-15 09:24:27 +02:00
Tommi Reiman
23b2719be5 add version to swagger api 2023-03-14 21:09:03 +02:00
Tommi Reiman
25b75c877a offer both swagger & openapi docs in ui 2023-03-14 20:58:07 +02:00
Joel Kaasinen
814c8b88e2 refactor: factor out -identity-coercer 2023-03-14 14:39:05 +02:00
Joel Kaasinen
bbaf4b27f7 refactor: remove commented-out code 2023-03-14 14:37:53 +02:00
Tommi Reiman
11f47527f1 add reitit-openapi module, 0.5.18 -> 0.6.0 2023-03-14 12:24:05 +02:00
Joel Kaasinen
9b50baca0c doc: examples/http-swagger: authentication 2023-03-13 15:37:49 +02:00
Joel Kaasinen
20cafa3d9b doc: examples/http-swagger: tags for openapi, openapi-feature 2023-03-13 07:33:42 +02:00
Joel Kaasinen
7defd98808 doc: Swagger-UI 5.0.0-alpha.0 has OpenAPI 3.1 support
mention in docs, use in http-swagger example
2023-03-10 14:34:05 +02:00
Joel Kaasinen
304b77cb7d doc: mention lack of swagger-ui support for openapi 3.1 2023-03-10 13:53:10 +02:00
Joel Kaasinen
3fa50ea0f6 doc: mark openapi support as alpha 2023-03-10 10:17:42 +02:00
Joel Kaasinen
8c87fef7b6 feat: warning when swagger encounters per-content-type coercions 2023-03-10 08:52:45 +02:00
Joel Kaasinen
ae55b6628c doc: update docstring of openapi-feature 2023-03-09 10:21:46 +02:00
Tommi Reiman
2a789557f2
Merge pull request #590 from dgb23/patch-1
Update composing_routers.md
2023-03-09 08:40:53 +02:00
Joel Kaasinen
50c1af9a5b feat: route data validation for per-content-type coercions 2023-03-08 14:39:52 +02:00
Joel Kaasinen
9ae3cd0824 fix: reitit.openapi route data validation
:kind set? would've worked, but there's no need to insist a set

also, turn on route data validation in openapi_test.clj
2023-03-08 14:39:52 +02:00
Joel Kaasinen
8df8bf06cc doc: initial docs for openapi support & per-content-type coercion 2023-03-08 11:23:18 +02:00
Joel Kaasinen
52b7402575 doc: openapi in examples/http-swagger 2023-03-08 11:22:02 +02:00
Joel Kaasinen
2cc6e33654 test: openapi operationId, tags and deprecated 2023-03-08 10:55:23 +02:00
Joel Kaasinen
16145dbdce test: validate generated openapi specs in all tests 2023-03-07 09:13:38 +02:00
dgb23
11534551da
Update composing_routers.md
I wondered how one would wrap a ring handler that can be recreated at runtime without restarting the server. @ikitommi suggested to use the recently added `reloading-ring-handler` as a starting point.

I propose a small example here that illustrates the pattern.
2023-03-07 00:13:15 +01:00
Wes Morgan
4f31304a1a
Lift definitions to root of swagger.json
...so that all of the absolute $ref's to them will resolve
2023-03-06 12:16:48 -07:00
Wes Morgan
229171c7e3
Move more of swagger gen into malli lib
...so we can fix #558.
2023-03-06 12:15:20 -07:00
Joel Kaasinen
8e099febdd test: validate generated openapi spec 2023-03-06 16:10:48 +02:00
Joel Kaasinen
2596f25411 chore: openapi-schema-validator dev dependency 2023-03-06 16:10:48 +02:00
Joel Kaasinen
df0d4c4935 chore: upgrade node 2023-03-06 16:10:48 +02:00
Joel Kaasinen
4c990fb44f feat: openapi default request/response schemas
use a default schema from :request/:response :body for all specified
:content-types
2023-03-06 10:58:03 +02:00
Joel Kaasinen
c3a3ca9f95 feat: change syntax for :request parameter coercion default schema
use :request :body instead of :request :content :default for symmetry
with :response
2023-03-06 10:54:24 +02:00
Joel Kaasinen
7842160656 Merge remote-tracking branch 'origin/master' into openapi 2023-03-03 14:43:23 +02:00
Joel Kaasinen
2d60702769 fix: per-content-type openapi w/ spec 2023-03-03 14:15:31 +02:00
Joel Kaasinen
b149c8c5af test: rework openapi3 tests
- DRY out all-parameter-types-test and per-content-type-test
- Remove redundant assertions
- Run same test for malli/schema/spec
- Clean up commented-out code etc.
2023-03-03 14:15:31 +02:00
Joel Kaasinen
c8d679c6b3 feat: per-content-type request/response coercions
implemented on the reitit-core level so individual coercions don't
need changes

syntax:

{:parameters {:request {:content {"application/edn" [:map ...]}}}
 :responses {200 {:content {"application/edn" [:map ...]}}}}
2023-03-03 14:15:31 +02:00
Joel Kaasinen
8f48cdc96c test: enable openapi spec tests 2023-03-03 14:15:22 +02:00
kimmoahola
aec611f8b7 Fix date of version 0.6.0 2023-02-22 13:11:46 +02:00
Tommi Reiman
ae138c6dec updated release guide 2023-02-21 15:54:52 +02:00
Tommi Reiman
47f1ee0c84 0.6.0 2023-02-21 15:17:00 +02:00
Ilmo Raunio
310dcd0e99
Merge pull request #581 from metosin/add-support-for-fragment-parameters-2
Add reitit-frontend support for fragment string
2023-02-21 15:05:25 +02:00
Ilmo Raunio
e490f5df05
Merge pull request #584 from metosin/reloading-ring-handler
reloading-ring-handler
2023-02-21 14:42:33 +02:00
Juho Teperi
2494f702d9 Read fragment string without decoding
Users can use Malli decoding to control decoding per schema.
2023-02-16 13:13:48 +02:00
Juho Teperi
83c31e35bc Revert "Revert "Merge pull request #554 from just-sultanov/add-support-for-fragment-parameters""
This reverts commit 4d1b00edfa.
2023-02-16 11:53:27 +02:00
Joel Kaasinen
f03134e215 fix: malli openapi tests
malli.json-schema now outputs `:additionalProperties false`
2023-02-15 16:07:23 +02:00
Tommi Reiman
b6c046318b
Merge pull request #586 from bplubell/fix-ring-router-docs
Fix incorrect ring-router doc references
2023-02-01 17:30:42 +02:00
bplubell
e175dc76c9 Fix incorrect ring-router doc references
It looks like documentation references to `ring-router` are left-overs
from early README examples - I couldn't find any code that ever used the
name. The Ring router named `reitit.ring/router`.

While it didn't take too long for me to realize why my `ring-router` was
not working, I had to look the examples to figure out the name of the
function was just `router` - which was confusing since the section
header stated `reitit-router`.
2023-01-31 15:51:31 -08:00
Chris Badahdah
999f6c3dbd Allow var handlers 2023-01-31 16:04:28 -07:00
Tommi Reiman
d1bb44a88f
Merge branch 'master' into reloading-ring-handler 2023-01-22 14:30:06 +02:00
Tommi Reiman
0648296315
Merge branch 'master' into feature/openapi 2023-01-22 14:29:22 +02:00
Tommi Reiman
e5bd123740
Merge pull request #579 from metosin/mm
2023 Cleanup Branch
2023-01-22 14:26:07 +02:00
Tommi Reiman
744326d6af (c) 2023-01-22 14:24:30 +02:00
Tommi Reiman
24b2808886 update malli 2023-01-22 14:23:51 +02:00
Tommi Reiman
3fd20f2294 reloading-ring-handler 2023-01-22 14:15:08 +02:00
Tommi Reiman
e75c833fe1 CHANGELOG 2023-01-22 14:07:38 +02:00
Tommi Reiman
358c447698 Update to latest malli 2023-01-21 10:58:55 +02:00
Tommi Reiman
d0a9fd196b (c) year 2023-01-21 10:58:55 +02:00
Tommi Reiman
8e1e7e62ca fix spec tests + format 2023-01-21 10:58:53 +02:00
Tommi Reiman
1247e7ff25 CL 2023-01-21 10:56:42 +02:00
Tommi Reiman
6a396d1315 CL 2023-01-21 10:56:42 +02:00
Tommi Reiman
a290b22679 CHANGELOG 2023-01-21 10:56:42 +02:00
Tommi Reiman
034dc9cfbd CHANGELOG 2023-01-21 10:56:42 +02:00
Tommi Reiman
d1e02fd0a1 Update dependencies 2023-01-21 10:56:42 +02:00
Tommi Reiman
f27c2fc2aa clean 2023-01-21 10:56:42 +02:00
Tommi Reiman
98a4d9b447 :meta-merge-fn -> :meta-merge 2023-01-21 10:56:42 +02:00
Tommi Reiman
3ec5acc7a1
Merge pull request #582 from ilmoraunio/issue-565
Include query-string as part of location header in redirect-trailing-slash-handler
2023-01-21 10:51:20 +02:00
Tommi Reiman
22fb9810a7
Merge pull request #552 from frwdrik/patch-1
Remove redundant s/and
2023-01-21 10:43:26 +02:00
Ilmo Raunio
0870b20a05 Add query-string to redirect-trailing-slash-handler 2023-01-20 00:20:10 +02:00
Juho Teperi
4d1b00edfa Revert "Merge pull request #554 from just-sultanov/add-support-for-fragment-parameters"
This reverts commit c2c267f4d8, reversing
changes made to 8087522b82.
2023-01-18 18:38:47 +02:00
Tommi Reiman
c2c267f4d8
Merge pull request #554 from just-sultanov/add-support-for-fragment-parameters
Add support for fragment parameters in the reitit-frontend module
2023-01-09 17:33:24 +02:00
Tommi Reiman
8087522b82
Merge pull request #452 from TimoKramer/support-operationid
Support operationId in reitit-swagger
2023-01-09 17:29:49 +02:00
Tommi Reiman
42e988e518
Merge branch 'master' into support-operationid 2023-01-09 17:27:00 +02:00
Tommi Reiman
23c20dc44e
Merge pull request #557 from handerpeder/master
Polish pedestal chains when printing context diffs
2023-01-09 17:18:39 +02:00
Tommi Reiman
af441e6154
Merge pull request #569 from SagaHealthcareIT/swagger-operationid-support
Swagger: support operationId in generated swagger json
2023-01-09 17:14:10 +02:00
Tommi Reiman
26a581298a
Merge pull request #561 from pfeodrippe/meta-merge
add `:meta-merge-fn` option
2023-01-09 16:39:12 +02:00
Tommi Reiman
8654bf3277
Merge pull request #577 from devn/devn/upgrade-jackson-for-cves
Upgrade jackson for CVE-2022-42003 and CVE-2022-42004
2022-12-18 09:55:58 +02:00
Tommi Reiman
b918c5f3ba
Merge pull request #578 from dharrigan/docs/update-startrek
Update documentation and link to the startrek project
2022-12-18 09:30:31 +02:00
David Harrigan
ffe8846de1
Update documentation and link to the startrek project
-=david=-
2022-12-18 07:09:30 +00:00
Devin Walters
7e05f4931e Upgrade jackson for CVE-2022-42003 and CVE-2022-42004 2022-12-17 16:00:04 -06:00
Tommi Reiman
f449bf848d
Merge pull request #576 from bsless/coercion-errors-perf
Coercion errors perf
2022-12-13 23:01:44 +02:00
Ben Sless
24f38e0dfa Unroll merge and hash-map coercion 2022-12-06 21:35:26 +02:00
Ben Sless
8398c98595 Add serialize-failed-result coercion option
False by default, if true will serialize the failed coercion result in
the error message
2022-12-06 21:34:13 +02:00
Ben Sless
bc4443a935 Remove schema and errors from default malli coercion error keys 2022-12-06 21:20:48 +02:00
Tommi Reiman
1dbca52002
Merge pull request #574 from ianffcs/add-pedestal-malli-swagger-example
pedestal-malli-swagger-example: fix project.clj in example
2022-12-05 18:24:42 +02:00
Ian Fernandez
544e264ffb fix example in project.clj 2022-12-05 14:56:56 +00:00
Tommi Reiman
8e5fd8a9db
Merge pull request #573 from frwdrik/patch-2
Rename variable handle -> handler
2022-12-05 15:13:29 +02:00
Fredrik Vaeng Røtnes
edee97a550
Rename variable handle -> handler 2022-12-05 13:06:27 +00:00
Tommi Reiman
3d5e0b154c
Merge pull request #572 from ianffcs/add-pedestal-malli-swagger-example
Add example for Reitit + Pedestal + Malli coercion
2022-12-04 15:00:03 +02:00
Ian Fernandez
eebc2a3df0 Add example for Reitit + Pedestal + Malli coercion 2022-12-04 00:07:05 +00:00
Jesse Dowell
148fa2167b
Swagger: support operationId in generated swagger json 2022-11-13 18:54:43 -05:00
Tommi Reiman
e84495585c
Merge pull request #566 from metosin/toni/update-empty-seq-handling-in-query-string
Handle empty seq as empty string in `query-string`
2022-10-16 15:05:21 +03:00
Toni Väisänen
c69b4cde3a Handle empty seq as empty string in query-string
example:

instead of

```clojure
(query-string {:nil nil
               :vec []
               :seq-empty '()})
;; => "nil=&&"
```

now

```clojure
(query-string {:nil nil
               :vec []
               :seq-empty '()})
;; => "nil=&vec=&seq-empty="
```
2022-10-12 13:24:07 +03:00
Enzzo Cavallo
c576b47634
OpenAPI V3 Support 2022-09-05 03:21:53 -03:00
Paulo Feodrippe
fc73d02e01 add :meta-merge-fn option 2022-08-11 07:55:15 -04:00
Peder Refsnes
4e14b1f05e Polish pedestal chains when printing context diffs 2022-07-19 22:33:28 +02:00
Ilshat Sultanov
25a051b003
Add support for fragment parameters in the reitit-frontend module
We have to process the fragment parameters due to the fact that the authorization server returns a callback in the following format:
`https://example.com/oauth/google/callback#access_token=foo&refresh_token=bar&provider_token=baz&token_type=bearer&expires_in=3600`

Links:
- https://www.rfc-editor.org/rfc/rfc6749#section-4.2
- https://www.rfc-editor.org/rfc/rfc6749#section-4.2.2
2022-06-15 23:38:51 +05:00
Fredrik Vaeng Røtnes
aeab5b96a6
Remove redundant s/and 2022-05-09 10:36:40 +00:00
Tommi Reiman
3dff4c84aa 0.5.18 2022-04-05 21:38:04 +03:00
Tommi Reiman
38d3fb0dda CHANGELOG 2022-04-05 21:37:42 +03:00
Tommi Reiman
4e40d3e2c9
Merge pull request #537 from frwdrik/master
Save three seq constructions
2022-04-05 21:34:56 +03:00
Tommi Reiman
e6bd4f2e57
Merge pull request #544 from antonmos/patch-2
update jackson-databind for CVE-2020-36518
2022-04-05 21:27:27 +03:00
Tommi Reiman
37d9d490ca
Merge pull request #521 from mthl/remove-goog-extend
Remove unused internal ‘goog-extend’ function
2022-04-05 21:24:24 +03:00
Tommi Reiman
98944a2b50
Merge pull request #547 from zackteo/patch-1
Balance parenthesis in docs
2022-04-05 21:23:18 +03:00
Tommi Reiman
b12c433652
Merge pull request #549 from metosin/on-coercion-error
on-coercion-error
2022-04-05 21:23:04 +03:00
Tommi Reiman
40bf16857f doc the option 2022-04-05 21:22:36 +03:00
Tommi Reiman
b282e32b73 . 2022-04-05 18:43:34 +03:00
Tommi Reiman
1ecfd1ae02 CHANGELOG, pass match too 2022-04-05 17:55:09 +03:00
Tommi Reiman
9e4b420fc8 on-coercion-error 2022-04-05 17:33:25 +03:00
Anton Mostovoy
ea38b60b28
update jackson deps per most recent recommendation
see https://github.com/FasterXML/jackson-databind/issues/2816#issue-678075273
2022-03-31 16:31:42 -05:00
Zachary Teo
053ac5b961 Balance parenthesis in docs 2022-03-30 21:36:05 +08:00
Anton Mostovoy
54ae50525c
update jackson-databind for CVE-2020-36518
See https://github.com/FasterXML/jackson-databind/issues/2816

See https://github.com/FasterXML/jackson-databind/compare/jackson-databind-2.13.2...jackson-databind-2.13.2.1
2022-03-25 13:21:20 -05:00
Fredrik Vaeng Røtnes
43d6f52208
Update spec.cljc
Stray character
2022-03-18 15:55:20 +00:00
Tommi Reiman
f0405adc02 0.5.17 2022-03-10 20:15:43 +02:00
Tommi Reiman
ae73d031b9
Merge pull request #539 from metosin/fix-538
fix #538
2022-03-10 15:34:05 +02:00
Tommi Reiman
650ff3d6b3 . 2022-03-10 15:23:07 +02:00
Tommi Reiman
b0602d60c9 one more time 2022-03-10 15:21:15 +02:00
Tommi Reiman
f9841363c5 faster impl, removes all intermediate steps + cleanup 2022-03-10 14:21:39 +02:00
Tommi Reiman
1e5fb601da fix #538 2022-03-10 13:42:53 +02:00
Fredrik Vaeng Røtnes
7033abc530 Save three seq constructions 2022-03-05 01:46:03 +01:00
Tommi Reiman
acfc48faa1 0.5.16 2022-02-15 16:57:07 +02:00
Tommi Reiman
382661a6c4 0.5.16 2022-02-15 16:55:08 +02:00
Tommi Reiman
a2150436d3
Merge pull request #531 from metosin/malli-lite-example
Malli lite example
2022-02-15 15:30:47 +02:00
Tommi Reiman
20b37b7d43 lite-it! 2022-02-15 13:50:35 +02:00
Tommi Reiman
0ac3a14805 initial commit 2022-02-15 13:37:12 +02:00
Tommi Reiman
0a872d8f3a 0.5.16-SNAPSHOT 2022-02-14 17:52:32 +02:00
Tommi Reiman
2f31f658cc malli 0.8.2 2022-02-14 17:48:51 +02:00
Tommi Reiman
198cfda00d
Merge pull request #530 from metosin/re-format
Re-format
2022-02-14 17:41:30 +02:00
Tommi Reiman
f3dee769fb format-ns 2022-02-14 16:59:20 +02:00
Tommi Reiman
2aba5610c7 format 2022-02-14 16:58:10 +02:00
Tommi Reiman
45fbe5eaa2
Merge pull request #529 from metosin/malli-lite
Add support for malli-lite
2022-02-14 16:55:03 +02:00
Tommi Reiman
40eb09b2a9 fix CHANGELOG 2022-02-14 16:45:14 +02:00
Tommi Reiman
d0f7126491 add support for malli-lite 2022-02-14 16:45:14 +02:00
Tommi Reiman
88170bc495
Merge pull request #528 from metosin/bumpup
Update deps & format files
2022-02-14 16:44:43 +02:00
Tommi Reiman
060e593414 dev guide 2022-02-12 22:42:10 +02:00
Tommi Reiman
bdcb1eb5b1 clean-ns 2022-02-12 22:35:27 +02:00
Tommi Reiman
5d4c886d35 format 2022-02-12 22:34:26 +02:00
Tommi Reiman
b7cc420fde update deps 2022-02-12 22:30:42 +02:00
Juho Teperi
07c39fc2df Setup clj-kondo files for each module
Clojure-lsp will run clj-kondo on the module folders, so those modules
should refer back to root configuration. Clj-kondo itself works the
same, if running from a module directory. Some editor plugins might run
clj-kondo on the working directory always (reitit root folder usually),
and in those cases this isn't needed.
2022-01-24 12:39:42 +02:00
Juho Teperi
5973bc0f26
Merge pull request #523 from prestancedesign/patch-1
Replace with when-let since no else branches
2021-12-26 21:15:41 +02:00
Juho Teperi
ad90a2788e The goog-extend macro isn't used
Reitit.frontend.history was rewritten in 08156f6a6d
2021-12-22 21:49:02 +02:00
Michael Salihi
673f6dec72
Replace with when-let since no else branches 2021-12-09 10:48:59 +01:00
Mathieu Lirzin
fce79e903b
Remove unused internal ‘goog-extend’ function 2021-12-04 18:06:51 +01:00
Tommi Reiman
93e288bf34
Merge pull request #518 from 15joeybloom/patch-1
Fix middleware ordering in ring example
2021-11-15 20:18:48 +02:00
Joey Bloom
53c2e4ed6b
Fix middleware ordering in ring example 2021-11-08 17:27:01 -06:00
Juho Teperi
5bd4aec775 Update changelog 2021-11-03 13:01:22 +02:00
Juho Teperi
22461f638e
Merge pull request #507 from metosin/feature/improve-frontend-docs
Improve parameter names and docstrings on reitit frontend functions
2021-11-03 12:53:54 +02:00
Juho Teperi
9eded42ba1 Improve frontend docstrings 2021-11-03 12:53:27 +02:00
Juho Teperi
a7f3cd15ab
Merge pull request #481 from metosin/swagger-param-description-examples
Swagger param description examples
2021-11-03 12:22:51 +02:00
Juho Teperi
f537be56b7
Merge pull request #510 from devurandom/patch-1
Link to #134 from parameters-middleware refactoring note
2021-11-03 12:19:15 +02:00
Juho Teperi
153eaaf2b0 Merge branch 'zengxinhui-patch-2' 2021-11-03 12:17:54 +02:00
zengxinhui
59f4bf285a duplicate r/routes output
Perhaps this is not needed as the same output is shown above close.
Or the `nil`s need to be removed to match actual output. See PR513 https://github.com/metosin/reitit/pull/513
2021-11-03 12:17:30 +02:00
Juho Teperi
9f798c63c9 Reorder some example code 2021-11-03 12:16:42 +02:00
Juho Teperi
143bf06290
Merge pull request #515 from zengxinhui/patch-1
update r/routes output
2021-11-03 12:14:59 +02:00
Juho Teperi
c0ff024b05
Merge pull request #516 from prestancedesign/patch-1
Replace if-let by when-let since no else branches
2021-11-03 12:14:48 +02:00
Michael Salihi
6c835e6e09
Replace if-let by when-let since no else branches
for the recursive match by path example.
2021-10-13 18:50:20 +02:00
zengxinhui
f3a686026f
update r/routes output
`nil` should not be there.
2021-10-13 02:54:40 -04:00
Tommi Reiman
751ba17b75
Merge pull request #513 from zengxinhui/patch-1
update r/routes output
2021-10-13 09:54:04 +03:00
zengxinhui
04225b0eff
update r/routes output
The `nil`s are not there and need to be removed.
2021-10-13 02:41:07 -04:00
Miikka Koskinen
d0f1abe115
Merge pull request #512 from miikka/add-extra-test
Add a test for `/{foo}.html` style routes
2021-10-07 08:41:24 +03:00
Miikka Koskinen
0223fbc091
Merge pull request #511 from mike706574/patch-1
Fix typo in Swagger doc
2021-10-06 20:56:03 +03:00
Miikka Koskinen
9160aa0f2b Add a test for /{foo}.html style routes
Just like path `///` would not match `/{foo}/`, `/..html` does not match
`/{foo}.html`.
2021-10-06 20:42:09 +03:00
mike
1737e19214
Fix typo in Swagger doc 2021-10-05 12:51:21 -05:00
Miikka Koskinen
a83285f39d
Update README.md 2021-10-01 11:03:48 +03:00
Dennis Schridde
ef0aeaa7c4
Link to #134 from parameters-middleware refactoring note 2021-10-01 01:44:36 +02:00
Juho Teperi
ac68f0f726 Improve parameter names and docstrings on reitit frontend functions 2021-09-07 14:38:42 +03:00
Tommi Reiman
8694d312f8 0.5.15 2021-08-05 18:46:29 +03:00
Tommi Reiman
db84daca95 0.5.14 2021-08-03 13:41:27 +03:00
Tommi Reiman
f8a8930375 Fix CHANGELOG 2021-08-03 13:37:01 +03:00
Tommi Reiman
10747acb00 Merge branch 'master' of github.com:metosin/reitit 2021-08-03 13:36:09 +03:00
Tommi Reiman
fd12b3c6f2 CHANGELOG 2021-08-03 13:35:46 +03:00
Tommi Reiman
2747428dea
Merge pull request #489 from metosin/fix-resource-handler-url-decoding
Handle URL-encoded paths in file and resource handlers
2021-08-03 13:34:06 +03:00
Tommi Reiman
38f2bd4812
Merge branch 'master' into fix-resource-handler-url-decoding 2021-08-03 13:33:39 +03:00
Tommi Reiman
5486174722
Merge pull request #501 from metosin/malli-fix
Fix malli encoding & update deps
2021-08-03 13:29:53 +03:00
Tommi Reiman
57e1d1668d
Merge pull request #495 from nextjournal/pedestal-enrich-default-request
Enrich request for pedestal/routing-interceptor default-queue
2021-08-03 13:29:11 +03:00
Tommi Reiman
020c424b4e dead code, CHANGELOG 2021-08-03 13:24:29 +03:00
Tommi Reiman
20b7cabed7 Fix Malli encoding,, #498 2021-08-03 08:46:51 +03:00
Tommi Reiman
33afa9f999 update deps 2021-08-03 08:46:29 +03:00
Tommi Reiman
7a1cc78a80
Merge pull request #500 from branch14/improve-some-docs
Improve some docs
2021-07-27 21:22:05 +03:00
Phil Hofmann
478ee18a32 improve some docs 2021-07-27 18:33:17 +02:00
Dieter Komendera
056c70d269 Enrich request for pedestal/routing-interceptor default-queue
This ensures requests handled by the default queue also have
access to the router per the injected :reitit.core/router key
on the request.
2021-06-24 14:07:23 +02:00
Miikka Koskinen
9ba73524f3
Update CHANGELOG.md
Co-authored-by: Ilmo Raunio <ilmoraunio@users.noreply.github.com>
2021-06-21 09:15:06 +03:00
Timo Kramer
38ec679207 Extend the docs to mention the operationID 2021-05-29 11:48:57 +02:00
Tommi Reiman
de5bdeba19
Merge pull request #493 from prestancedesign/patch-1
Add new SPA application example link
2021-05-26 07:09:14 +03:00
Michael Salihi
287a01d4f3
Add new SAP application example link 2021-05-25 23:12:15 +02:00
Miikka Koskinen
6202e802c9 Update CHANGELOG.md 2021-04-30 14:51:10 +03:00
Miikka Koskinen
1297cfd902 Handle URL-encoded paths in file and resource handlers 2021-04-30 14:38:18 +03:00
Miikka Koskinen
f212edfcd6
Merge pull request #488 from rlovtangen/patch-1
Update content_negotiation.md
2021-04-30 13:41:06 +03:00
Ronny Løvtangen
1494641ab8
Update content_negotiation.md
Not 100% sure, but looks like it should be :date-format, not :data-format here, based on the example in the section above.
2021-04-24 22:20:16 +02:00
Miikka Koskinen
caa571cd66
Update development.md 2021-04-23 17:55:47 +03:00
Miikka Koskinen
2638041e0c
Update development.md 2021-04-23 17:53:53 +03:00
Miikka Koskinen
51a26ed052 Release 0.5.13 2021-04-23 17:41:40 +03:00
Miikka Koskinen
1b74373911 Use explicit :refers 2021-04-23 17:15:35 +03:00
Miikka Koskinen
1b583c1cc2 Remove operation-id uniqueness check
Let's leave that for other tools for now.
2021-04-23 17:02:17 +03:00
Tommi Reiman
40efc2d9d4
Merge pull request #487 from audriu/patch-1
Fix Ring example
2021-04-22 20:51:34 +03:00
Audrius
522356ba71
Fix for the PR comments 2021-04-22 10:05:02 +03:00
Audrius
02004cac8d
Fix Ring example
Maybe it is not perfect solution but at least it works. It was somewhat weird that the front page of the lib contains non working examples.
2021-04-22 09:27:07 +03:00
Tommi Reiman
1ab075bd35
Merge pull request #485 from ZaymonFC/patch-1
Mention Malli in introduction under coercion
2021-04-12 07:20:55 +03:00
Zaymon
388f825d4d
Update README.md 2021-04-12 13:39:15 +10:00
Miikka Koskinen
ac9ff806df
Merge pull request #476 from metosin/feature/update-static-handler-docs
Document serving static resources from the file system
2021-04-09 14:55:19 +03:00
Miikka Koskinen
d0e83fed41 Merge remote-tracking branch 'origin/master' into feature/update-static-handler-docs 2021-04-09 14:49:56 +03:00
Miikka Koskinen
b8110f1e40
Merge pull request #482 from keoko/fix-typos
fix documentation typos
2021-04-09 14:49:09 +03:00
Natxo Cabré
722bd8950f removed blank line 2021-03-12 13:33:47 +01:00
Natxo Cabré
1e76ea8114 fixed malli create example 2021-03-12 13:24:28 +01:00
Natxo Cabré
26c4ebc645 defined router var so the merged route tree example returns the commented data structure 2021-03-12 12:57:13 +01:00
Natxo Cabré
c5bb467402 fix route_conflicts link 2021-03-12 12:50:51 +01:00
Natxo Cabré
5831f2f4f6 fixed typo in explicitely 2021-03-12 12:45:55 +01:00
Juho Teperi
ddbbe1a0da List different examples 2021-03-11 22:08:07 +02:00
Juho Teperi
a3aa5df111 Fix exception log handler, *out* is not PrintWriter necessarily 2021-03-11 21:57:15 +02:00
Juho Teperi
c212a7a9fa Ring-swagger is not Schema example 2021-03-11 21:56:22 +02:00
Juho Teperi
1992198b58 Fix spec example 2021-03-11 21:22:33 +02:00
Juho Teperi
8a205002a8 Add examples for Swagger parameter description 2021-03-11 20:55:52 +02:00
Tommi Reiman
349c838de4
Merge pull request #478 from jmckitrick/patch-1
Update swagger_ui.cljc
2021-03-11 15:56:25 +02:00
Jonathon McKitrick
c033f10e4e
Update swagger_ui.cljc
Fix spelling in docstring.
2021-03-11 08:41:31 -05:00
Miikka Koskinen
fb5eb1ff98
Update development.md 2021-03-05 20:45:28 +02:00
Tommi Reiman
7520cd425c malli 0.3.0 2021-03-02 21:25:02 +02:00
Miikka Koskinen
8567550ad9 Document serving static resources from the file system 2021-03-02 08:39:51 +02:00
Miikka Koskinen
6f2e181335
Merge pull request #473 from metosin/feature/finish-not-found
Make the not-found-handler fix backwards compatible
2021-03-02 08:38:54 +02:00
Miikka Koskinen
b6d9707b53 Copy file & resource handler docstring update to docs 2021-03-02 08:28:36 +02:00
Tommi Reiman
0c93cfb1f9 update deps 2021-02-27 19:22:42 +02:00
Miikka Koskinen
1f18cbe747
Update CHANGELOG.md 2021-02-26 08:46:01 +02:00
Miikka Koskinen
39fc17c5fd
Update CHANGELOG.md 2021-02-26 08:45:29 +02:00
Miikka Koskinen
5824d9eeef Make the not-found-handler fix backwards compatible
PR #471 aimed to fix issue #464. However, the change was slightly
backwards-incompatible, since it made the file and resource handlers use
the default 404 handler when mounted outside of the router. The previous
behavior was to return nil in that case.

This patch restores the previous behavior and clarifies that `:path`
option can be used only when the file/resource handler is mounted
outside of a router.
2021-02-26 08:14:01 +02:00
Miikka Koskinen
88897a2264 Remove useless testing calls 2021-02-26 07:41:03 +02:00
Tommi Reiman
07dfb91d96
Merge pull request #472 from metosin/malli-coercion-doc-fix
Missing curly brace in documentation
2021-02-21 09:18:36 +02:00
Kari Marttila
60fee31733 Missing curly brace in documentation - I noticed this while trying the example in REPL 2021-02-20 21:00:59 +02:00
Miikka Koskinen
ff55f85677
Merge pull request #471 from metosin/resource-handler-issue
Support not-found-handler with path in resource handler (#464)
2021-02-19 15:31:55 +02:00
Kari Marttila
902b33f004 Support not-found-handler with path in resource handler (#464) 2021-02-19 15:17:26 +02:00
Miikka Koskinen
e64490f473
Merge pull request #470 from metosin/feature/migrate-to-github-actions
Migrate to GitHub Actions
2021-02-17 16:31:04 +02:00
Miikka Koskinen
d80f1038f6 Stop using CircleCI 2021-02-17 16:22:17 +02:00
Miikka Koskinen
a94b6db06e Start using GitHub Actions 2021-02-17 16:22:01 +02:00
Tommi Reiman
03f029e536 mob rules 2021-02-04 20:48:08 +02:00
Tommi Reiman
f43a8311fc 0.5.12 2021-02-04 20:40:25 +02:00
Tommi Reiman
e7cec06b71
Merge pull request #469 from ykarikos/feature/upgrade-dependencies
Upgrade Clojure dependencies
2021-02-04 20:09:25 +02:00
Yrjö Kari-Koskinen
d478ec1d23
Upgrade dependencies 2021-02-04 08:41:33 +02:00
Miikka Koskinen
36673d4674
Update CHANGELOG.md 2021-01-29 15:08:44 +02:00
Miikka Koskinen
eb10af92c4
Merge pull request #466 from metosin/feature/space-as-separator
Allow space as separator, fixes #411
2021-01-29 15:08:35 +02:00
Kimmo Koskinen
496e6b6fc7 Allow space as separator, fixes #411
Fixes https://github.com/metosin/reitit/issues/411
2021-01-29 15:02:35 +02:00
Tommi Reiman
0ecc2fb9a7
Merge pull request #456 from raphaelsaunier/fix/relative-urls-in-doc
Fix relative links in documentation
2021-01-10 18:10:53 +02:00
Tommi Reiman
8cca1fe40b
Merge pull request #460 from scott-silver/patch-1
Pedantic grammer correction
2020-12-28 00:00:44 +02:00
Tommi Reiman
b4099169be 0..5.11 with updated deps 2020-12-27 22:50:02 +02:00
scott-silver
d663342fbb
Update README.md 2020-12-27 10:31:48 -08:00
Tommi Reiman
8be1b16cc7
Merge pull request #458 from pariyatti/restful_forms_doc_fix
Downcase hidden methods in RESTful example
2020-12-20 21:53:10 +02:00
Juho Teperi
12d3dcabec Fix #457, fix reflection warning in reitit.ring 2020-12-19 19:50:35 +02:00
Steven Deobald
7fb720ef36 Handle nil for lower-case
- when "_method" is actually empty, we need to avoid trying to
  do string manipulation on nil from params.
2020-12-15 19:29:50 -06:00
Steven Deobald
a3b251449b Downcase hidden methods in RESTful example
- this documentation is mildly confusing when combined with hiccup's
  `form-to` since hiccup forcibly transforms the method specified in
  its convenience syntax: `(form-to [:delete "/my-url"] ... )` into an
  upper-case string:
  80e48352dd/src/hiccup/form.clj (L130)

- it's also common to get an upper-case string from elsewhere so it
  seems best to wrap the hidden `_method` in `lower-case`.
2020-12-15 15:48:00 -06:00
Raphaël Saunier
c539c53ae2 Fix broken links in documentation 2020-12-01 10:00:20 +01:00
Timo Kramer
e095cd2efa Support operationId in reitit-swagger
OpenAPI Specification allows the operationId to be added to the
"Operation Object" alongside e.g. summary and description. This
commit introduces the support of this element in the
reitit-swagger module and extends the tests. One test shows the
correct use of operationId where both are distinct and one
shows the failing of the swagger creation when the IDs are not
distinct.

- Spec: https://swagger.io/specification/#operation-object
- Adds the support for operationId
- Adds operationId in two places of the swagger test
- Adds a test that checks exception on duplicate IDs
- Closes #451
2020-11-25 18:38:35 +01:00
Tommi Reiman
3a6985eb71
Merge pull request #448 from teodorlu/docs-coercion-malli
Link to docs for Malli coercion
2020-11-01 17:55:50 +02:00
Teodor Heggelund
d1b8e1b98b Link to docs for Malli coercion 2020-11-01 16:49:03 +01:00
Tommi Reiman
fb771a33bd
Merge pull request #446 from zelark/update-handler
Update create-swagger-ui-handler
2020-10-22 17:23:07 +03:00
Aleksandr Zhuravlev
2fc893bf6f Update examples 2020-10-22 17:03:14 +03:00
Aleksandr Zhuravlev
abfe810700 Update link to config params doc 2020-10-22 15:18:16 +03:00
Aleksandr Zhuravlev
bf3242a6f4 Get rid of conf.js (not used anymore) 2020-10-22 15:16:55 +03:00
Tommi Reiman
e27d5c0213 CHANGELOG 2020-10-22 00:13:39 +03:00
Tommi Reiman
0b2c4d0d8e 0.5.10 2020-10-22 00:10:52 +03:00
Tommi Reiman
dbf3751815 0.5.9 2020-10-19 10:46:38 +03:00
Tommi Reiman
f2f7d3a428
Merge pull request #438 from metosin/frontend-fixes
Frontend fixes
2020-10-19 10:42:45 +03:00
Tommi Reiman
ff647f3a2c 0.5.8 2020-10-19 08:31:29 +03:00
Tommi Reiman
422e97ffed Update CHANGELOG 2020-10-19 08:29:53 +03:00
Tommi Reiman
c3df762b48
Merge pull request #444 from dawran6/376-route-data-spec-for-conflicting
Add :conflicting to route data spec
2020-10-19 08:08:38 +03:00
Daw-Ran Liou
c0a76c6648 Add :conflicting to route data spec 2020-10-19 03:02:18 +08:00
Tommi Reiman
8c3ad99276 0.5.7 2020-10-18 20:43:24 +03:00
Tommi Reiman
9ce7e6593c malli 0.2.0, update docs & tune 2020-10-18 20:22:01 +03:00
Tommi Reiman
b4d1b36c8d
Merge pull request #442 from metosin/params-to-parameters
Update nested controllers example, :params -> :parameters
2020-10-18 19:19:44 +03:00
Tommi Reiman
21df83c804
Merge pull request #443 from skibe/resource-handler-path-size-fix
Fix resource handler path matching
2020-10-16 11:24:48 +03:00
Kimmo Rantala
2ed5b48067 Fix resource handler path matching
File/resource handler checks that uri actually matches to path instead
of comparing just path length to uri length.
2020-10-15 23:33:20 +03:00
Juho Teperi
845240d330
Merge branch 'master' into frontend-fixes 2020-10-15 09:49:14 +03:00
Kimmo Koskinen
b51374fec2
Update example, :params -> :parameters 2020-10-14 10:08:20 +03:00
Tommi Reiman
d57851c2d3
Merge pull request #441 from PrestanceDesign/patch-1
One more Reitit project example
2020-10-14 10:06:04 +03:00
Michael Salihi
61945adddf
One more Reitit project example
Adding on todo backend with Reitit on https://www.todobackend.com/ website.
2020-10-13 23:47:10 +02:00
Tommi Reiman
d7ed4f32af Merge branches 'master' and 'master' of github.com:metosin/reitit 2020-10-12 10:17:13 +03:00
Tommi Reiman
15812930a3 Update deps 2020-10-12 10:17:07 +03:00
Tommi Reiman
f037b803b6
Merge pull request #439 from nate/patch-1
Fixing broken links to documentation in README
2020-10-02 12:43:23 +03:00
Nate Sutton
5825694153
Fixing broken links to documentation in README
A number of links were broken in the topmost link section of the README and this commit fixes them.
2020-10-01 18:53:32 -05:00
Tommi Reiman
39ec264da8
Merge pull request #417 from Koura/issue-217-consume-form-params
Issue 217 consume form params
2020-09-30 12:25:52 +03:00
Juho Teperi
c4afca9417 Update CHANGELOG.md 2020-09-26 16:54:35 +03:00
Juho Teperi
58f9871747 Test rfe start! 2020-09-26 16:51:19 +03:00
Juho Teperi
5651b4648a Fix reitit.frontend.easy not correctly removing old event listeners
In 0.5.0 rfe start! fn started using first on-navigate callback to get
the reference to History instance, so that user on-navigate can use rfe
functions that need this reference. History implementations called
on-navigate with the instance without event listeners set, so when stop!
was called listeners weren't removed.
2020-09-26 16:51:19 +03:00
Juho Teperi
390cdb1e4e Rename some re-frame effects/events 2020-09-26 16:51:19 +03:00
Juho Teperi
0c85b3d54b Reindent re-frame example code 2020-09-26 16:51:19 +03:00
Juho Teperi
dea8894610 Clean re-frame example initialization 2020-09-26 16:51:19 +03:00
Tommi Reiman
208eeeb5c5 0.5.6 2020-09-26 16:09:49 +03:00
Tommi Reiman
5e290db106 0.5.6 2020-09-26 16:08:34 +03:00
Tommi Reiman
b809b6328d
Merge pull request #427 from Koura/update/ring-malli-swagger-example
Fix: broken ring-malli-swagger example
2020-09-26 12:13:26 +03:00
Tommi Reiman
03c5b04384
Merge branch 'master' into update/ring-malli-swagger-example 2020-09-26 12:13:18 +03:00
Tommi Reiman
fbff819909 update deps 2020-09-26 12:11:14 +03:00
Tommi Reiman
48e0c7f883
Update README.md 2020-09-20 18:00:04 +03:00
Tommi Reiman
21fbad22a0
Merge pull request #436 from dharrigan/feat/startrek-example
Add in another usage example.
2020-09-20 17:58:57 +03:00
Tommi Reiman
f95016ad4e
Update README.md 2020-09-20 17:58:37 +03:00
David Harrigan
755bc131de Add in another usage example. 2020-09-19 20:39:48 +01:00
Tommi Reiman
31cee66548
Merge pull request #435 from PrestanceDesign/patch-1
New section with example repositories using Reitit
2020-09-18 18:18:10 +03:00
Michael Salihi
2d16c2f172
New section with example repositories using Reitit 2020-09-18 17:01:22 +02:00
Tommi Reiman
720b1f9524
Merge pull request #433 from perttikellomaki/patch-1
Update basics.md
2020-09-07 15:06:11 +03:00
Pertti Kellomäki
8ae0a4ccd4
Update basics.md
My pet peeve ;-)
Few = hardly any.
A few  = a couple
Some = a few or maybe more.
2020-09-04 21:49:56 +03:00
Tommi Reiman
2f17f09856
Merge pull request #428 from nivekuil/patch-1
Fix rfe/start! example arity
2020-08-17 10:42:11 +03:00
Kevin Liu
218cd4be69
Fix rfe/start! example arity
The second arg should be the on-navigate callback and the opts should be the third arg.
2020-08-16 19:54:20 -07:00
tjalkane
d2bc642ac6 Remove redundant malli.core/walk call 2020-08-05 22:05:52 +03:00
tjalkane
37fc06b081 Fix: broken ring-malli-swagger example
Change example to use recently changed `m/accept` -> `m/walk` and
`m/schema-visitor` -> `m/schema-walker`
2020-08-04 21:26:58 +03:00
Miikka Koskinen
afd0af83be Point to cljdoc for documentation
Fixing all the links in CHANGELOG.md would have been too much work, so I
just left them as-is. I've converted the pages under
https://metosin.github.io/reitit/ to redirects, so the links should
work anyway.

Closes https://github.com/metosin/reitit/issues/426
2020-07-28 13:15:10 +03:00
Miikka Koskinen
612555cdc0 CircleCI: Disable build-docs
See https://github.com/metosin/reitit/issues/426 for background.
2020-07-24 14:14:02 +03:00
Miikka Koskinen
aa833ac200
Merge pull request #418 from Koura/fix-docs-wording
Improve wording for interceptors documentation
2020-07-24 14:02:50 +03:00
Miikka Koskinen
486e3928c5 examples/buddy-auth: README improvements and dep upgrade 2020-07-24 13:51:10 +03:00
Miikka Koskinen
9affebbb73
Merge pull request #419 from vharmain/examples/buddy-auth
Add Buddy example
2020-07-24 13:42:44 +03:00
Miikka Koskinen
9fe64a333f Update copyright year 2020-07-24 13:06:20 +03:00
Miikka Koskinen
11a419d8b9 examples/fronted: Upgrade dependencies
Fixes https://github.com/metosin/reitit/issues/423
2020-07-24 13:05:57 +03:00
Tommi Reiman
43e1a520d6 0.5.5 2020-07-15 11:28:11 +03:00
Tommi Reiman
274d527d2f 0.5.4 (with latest malli) 2020-07-13 11:08:19 +03:00
Valtteri Harmainen
d3c21e3c6c Add Buddy example 2020-07-11 13:35:43 +03:00
tjalkane
807b75f7c0 Improve wording for interceptors documentation 2020-07-10 15:46:28 +03:00
tjalkane
ce635609c7 Omit swagger-data for muuntaja middleware/interceptor
Leave out swagger-data when `:form`, but no `:body` parameters, are
defined. If swagger-data is not omitted, order of parameters and
muuntaja middleware will matter.
2020-07-10 09:52:20 +03:00
Tommi Reiman
56cbd67abd 0.5.3 2020-07-09 22:06:04 +03:00
Tommi Reiman
e6e6bd3e27 update malli 2020-07-09 22:05:10 +03:00
Tommi Reiman
06cc5090d5 Update deps 2020-07-09 22:00:05 +03:00
tjalkane
6703a02baa Publish Swagger :consumes for parameters-interceptor
Make parameters-interceptor automatically publish swagger data for
endpoints when :form, but not :body params, are present.
2020-07-08 10:18:57 +03:00
tjalkane
15df0c5005 Automatically publish Swagger :consumes for :form params
Make parameters-middleware publish swagger data for endpoints when
:form, but not :body params,
are present.
2020-07-08 10:07:43 +03:00
Tommi Reiman
0ffef07cbf
Merge pull request #415 from Koura/cleanup-test-namespaces
Cleanup test namespaces
2020-06-26 10:44:11 +03:00
tjalkane
c1c3cd26b4 Remove duplicate require of schema.core 2020-06-26 00:25:09 +03:00
tjalkane
87d7e2e3c9 Fix: rename a test to make it run
The renamed test is redeclared later in the same namespace
causing the original one to not be run with `lein test`.
2020-06-26 00:16:06 +03:00
Tommi Reiman
9ca88d7165
Merge pull request #414 from Koura/issue-375-remove-top-level-tags
Replace top-level `:tags` with `[:swagger :tags]` path in documentation
2020-06-25 10:56:27 +03:00
tjalkane
0cfe5b4ea5 Replace top-level :tags with [:swagger :tags] path in documentation 2020-06-24 23:11:23 +03:00
Tommi Reiman
cf45940715 update malli 2020-06-11 20:28:49 +03:00
Tommi Reiman
3a73ddab51
Merge pull request #412 from rschmukler/rschmukler/malli-fix
fix: coercion.malli use m/type
2020-06-11 20:14:55 +03:00
Ryan Schmukler
962ffc6fb5
fix: coercion.malli use m/type
Renames `m/name` to `m/type` per the breaking change introduced in malli
4880734d554511f16fee9c1d28a9d340c8b632c1.
2020-06-11 10:20:50 -04:00
Tommi Reiman
2ff255eb89
Merge pull request #410 from jhacksworth/fix-link-in-default-middleware-doc
Fix link in default_middleware.md
2020-06-03 14:08:52 +03:00
J Hacksworth
bb4a7502fb Fix link in default_middleware.md
"Content Negotation" was moved to its own page. This commit adds a
section with a link to the new page, similar to the way the "Exception
Handling" section is presented.
2020-06-02 10:45:26 -04:00
Tommi Reiman
e30e739a15 0.5.2 2020-05-27 08:32:38 +03:00
Tommi Reiman
aa3d38e971 Remove unneeded default responses from examples 2020-05-27 08:31:02 +03:00
Tommi Reiman
da7cef232c Update CHANGELOG 2020-05-26 22:39:51 +03:00
Tommi Reiman
681c994f7f Test that middleware swagger injections work 2020-05-26 22:35:43 +03:00
Tommi Reiman
f2e0470ecd
Merge pull request #408 from metosin/PR
Better malli-coercion
2020-05-26 22:03:45 +03:00
Tommi Reiman
4e9537cecf
Merge pull request #404 from piotr-yuxuan/master
[0.5.1] Ass default :responses for Swagger-UI
2020-05-26 21:33:18 +03:00
Tommi Reiman
57da6fa5ad optimized http-coercion 2020-05-26 21:32:26 +03:00
Tommi Reiman
e649ed22b9 New options for malli coercion 2020-05-26 08:09:35 +03:00
Tommi Reiman
f41006c8bb just validation 2020-05-25 23:50:27 +03:00
Tommi Reiman
ea5ec93793 faster malli coercion 2020-05-25 21:54:55 +03:00
Tommi Reiman
a2d8208600 Fix #407 2020-05-25 21:54:27 +03:00
Tommi Reiman
28962e75df Update malli 2020-05-25 21:53:40 +03:00
Tommi Reiman
9e42140a28
Merge pull request #356 from MattiNieminen/bugfix/355-add-missing-dependency
Add missing spec-tools dependency to reitit-middleware. Fixes #355
2020-05-25 16:48:23 +03:00
Miikka Koskinen
e27f2449b9
Merge pull request #405 from xificurC/patch-2
fix negation -> negotiation middleware
2020-05-22 16:36:08 +03:00
Peter Nagy
efe07e3d5d
fix negation -> negotiation middleware
that was a funny typo!
2020-05-21 22:19:32 +02:00
piotr-yuxuan
6e332e46f1
Low-priority default :responses in Swagger spec
According to meta-merge documentation, ^:displace is a good solution
for default values. This is still inside the meta-merge, as I don't
see where else I could put it. Perhaps a better solution is available.
2020-05-20 22:45:11 +01:00
piotr-yuxuan
3e8eaa48d7
Fix default description
https://github.com/metosin/reitit/issues/403
2020-05-19 23:42:56 +01:00
Tommi Reiman
50f0120e8c 0.5.1 2020-05-18 15:00:28 +03:00
Tommi Reiman
9a0395837c Fix #402 2020-05-18 14:52:41 +03:00
Matti Nieminen
20a3864482 Add missing spec-tools dependency to reitit-middleware. Fixes #355 2020-01-31 12:19:53 +02:00
230 changed files with 11927 additions and 6056 deletions

View file

@ -1,130 +0,0 @@
version: 2.1
jobs:
test-clj:
working_directory: ~/test
parameters:
image-name:
type: string
docker:
- image: << parameters.image-name >>
steps:
- checkout
- restore_cache:
keys:
- 'v1-clj-{{ checksum "project.clj" }}'
- 'v1-clj-'
- 'v1-test-'
- run:
name: Install modules
command: ./scripts/lein-modules install
- run:
name: Run tests
command: ./scripts/test.sh clj
- run:
name: Install curl if missing
command: apt update && apt install -y curl
- run:
name: Verify cljdoc.edn
command: curl -fsSL https://raw.githubusercontent.com/cljdoc/cljdoc/master/script/verify-cljdoc-edn | bash -s doc/cljdoc.edn
- store_test_results:
# path must be a directory under which there a subdirectories that
# contain the JUnit XML files.
path: ~/test/target/results
# - run:
# name: Run coverage
# command: ./scripts/submit-to-coveralls.sh clj
- save_cache:
key: 'v1-clj-{{ checksum "project.clj" }}'
paths:
- ~/.m2
- ~/.cljs/.aot_cache
test-cljs:
working_directory: ~/test
docker:
- image: circleci/clojure:lein-2.8.1-node-browsers
steps:
- checkout
- restore_cache:
keys:
- 'v1-cljs-{{ checksum "project.clj" }}-{{ checksum "package.json" }}'
- 'v1-cljs-'
- run:
name: Install npm dependencies
command: npm install
- run:
name: Install modules
command: ./scripts/lein-modules install
- run:
name: Run tests
command: ./scripts/test.sh cljs
- store_test_results:
path: ~/test/target/results
- save_cache:
key: 'v1-cljs-{{ checksum "project.clj" }}-{{ checksum "package.json" }}'
paths:
- ~/.m2
- ~/test/node_modules
build-docs:
working_directory: ~/build
docker:
- image: circleci/node:latest
steps:
- checkout
- run: rm package.json package-lock.json
- restore_cache:
keys:
- 'v1-gitbook-{{ checksum "book.json" }}'
- 'v1-gitbook-'
- run:
name: "Install GitBook"
command: npm install gitbook-cli && ./node_modules/.bin/gitbook install
- run:
name: "Clone gh-pages"
command: git clone --branch gh-pages git@github.com:metosin/reitit.git ~/gh-pages
- run:
name: Build the documentation
command: |
./node_modules/.bin/gitbook build
cp -r _book/* ~/gh-pages/
- add_ssh_keys:
fingerprints:
- "2d:eb:be:af:53:33:36:01:40:61:81:9d:76:84:8e:83"
- deploy:
name: Upload the documentation
command: |
cd ~/gh-pages
git config user.name "Automatic build"
git config user.email "noreply@metosin.fi"
git add -A
git commit -m "Build book from commit $CIRCLE_SHA1 [skip ci]"
git push
- save_cache:
key: 'v1-gitbook-{{ checksum "book.json" }}'
paths:
- node_modules
workflows:
version: 2
test-and-build-docs:
jobs:
- test-clj:
name: jdk8
image-name: clojure:openjdk-8-lein-2.9.1
- test-clj:
name: jdk11
image-name: clojure:openjdk-11-lein-2.9.1
- test-clj:
name: jdk13
image-name: clojure:openjdk-13-lein-2.9.1
- test-clj:
name: jdk14
image-name: clojure:openjdk-14-lein-2.9.1
- test-cljs
- build-docs:
filters:
branches:
only:
- master

View file

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

View file

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

26
.github/workflows/release.yml vendored Normal file
View 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 }}"

118
.github/workflows/testsuite.yml vendored Normal file
View file

@ -0,0 +1,118 @@
name: testsuite
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
build-clj:
strategy:
matrix:
# Supported Java versions: LTS releases and latest
jdk: [11, 17, 21, 25]
clojure: [11, 12]
name: Clojure ${{ matrix.clojure }} (Java ${{ matrix.jdk }})
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache dependencies
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@v4
with:
distribution: temurin
java-version: ${{ matrix.jdk }}
- name: Setup Clojure
uses: DeLaGuardo/setup-clojure@13.1
with:
lein: 2.9.5
clj-kondo: 2025.12.23
# Install openapi-schema-validator for openapi-tests
# Uses node version from the ubuntu-latest
- name: Install dependencies
run: npm ci
- name: Run Linter
run: ./lint.sh
- name: Run tests
run: ./scripts/test.sh clj${{ matrix.clojure }}
build-cljs:
name: ClojureScript
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache dependencies
uses: actions/cache@v4
with:
path: |
~/.m2/repository
key: ${{ runner.os }}-cljs-${{ hashFiles('**/project.clj') }}
restore-keys: |
${{ runner.os }}-cljs-
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
- name: Setup Clojure
uses: DeLaGuardo/setup-clojure@13.1
with:
lein: 2.9.5
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Run tests
run: ./scripts/test.sh cljs
lint:
name: Lint cljdoc.edn
runs-on: ubuntu-latest
steps:
- 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
check-cljdoc:
name: Check cljdoc analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Clojure
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

3
.gitignore vendored
View file

@ -13,3 +13,6 @@ pom.xml.asc
/_book /_book
figwheel_server.log figwheel_server.log
/.idea /.idea
.clj-kondo
.shadow-cljs
.cache

3
.lsp/config.edn Normal file
View file

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

View file

@ -12,6 +12,564 @@ We use [Break Versioning][breakver]. The version numbers follow a `<major>.<mino
[breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md [breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md
## UNRELEASED
* **FIX** redirect-trailing-slash-handler won't make external redirects. [#776](https://github.com/metosin/reitit/pull/776)
* Allow colons in bracket parameter syntax. [#770](https://github.com/metosin/reitit/pull/770)
* Add `url-encode?` option to `match-by-name`. [#778](https://github.com/metosin/reitit/pull/778)
## 0.10.0 (2026-01-09)
* Improve & document how response schemas get picked in per-content-type coercion. See [docs](./doc/ring/coercion.md#per-content-type-coercion). [#745](https://github.com/metosin/reitit/issues/745).
* **BREAKING** Remove unused `reitit.dependency` ns. [#763](https://github.com/metosin/reitit/pull/763)
* Support passing options to malli humanize. See [docs](./doc/coercion/malli_coercion.md). [#467](https://github.com/metosin/reitit/issues/467)
* **FIX** Handling of ex-type keyword hierarchies in create-exception-middleware. [#768](https://github.com/metosin/reitit/issues/768)
* Updated dependencies:
```
[metosin/malli "0.20.0"] is available but we use "0.19.2"
[com.fasterxml.jackson.core/jackson-core "2.20.1"] is available but we use "2.20.0"
[com.fasterxml.jackson.core/jackson-databind "2.20.1"] is available but we use "2.20.0"
[org.clojure/core.rrb-vector "0.2.1"] is available but we use "0.2.0"
```
## 0.9.2 (2025-10-28)
* Allow multimethods as handlers when validating [#755](https://github.com/metosin/reitit/pull/755)
* Improve error reporting when generating OpenAPI fails [#754](https://github.com/metosin/reitit/pull/754)
* Allow middleware registry to be used when defining middleware in `ring-handler`. See [docs](./doc/ring/middleware_registry.md). [#739](https://github.com/metosin/reitit/pull/739)
* Allow passing options (eg. `:malli.transform/add-optional-keys`) to malli's `default-value-transformer`. See [docs](./doc/coercion/malli_coercion.md). [#756](https://github.com/metosin/reitit/pull/756)
* **FIX**: `match-by-name!` returning `nil` instead of throwing an exception for some partial matches [#758](https://github.com/metosin/reitit/issues/758)
* Updated dependencies:
```
[com.fasterxml.jackson.core/jackson-core "2.20.0"] is available but we use "2.18.2"
[com.fasterxml.jackson.core/jackson-databind "2.20.0"] is available but we use "2.18.2"
[compojure "1.7.2"] is available but we use "1.7.1"
[fipp "0.6.29"] is available but we use "0.6.27"
[metosin/malli "0.19.2"] is available but we use "0.18.0"
[ring "1.15.3"] is available but we use "1.14.1"
[ring/ring-core "1.15.3"] is available but we use "1.14.1"
[ring/ring-defaults "0.7.0"] is available but we use "0.6.0"
```
## 0.9.1 (2025-05-27)
* **FIX**: response coercion threw an exception for unlisted HTTP status codes if there was no `:default`. Broken in 0.9.0. [#742](https://github.com/metosin/reitit/issues/742)
## 0.9.0 (2025-05-23)
* Improvements to mime type handling in `create-file-handler` and `create-resource-handler` [#733](https://github.com/metosin/reitit/pull/733)
* 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:
```clojure
[metosin/ring-swagger-ui "4.19.1"] is available but we use "4.18.1"
```
## 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**
## 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
* Updated dependencies:
```clojure
[metosin/schema-tools "0.13.1"] is available but we use "0.13.0"
[com.fasterxml.jackson.core/jackson-core "2.15.1"] is available but we use "2.14.2"
[com.fasterxml.jackson.core/jackson-databind "2.15.1"] is available but we use "2.14.2"
```
## 0.7.0-alpha4 (2023-05-17)
**[compare](https://github.com/metosin/reitit/compare/0.7.0-alpha3...0.7.0-alpha4)**
* OpenAPI 3 parameter descriptions get populated from malli/spec/schema descriptions. [#612](https://github.com/metosin/reitit/issues/612)
## 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))
## 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:
```clojure
[metosin/ring-swagger-ui "4.18.1"] is available but we use "4.15.5"
[metosin/malli "0.11.0"] is available but we use "0.10.1"
[ring/ring-core "1.10.0"] is available but we use "1.9.6"
```
## 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)
* FIX: redirect-trailing-slash-handler strips query-params [#565](https://github.com/metosin/reitit/issues/565)
* **BREAKING**: Drop tests for Clojure 1.9, run tests with 1.10 & 1.11
* NEW option `:meta-merge` on a router for custom merge strategy on route data
* Swagger: support operationId in generated swagger json [#452](https://github.com/metosin/reitit/pull/452) & [#569](https://github.com/metosin/reitit/pull/569)
* Update documentation and link to the startrek project [#578](https://github.com/metosin/reitit/pull/578)
* Upgrade jackson for CVE-2022-42003 and CVE-2022-42004 [#577](https://github.com/metosin/reitit/pull/577)
* Improved coercion errors perf [#576](https://github.com/metosin/reitit/pull/576)
* Add example for Reitit + Pedestal + Malli coercion [#572](https://github.com/metosin/reitit/pull/572)
* Handle empty seq as empty string in query-string [#566](https://github.com/metosin/reitit/pull/566)
* Polish pedestal chains when printing context diffs [#557](https://github.com/metosin/reitit/pull/557)
* Updated dependencies:
```clojure
[metosin/ring-swagger-ui "4.15.5"] is available but we use "4.3.0"
[metosin/jsonista "0.3.7"] is available but we use "0.3.5"
[metosin/malli "0.10.1"] is available but we use "0.8.2"
[fipp "0.6.26"] is available but we use "0.6.25"
[ring/ring-core "1.9.6"] is available but we use "1.9.5"
[com.fasterxml.jackson.core/jackson-core "2.14.2"] is available but we use "2.14.1"
[com.fasterxml.jackson.core/jackson-databind "2.14.2"] is available but we use "2.14.1"
```
## 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)
## 0.5.17 (2022-03-10)
**[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)**
* Support for [Malli Lite Syntax](https://github.com/metosin/malli#lite) in coercion (enabled by default):
```clj
["/add/:id" {:post {:parameters {:path {:id int?}
:query {:a (l/optional int?)}
:body {:id int?
:data {:id (l/maybe int?)
:orders (l/map-of uuid? {:name string?})}}}
:responses {200 {:body {:total pos-int?}}
500 {:description "fail"}}}}]
```
* Improved Reitit-frontend function docstrings
* Updated deps:
```clj
[metosin/ring-swagger-ui "4.3.0"] is available but we use "3.46.0"
[metosin/jsonista "0.3.5"] is available but we use "0.3.3"
[metosin/malli "0.8.2"] is available but we use "0.5.1"
[com.fasterxml.jackson.core/jackson-core "2.13.1"] is available but we use "2.12.4"
[com.fasterxml.jackson.core/jackson-databind "2.13.1"] is available but we use "2.12.4"
[fipp "0.6.25"] is available but we use "0.6.24"
[expound "0.9.0"] is available but we use "0.8.9"
[ring/ring-core "1.9.5"] is available but we use "1.9.4"
```
## 0.5.15 (2021-08-05)
**[compare](https://github.com/metosin/reitit/compare/0.5.14...0.5.15)**
* recompiled with Java8
## 0.5.14 (2021-08-03)
**[compare](https://github.com/metosin/reitit/compare/0.5.13...0.5.14)**
* updated deps:
```clj
[metosin/ring-swagger-ui "3.46.0"] is available but we use "3.36.0"
[metosin/jsonista "0.3.3"] is available but we use "0.3.1"
[metosin/malli "0.5.1"] is available but we use "0.3.0"
[com.fasterxml.jackson.core/jackson-core "2.12.4"] is available but we use "2.12.1"
[com.fasterxml.jackson.core/jackson-databind "2.12.4"] is available but we use "2.12.1"
[fipp "0.6.24"] is available but we use "0.6.23"
[ring/ring-core "1.9.4"] is available but we use "1.9.1"
[io.pedestal/pedestal.service "0.5.9"] is available but we use "0.5.8"
```
### `reitit-ring`
* Fixes `reitit.ring/create-resource-handler` and `reitit.ring/create-file-handler` to support URL-escaped characters. [#484](https://github.com/metosin/reitit/issues/484). PR [#489](https://github.com/metosin/reitit/pull/489).
### `reitit-malli`
* FIX: Malli response coercision seems to do nothing at all [#498](https://github.com/metosin/reitit/pull/501)
### `reitit-pedestal`
* Enrich request for pedestal/routing-interceptor default-queue [#495](https://github.com/metosin/reitit/pull/495)
## 0.5.13 (2021-04-23)
**[compare](https://github.com/metosin/reitit/compare/0.5.12...0.5.13)**
* updated deps:
```clj
[metosin/malli "0.3.0"] is available but we use "0.2.1"
[metosin/schema-tools "0.12.3"] is available but we use "0.12.2"
[ring/ring-core "1.9.1"] is available but we use "1.9.0"
[metosin/schema-tools "0.12.3"] is available but we use "0.12.2"
[expound "0.8.9"] is available but we use "0.8.7"
[ring "1.9.1"] is available but we use "1.9.0"
```
### `reitit-ring`
* Make reitit.ring/create-resource-handler's `:not-found-handler` work when used outside of a router. [#464](https://github.com/metosin/reitit/issues/464). PR [#471](https://github.com/metosin/reitit/pull/471) by Kari Marttila and Metosin Maintenance Mob.
## 0.5.12 (2021-02-01)
**[compare](https://github.com/metosin/reitit/compare/0.5.11...0.5.12)**
* updated deps:
```
[metosin/spec-tools "0.10.5"] is available but we use "0.10.4"
[metosin/jsonista "0.3.1"] is available but we use "0.3.0"
[metosin/muuntaja "0.6.8"] is available but we use "0.6.7"
[ring/ring-core "1.9.0"] is available but we use "1.8.2"
[metosin/muuntaja "0.6.8"] is available but we use "0.6.7"
[com.fasterxml.jackson.core/jackson-core "2.12.1"] is available but we use "2.12.0"
[com.fasterxml.jackson.core/jackson-databind "2.12.1"] is available but we use "2.12.0"
```
* Allow whitespace as separator in route paths. [#411](https://github.com/metosin/reitit/issues/411). PR [#466](https://github.com/metosin/reitit/pull/466) by Kimmo Koskinen and Metosin Maintenance Mob.
## 0.5.11 (2020-12-27)
**[compare](https://github.com/metosin/reitit/compare/0.5.10...0.5.11)**
* updated deps:
```clj
[metosin/ring-swagger-ui "3.36.0"] is available but we use "3.25.3"
[metosin/jsonista "0.3.0"] is available but we use "0.2.7"
[com.fasterxml.jackson.core/jackson-core "2.12.0"] is available but we use "2.11.2"
[com.fasterxml.jackson.core/jackson-databind "2.12.0"] is available but we use "2.11.2"
```
## 0.5.10 (2020-10-22)
**[compare](https://github.com/metosin/reitit/compare/0.5.9...0.5.10)**
* updated deps:
```clj
[metosin/malli "0.2.1] is available but we use "0.2.0"
```
### `reitit-malli`
* fix [#445](https://github.com/metosin/reitit/issues/445): Malli response coercion failing for `[:sequential string?]` if the response body is an empty vector
## 0.5.9 (2020-10-19)
**[compare](https://github.com/metosin/reitit/compare/0.5.8...0.5.9)**
### `reitit-frontend`
- `reitit.frontend.easy/start!` now correctly removes old event listeners
when called repeatedly (e.g. with hot code reload workflow)
([#438](https://github.com/metosin/reitit/pull/438))
## 0.5.8 (2020-10-19)
**[compare](https://github.com/metosin/reitit/compare/0.5.7...0.5.8)**
* Add `:conflicting` to route data spec [#444](https://github.com/metosin/reitit/pull/444).
## 0.5.7 (2020-10-18)
**[compare](https://github.com/metosin/reitit/compare/0.5.6...0.5.7)**
* updated deps:
```clj
[metosin/malli "0.2.0"] is available but we use "0.0.1-20200924.063109-27"
[com.fasterxml.jackson.core/jackson-core "2.11.3"] is available but we use "2.11.2"
[com.fasterxml.jackson.core/jackson-databind "2.11.3"] is available but we use "2.11.2"
[expound "0.8.6"] is available but we use "0.8.5"
[ring/ring-core "1.8.2"] is available but we use "1.8.1"
```
### `reitit-ring`
* Fix resource handler path matching [#443](https://github.com/metosin/reitit/pull/443)
* Automatically publish Swagger `:consumes` for `:form` params, fixes [#217](https://github.com/metosin/reitit/issues/217).
## 0.5.6 (2020-09-26)
**[compare](https://github.com/metosin/reitit/compare/0.5.5...0.5.6)**
* updated deps:
```clj
[metosin/malli "0.0.1-20200924.063109-27"] is available but we use "0.0.1-20200715.082439-21"
[metosin/spec-tools "0.10.4"] is available but we use "0.10.3"
[metosin/jsonista "0.2.7"] is available but we use "0.2.6"
[com.fasterxml.jackson.core/jackson-core "2.11.2"] is available but we use "2.11.0"
[com.fasterxml.jackson.core/jackson-databind "2.11.2"] is available but we use "2.11.0"
```
### `reitit-malli`
* `:map-of` keys in JSON are correctly decoded using string-decoders
* new `:encode-error` option in coercion:
```clj
(def coercion
(reitit.coercion.malli/create
{:encode-error (fn [error] {:errors (:humanized error)})}))
; results in... => {:status 400, :body {:errors {:x ["missing required key"]}}}
```
## 0.5.5 (2020-07-15)
**[compare](https://github.com/metosin/reitit/compare/0.5.4...0.5.5)**
* recompile with Java8
```clj
[metosin/malli "0.0.1-20200715.082439-21"] is available but we use "0.0.1-20200713.080243-20"
```
## 0.5.4 (2020-07-13)
**[compare](https://github.com/metosin/reitit/compare/0.5.3...0.5.4)**
```clj
[metosin/malli "0.0.1-20200713.080243-20"] is available but we use "0.0.1-20200709.163702-18"
```
## 0.5.3 (2020-07-09)
**[compare](https://github.com/metosin/reitit/compare/0.5.2...0.5.3)**
```clj
[metosin/malli "0.0.1-20200709.163702-18"] is available but we use "0.0.1-20200525.162645-15"
```
## 0.5.2 (2020-05-27)
**[compare](https://github.com/metosin/reitit/compare/0.5.1...0.5.2)**
```clj
[metosin/malli "0.0.1-20200525.162645-15"] is available but we use "0.0.1-20200404.091302-14"
```
### `reitit-malli`
* Fixed coercion with `:and` and `:or`, fixes [#407](https://github.com/metosin/reitit/issues/407).
* New options to `reitit.coercion.malli/create`:
* `:validate` - boolean to indicate whether validation is enabled (true)
* `:enabled` - boolean to indicate whether coercion (and validation) is enabled (true)
### `reitit-swagger`
* If no `:responses` are defined for an endpoint, add `{:responses {:default {:description ""}}}` to make swagger spec valid, fixes [#403](https://github.com/metosin/reitit/issues/403) by [胡雨軒 Петр](https://github.com/piotr-yuxuan).
### `reitit-ring`
* Coercion middleware will not to mount if the selected `:coercion` is not enabled for the given `:parameters`, e.g. "just api-docs"
### `reitit-http`
* Coercion interceptor will not to mount if the selected `:coercion` is not enabled for the given `:parameters`, e.g. "just api-docs"
## 0.5.1 (2020-05-18)
**[compare](https://github.com/metosin/reitit/compare/0.5.0...0.5.1)**
```clj
[metosin/sieppari "0.0.0-alpha13"] is available but we use "0.0.0-alpha10"
```
* new sieppari NOT to include all it's dev dependencies, fixes [#402](https://github.com/metosin/reitit/issues/402)
* re-compile with Java8
## 0.5.0 (2020-05-17) ## 0.5.0 (2020-05-17)
* **NOTE** Due to [issues with Jackson versioning](https://clojureverse.org/t/depending-on-the-right-versions-of-jackson-libraries/5111), you might get errors after updating as [Cheshire still uses older version](https://github.com/dakrone/cheshire/pull/164) as is most likely as a transitive dependency via 3rd party libs. To resolve issues (with Leiningen), you can either: * **NOTE** Due to [issues with Jackson versioning](https://clojureverse.org/t/depending-on-the-right-versions-of-jackson-libraries/5111), you might get errors after updating as [Cheshire still uses older version](https://github.com/dakrone/cheshire/pull/164) as is most likely as a transitive dependency via 3rd party libs. To resolve issues (with Leiningen), you can either:

View file

@ -1,10 +0,0 @@
help:
@just --list
# Initializes lint
init-lint:
clj-kondo --lint $(lein classpath)
# Lints the project
lint:
./lint.sh

134
README.md
View file

@ -1,17 +1,23 @@
# reitit [![Build Status](https://img.shields.io/circleci/project/github/metosin/reitit.svg)](https://circleci.com/gh/metosin/reitit) [![cljdoc badge](https://cljdoc.org/badge/metosin/reitit)](https://cljdoc.org/jump/release/metosin/reitit) [![Slack](https://img.shields.io/badge/clojurians-reitit-blue.svg?logo=slack)](https://clojurians.slack.com/messages/reitit/) # reitit
[![Build Status](https://github.com/metosin/reitit/actions/workflows/testsuite.yml/badge.svg)](https://github.com/metosin/reitit/actions)
[![cljdoc badge](https://cljdoc.org/badge/metosin/reitit)](https://cljdoc.org/d/metosin/reitit/)
[![Clojars Project](https://img.shields.io/clojars/v/metosin/reitit.svg)](https://clojars.org/metosin/reitit)
[![Slack](https://img.shields.io/badge/clojurians-reitit-blue.svg?logo=slack)](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). A fast data-driven router for Clojure(Script).
* Simple data-driven [route syntax](https://metosin.github.io/reitit/basics/route_syntax.html) * Simple data-driven [route syntax](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-syntax/)
* Route [conflict resolution](https://metosin.github.io/reitit/basics/route_conflicts.html) * Route [conflict resolution](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-conflicts/)
* First-class [route data](https://metosin.github.io/reitit/basics/route_data.html) * First-class [route data](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-data/)
* Bi-directional routing * Bi-directional routing
* [Pluggable coercion](https://metosin.github.io/reitit/coercion/coercion.html) ([malli](https://github.com/metosin/malli), [schema](https://github.com/plumatic/schema) & [clojure.spec](https://clojure.org/about/spec)) * [Pluggable coercion](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/coercion/coercion-explained) ([malli](https://github.com/metosin/malli), [schema](https://github.com/plumatic/schema) & [clojure.spec](https://clojure.org/about/spec))
* Helpers for [ring](https://metosin.github.io/reitit/ring/ring.html), [http](https://metosin.github.io/reitit/http/interceptors.html), [pedestal](https://metosin.github.io/reitit/http/pedestal.html) & [frontend](https://metosin.github.io/reitit/frontend/basics.html) * Helpers for [ring](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/ring/ring-router), [http](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/http/interceptors/), [pedestal](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/http/pedestal/) & [frontend](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/frontend/basics/)
* Friendly [Error Messages](https://metosin.github.io/reitit/basics/error_messages.html) * Friendly [Error Messages](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/error-messages/)
* Extendable * Extendable
* Modular * Modular
* [Fast](https://metosin.github.io/reitit/performance.html) * [Fast](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/misc/performance)
Presentations: Presentations:
* [Reitit, The Ancient Art of Data-Driven](https://www.slideshare.net/mobile/metosin/reitit-clojurenorth-2019-141438093), Clojure/North 2019, [video](https://youtu.be/cSntRGAjPiM) * [Reitit, The Ancient Art of Data-Driven](https://www.slideshare.net/mobile/metosin/reitit-clojurenorth-2019-141438093), Clojure/North 2019, [video](https://youtu.be/cSntRGAjPiM)
@ -20,26 +26,36 @@ Presentations:
* [Data-Driven Ring with Reitit](https://www.metosin.fi/blog/reitit-ring/) * [Data-Driven Ring with Reitit](https://www.metosin.fi/blog/reitit-ring/)
* [Reitit, Data-Driven Routing with Clojure(Script)](https://www.metosin.fi/blog/reitit/) * [Reitit, Data-Driven Routing with Clojure(Script)](https://www.metosin.fi/blog/reitit/)
## [Full Documentation](https://metosin.github.io/reitit/) **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. There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians Slack](http://clojurians.net/) for discussion & help.
## Main Modules ## Main Modules
* `reitit` - all bundled * `metosin/reitit` - all bundled
* `reitit-core` - the routing core * `metosin/reitit-core` - the routing core
* `reitit-ring` - a [ring router](https://metosin.github.io/reitit/ring/ring.html) * `metosin/reitit-ring` - a [ring router](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/ring/ring-router/)
* `reitit-middleware` - [common middleware](https://metosin.github.io/reitit/ring/default_middleware.html) * `metosin/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 * `metosin/reitit-spec` [clojure.spec](https://clojure.org/about/spec) coercion
* `reitit-malli` [malli](https://github.com/metosin/malli) coercion * `metosin/reitit-malli` [malli](https://github.com/metosin/malli) coercion
* `reitit-schema` [Schema](https://github.com/plumatic/schema) coercion * `metosin/reitit-schema` [Schema](https://github.com/plumatic/schema) coercion
* `reitit-swagger` [Swagger2](https://swagger.io/) apidocs * `fi.metosin/reitit-openapi` [OpenAPI](https://www.openapis.org/) apidocs *
* `reitit-swagger-ui` Integrated [Swagger UI](https://github.com/swagger-api/swagger-ui) * `metosin/reitit-swagger` [Swagger2](https://swagger.io/) apidocs
* `reitit-frontend` Tools for [frontend routing]((https://metosin.github.io/reitit/frontend/basics.html)) * `metosin/reitit-swagger-ui` Integrated [Swagger UI](https://github.com/swagger-api/swagger-ui)
* `reitit-http` http-routing with Interceptors * `metosin/reitit-frontend` Tools for [frontend routing]((https://cljdoc.org/d/metosin/reitit/CURRENT/doc/frontend/basics/))
* `reitit-interceptors` - [common interceptors](https://metosin.github.io/reitit/http/default_interceptors.html) * `metosin/reitit-http` http-routing with Interceptors
* `reitit-sieppari` support for [Sieppari](https://github.com/metosin/sieppari) * `metosin/reitit-interceptors` - [common interceptors](https://cljdoc.org/d/metosin/reitit/CURRENT/doc/http/default-interceptors/)
* `reitit-dev` - development utilities * `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 ## Extra modules
@ -50,11 +66,15 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians
All main modules bundled: All main modules bundled:
```clj ```clj
[metosin/reitit "0.5.0"] [metosin/reitit "0.10.0"]
``` ```
Optionally, the parts can be required separately. Optionally, the parts can be required separately.
Reitit requires Clojure 1.11 and Java 11.
Reitit is tested with the LTS releases Java 11, 17, 21 and 25
## Quick start ## Quick start
```clj ```clj
@ -85,22 +105,29 @@ Optionally, the parts can be required separately.
A Ring routing app with input & output coercion using [data-specs](https://github.com/metosin/spec-tools/blob/master/README.md#data-specs). A Ring routing app with input & output coercion using [data-specs](https://github.com/metosin/spec-tools/blob/master/README.md#data-specs).
```clj ```clj
(require '[muuntaja.core :as m])
(require '[reitit.ring :as ring]) (require '[reitit.ring :as ring])
(require '[reitit.coercion.spec]) (require '[reitit.coercion.spec])
(require '[reitit.ring.coercion :as rrc]) (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])
(def app (def app
(ring/ring-handler (ring/ring-handler
(ring/router (ring/router
["/api" ["/api"
["/math" {:get {:parameters {:query {:x int?, :y int?}} ["/math" {:get {:parameters {:query {:x int?, :y int?}}
:responses {200 {:body {:total pos-int?}}} :responses {200 {:body {:total int?}}}
:handler (fn [{{{:keys [x y]} :query} :parameters}] :handler (fn [{{{:keys [x y]} :query} :parameters}]
{:status 200 {:status 200
:body {:total (+ x y)}})}}]] :body {:total (+ x y)}})}}]]
;; router data effecting all routes ;; router data affecting all routes
{:data {:coercion reitit.coercion.spec/coercion {:data {:coercion reitit.coercion.spec/coercion
:middleware [rrc/coerce-exceptions-middleware :muuntaja m/instance
: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 rrc/coerce-request-middleware
rrc/coerce-response-middleware]}}))) rrc/coerce-response-middleware]}})))
``` ```
@ -108,35 +135,40 @@ A Ring routing app with input & output coercion using [data-specs](https://githu
Valid request: Valid request:
```clj ```clj
(app {:request-method :get (-> (app {:request-method :get
:uri "/api/math" :uri "/api/math"
:query-params {:x "1", :y "2"}}) :query-params {:x "1", :y "2"}})
(update :body slurp))
; {:status 200 ; {:status 200
; :body {:total 3}} ; :body "{\"total\":3}"
; :headers {"Content-Type" "application/json; charset=utf-8"}}
``` ```
Invalid request: Invalid request:
```clj ```clj
(app {:request-method :get (-> (app {:request-method :get
:uri "/api/math" :uri "/api/math"
:query-params {:x "1", :y "a"}}) :query-params {:x "1", :y "a"}})
;{:status 400, (update :body jsonista.core/read-value))
; :body {:type :reitit.coercion/request-coercion, ; {:status 400
; :coercion :spec, ; :headers {"Content-Type" "application/json; charset=utf-8"}
; :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}})", ; :body {"spec" "(spec-tools.core/spec {:spec (clojure.spec.alpha/keys :req-un [:spec$8974/x :spec$8974/y]), :type :map, :leaf? false})"
; :problems [{:path [:y], ; "value" {"x" "1"
; :pred "clojure.core/int?", ; "y" "a"}
; :val "a", ; "problems" [{"via" ["spec$8974/y"]
; :via [:$spec20745/y], ; "path" ["y"]
; :in [:y]}], ; "pred" "clojure.core/int?"
; :value {:x "1", :y "a"}, ; "in" ["y"]
; :in [:request :query-params]}} ; "val" "a"}]
; "type" "reitit.coercion/request-coercion"
; "coercion" "spec"
; "in" ["request" "query-params"]}}
``` ```
## More examples ## More examples
* [`reitit-ring` with coercion, swagger and default middleware](https://github.com/metosin/reitit/blob/master/examples/ring-swagger/src/example/server.clj) * [`reitit-ring` with coercion, swagger and default middleware](https://github.com/metosin/reitit/blob/master/examples/ring-malli-swagger/src/example/server.clj)
* [`reitit-frontend`, the easy way](https://github.com/metosin/reitit/blob/master/examples/frontend/src/frontend/core.cljs) * [`reitit-frontend`, the easy way](https://github.com/metosin/reitit/blob/master/examples/frontend/src/frontend/core.cljs)
* [`reitit-frontend` with Keechma-style controllers](https://github.com/metosin/reitit/blob/master/examples/frontend-controllers/src/frontend/core.cljs) * [`reitit-frontend` with Keechma-style controllers](https://github.com/metosin/reitit/blob/master/examples/frontend-controllers/src/frontend/core.cljs)
* [`reitit-http` with Pedestal](https://github.com/metosin/reitit/blob/master/examples/pedestal/src/example/server.clj) * [`reitit-http` with Pedestal](https://github.com/metosin/reitit/blob/master/examples/pedestal/src/example/server.clj)
@ -144,9 +176,23 @@ Invalid request:
All examples are in https://github.com/metosin/reitit/tree/master/examples All examples are in https://github.com/metosin/reitit/tree/master/examples
## External resources
* Simple web application using Ring/Reitit and Integrant: https://github.com/PrestanceDesign/usermanager-reitit-integrant-example
* A simple Clojure backend using Reitit to serve up a RESTful API: [startrek](https://github.com/dharrigan/startrek). Technologies include:
* [Donut System](https://github.com/donut-party/system)
* [next-jdbc](https://github.com/seancorfield/next-jdbc)
* [JUXT Clip](https://github.com/juxt/clip)
* [Flyway](https://github.com/flyway/flyway)
* [HoneySQL](https://github.com/seancorfield/honeysql)
* [Babashka](https://babashka.org)
* https://www.learnreitit.com/
* Lipas, liikuntapalvelut: https://github.com/lipas-liikuntapaikat/lipas
* Implementation of the Todo-Backend API spec, using Clojure, Ring/Reitit and next-jdbc: https://github.com/PrestanceDesign/todo-backend-clojure-reitit
* Ping CRM, a single page app written in Clojure Ring, Reitit, Integrant and next.jdbc: https://github.com/prestancedesign/clojure-inertia-pingcrm-demo
## More info ## More info
[Check out the full documentation!](https://metosin.github.io/reitit/) [Check out the full documentation!](https://cljdoc.org/d/metosin/reitit/CURRENT/)
Join [#reitit](https://clojurians.slack.com/messages/reitit/) channel in [Clojurians slack](http://clojurians.net/). Join [#reitit](https://clojurians.slack.com/messages/reitit/) channel in [Clojurians slack](http://clojurians.net/).
@ -163,6 +209,6 @@ Roadmap is mostly written in [issues](https://github.com/metosin/reitit/issues).
## License ## License
Copyright © 2017-2020 [Metosin Oy](http://www.metosin.fi) Copyright © 2017-2023 [Metosin Oy](http://www.metosin.fi)
Distributed under the Eclipse Public License, the same as Clojure. Distributed under the Eclipse Public License, the same as Clojure.

26
bb.edn Normal file
View 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"))}}}

View file

@ -0,0 +1,3 @@
{
"name": "Example"
}

View file

@ -0,0 +1 @@
hello

View file

@ -6,7 +6,7 @@
* Route [conflict resolution](./basics/route_conflicts.md) * Route [conflict resolution](./basics/route_conflicts.md)
* First-class [route data](./basics/route_data.md) * First-class [route data](./basics/route_data.md)
* Bi-directional routing * Bi-directional routing
* [Pluggable coercion](./coercion/coercion.md) ([schema](https://github.com/plumatic/schema) & [clojure.spec](https://clojure.org/about/spec)) * [Pluggable coercion](./coercion/coercion.md) ([schema](https://github.com/plumatic/schema), [clojure.spec](https://clojure.org/about/spec), [malli](https://github.com/metosin/malli))
* Helpers for [ring](./ring/ring.md), [http](./http/interceptors.md), [pedestal](./http/pedestal.md) & [frontend](./frontend/basics.md) * Helpers for [ring](./ring/ring.md), [http](./http/interceptors.md), [pedestal](./http/pedestal.md) & [frontend](./frontend/basics.md)
* Friendly [Error Messages](./basics/error_messages.md) * Friendly [Error Messages](./basics/error_messages.md)
* Extendable * Extendable
@ -24,6 +24,7 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians
* `reitit-spec` [clojure.spec](https://clojure.org/about/spec) coercion * `reitit-spec` [clojure.spec](https://clojure.org/about/spec) coercion
* `reitit-schema` [Schema](https://github.com/plumatic/schema) coercion * `reitit-schema` [Schema](https://github.com/plumatic/schema) coercion
* `reitit-swagger` [Swagger2](https://swagger.io/) apidocs * `reitit-swagger` [Swagger2](https://swagger.io/) apidocs
* `reitit-openapi` OpenAPI 3 apidocs
* `reitit-swagger-ui` Integrated [Swagger UI](https://github.com/swagger-api/swagger-ui). * `reitit-swagger-ui` Integrated [Swagger UI](https://github.com/swagger-api/swagger-ui).
* `reitit-frontend` Tools for [frontend routing](frontend/basics.md) * `reitit-frontend` Tools for [frontend routing](frontend/basics.md)
* `reitit-http` http-routing with Pedestal-style Interceptors * `reitit-http` http-routing with Pedestal-style Interceptors
@ -40,7 +41,7 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians
All bundled: All bundled:
```clj ```clj
[metosin/reitit "0.5.0"] [metosin/reitit "0.10.0"]
``` ```
Optionally, the parts can be required separately. Optionally, the parts can be required separately.
@ -110,9 +111,9 @@ Reverse-routing:
; :path "/api/orders/2"} ; :path "/api/orders/2"}
``` ```
## Ring-router ## Ring router
Ring-router adds support for `:handler` functions, `:middleware` and routing based on `:request-method`. It also supports pluggable parameter coercion (`clojure.spec`), data-driven middleware, route and middleware compilation, dynamic extensions and more. A Ring router function adds support for `:handler` functions, `:middleware` and routing based on `:request-method`. It also supports pluggable parameter coercion (`clojure.spec`), data-driven middleware, route and middleware compilation, dynamic extensions and more.
```clj ```clj
(require '[reitit.ring :as ring]) (require '[reitit.ring :as ring])
@ -139,7 +140,7 @@ Routing:
```clj ```clj
(app {:request-method :get, :uri "/api/admin/users"}) (app {:request-method :get, :uri "/api/admin/users"})
; {:status 200, :body "ok", :wrap (:api :admin} ; {:status 200, :body "ok", :wrap (:api :admin)}
(app {:request-method :put, :uri "/api/admin/users"}) (app {:request-method :put, :uri "/api/admin/users"})
; nil ; nil

View file

@ -21,10 +21,11 @@
* [Plumatic Schema](coercion/schema_coercion.md) * [Plumatic Schema](coercion/schema_coercion.md)
* [Clojure.spec](coercion/clojure_spec_coercion.md) * [Clojure.spec](coercion/clojure_spec_coercion.md)
* [Data-specs](coercion/data_spec_coercion.md) * [Data-specs](coercion/data_spec_coercion.md)
* [Malli](coercion/malli_coercion.md)
## Ring ## Ring
* [Ring-router](ring/ring.md) * [Ring Router](ring/ring.md)
* [Reverse-routing](ring/reverse_routing.md) * [Reverse-routing](ring/reverse_routing.md)
* [Default handler](ring/default_handler.md) * [Default handler](ring/default_handler.md)
* [Slash handler](ring/slash_handler.md) * [Slash handler](ring/slash_handler.md)
@ -40,6 +41,7 @@
* [Route Data Validation](ring/route_data_validation.md) * [Route Data Validation](ring/route_data_validation.md)
* [Compiling Middleware](ring/compiling_middleware.md) * [Compiling Middleware](ring/compiling_middleware.md)
* [Swagger Support](ring/swagger.md) * [Swagger Support](ring/swagger.md)
* [OpenAPI Support](ring/openapi.md)
* [RESTful form methods](ring/RESTful_form_methods.md) * [RESTful form methods](ring/RESTful_form_methods.md)
## HTTP ## HTTP

View file

@ -143,10 +143,10 @@ As the `Match` contains all the route data, we can create a new matching functio
(require '[clojure.string :as str]) (require '[clojure.string :as str])
(defn recursive-match-by-path [router path] (defn recursive-match-by-path [router path]
(if-let [match (r/match-by-path router path)] (when-let [match (r/match-by-path router path)]
(if-let [subrouter (-> match :data :router)] (if-let [subrouter (-> match :data :router)]
(let [subpath (subs path (str/last-index-of (:template match) "/"))] (let [subpath (subs path (str/last-index-of (:template match) "/"))]
(if-let [submatch (recursive-match-by-path subrouter subpath)] (when-let [submatch (recursive-match-by-path subrouter subpath)]
(cons match submatch))) (cons match submatch)))
(list match)))) (list match))))
``` ```
@ -206,10 +206,10 @@ First, we need to modify our matching function to support router references:
(deref x) x)) (deref x) x))
(defn recursive-match-by-path [router path] (defn recursive-match-by-path [router path]
(if-let [match (r/match-by-path (<< router) path)] (when-let [match (r/match-by-path (<< router) path)]
(if-let [subrouter (-> match :data :router <<)] (if-let [subrouter (-> match :data :router <<)]
(let [subpath (subs path (str/last-index-of (:template match) "/"))] (let [subpath (subs path (str/last-index-of (:template match) "/"))]
(if-let [submatch (recursive-match-by-path subrouter subpath)] (when-let [submatch (recursive-match-by-path subrouter subpath)]
(cons match submatch))) (cons match submatch)))
(list match)))) (list match))))
``` ```
@ -405,9 +405,53 @@ All the beer-routes now match in constant time.
|-----------------|---------|----------------------- |-----------------|---------|-----------------------
| `/beers/sahti` | 40ns | static | `/beers/sahti` | 40ns | static
### Wrapping a swappable ring handler
In order for a ring handler to be recomposed, we can wrap it into a handler that dereferences it on request.
```clj
(defn deref-handler [rf]
(fn
([request] (@rf request))
([request respond raise] (@rf request respond raise))))
```
A simplified beer router version that creates a ring-handler.
```clj
(defn create-ring-handler [beers]
(ring/ring-handler
(ring/router
[["/beers"
(when (seq beers)
(for [beer beers]
[(str "/" beer)
{:get (fn [_] {:status 200 :body beer})}]))]])))
(def ring-handler
(atom (create-ring-handler nil)))
(defn reset-router! [beers]
(reset! ring-handler (create-ring-handler beers)))
```
We don't have any matching routes yet.
```clj
((deref-handler ring-handler) {:request-method :get :uri "/beers/lager"})
; nil
```
But we can add them later.
```clj
(reset-router! ["lager"])
((deref-handler ring-handler) {:request-method :get :uri "/beers/lager"})
; {:status 200, :body "lager"}
```
## TODO ## TODO
* add an example how to do dynamic routing with `reitit-ring`
* maybe create a `recursive-router` into a separate ns with all `Router` functions implemented correctly? maybe not... * maybe create a `recursive-router` into a separate ns with all `Router` functions implemented correctly? maybe not...
* add `reitit.core/merge-routes` to effectively merge routes with route data * add `reitit.core/merge-routes` to effectively merge routes with route data

View file

@ -3,7 +3,7 @@
Routers can be configured via options. The following options are available for the `reitit.core/router`: Routers can be configured via options. The following options are available for the `reitit.core/router`:
| key | description | key | description
|--------------|------------- |-----------------|-------------
| `:path` | Base-path for routes | `:path` | Base-path for routes
| `:routes` | Initial resolved routes (default `[]`) | `:routes` | Initial resolved routes (default `[]`)
| `:data` | Initial route data (default `{}`) | `:data` | Initial route data (default `{}`)
@ -15,4 +15,8 @@ Routers can be configured via options. The following options are available for t
| `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects | `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects
| `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes | `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes
| `:exception` | Function of `Exception => Exception ` to handle creation time exceptions (default `reitit.exception/exception`) | `:exception` | Function of `Exception => Exception ` to handle creation time exceptions (default `reitit.exception/exception`)
| `:meta-merge` | Function of `left right => merged` to merge route-data (default `meta-merge.core/meta-merge`)
| `:update-paths` | Sequence of Vectors with elements `update-path` and `function`, used to preprocess route data
| `:router` | Function of `routes opts => router` to override the actual router implementation | `:router` | Function of `routes opts => router` to override the actual router implementation

View file

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

View file

@ -1,6 +1,6 @@
# Different Routers # Different Routers
Reitit ships with several different implementations for the `Router` protocol, originally based on the [Pedestal](https://github.com/pedestal/pedestal/tree/master/route) implementation. `router` function selects the most suitable implementation by inspecting the expanded routes. The implementation can be set manually using `:router` option, see [configuring routers](advanced/configuring_routers.md). Reitit ships with several different implementations for the `Router` protocol, originally based on the [Pedestal](https://github.com/pedestal/pedestal/tree/master/route) implementation. `router` function selects the most suitable implementation by inspecting the expanded routes. The implementation can be set manually using `:router` option, see [configuring routers](configuring_routers.md).
| router | description | | router | description |
| ------------------------------|-------------| | ------------------------------|-------------|

View file

@ -22,7 +22,7 @@ The default exception formatting uses `reitit.exception/exception`. It produces
## Pretty Errors ## Pretty Errors
```clj ```clj
[metosin/reitit-dev "0.5.0"] [metosin/reitit-dev "0.10.0"]
``` ```
For human-readable and developer-friendly exception messages, there is `reitit.dev.pretty/exception` (in the `reitit-dev` module). It is inspired by the lovely errors messages of [ELM](https://elm-lang.org/blog/compiler-errors-for-humans) and [ETA](https://twitter.com/jyothsnasrin/status/1037703436043603968) and uses [fipp](https://github.com/brandonbloom/fipp), [expound](https://github.com/bhb/expound) and [spell-spec](https://github.com/bhauman/spell-spec) for most of heavy lifting. 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.
@ -51,4 +51,4 @@ See the [validating route data](route_data_validation.md) page.
## Runtime Exception ## Runtime Exception
See [Exception Handling with Ring](exceptions.md). See [Exception Handling with Ring](../ring/exceptions.md).

View file

@ -75,6 +75,17 @@ Path-parameters are automatically coerced into strings, with the help of (curren
; :path-params {:id "1"}} ; :path-params {:id "1"}}
``` ```
In case you want to do something like generate a template path for documentation, you can disable url-encoding:
```clj
(r/match-by-name router ::user {:id "<id goes here>"} {:url-encode? false})
; #reitit.core.Match{:template "/api/user/:id"
; :data {:name :user/user}
; :path "/api/user/<id goes here>"
; :result nil
; :path-params {:id "<id goes here>"}}
```
There is also an exception throwing version: There is also an exception throwing version:
```clj ```clj

View file

@ -11,7 +11,8 @@ Route data is the key feature of reitit. Routes can have any map-like data attac
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 ```clj
(require '[reitit.core :as r]) (require '[reitit.core :as r])
@ -79,11 +80,13 @@ Resolved route tree:
; :name :user/ping}] ; :name :user/ping}]
; ["/api/admin/users" {:interceptors [::api] ; ["/api/admin/users" {:interceptors [::api]
; :roles #{:admin} ; :roles #{:admin}
; :name ::users} nil] ; :name ::users}]
; ["/api/admin/db" {:interceptors [::api ::db] ; ["/api/admin/db" {:interceptors [::api ::db]
; :roles #{:db-admin}}]] ; :roles #{:db-admin}}]]
``` ```
See also [nested parameter definitions for coercions](../ring/coercion.md#nested-parameter-definitions)
## Route Data Fragments ## 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. 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.

View file

@ -1,6 +1,9 @@
# Route Syntax # 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. 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: Simple route:
```clj ```clj
["/ping"] ["/ping" {:handler ping}]
``` ```
Two routes: Two routes with more data:
```clj ```clj
[["/ping"] [["/ping" {:handler ping
["/pong"]] :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 ```clj
[["/ping" ::ping] [["/users/:user-id" {:handler get-user}]
["/pong" {:name ::pong}]] ["/api/:version/ping" {:handler ping-version}]]
```
Routes with path parameters:
```clj
[["/users/:user-id"]
["/api/:version/ping"]]
``` ```
```clj ```clj
[["/users/{user-id}"] [["/users/{user-id}" {:handler get-user}]
["/files/file-{number}.pdf"]] ["/files/file-{number}.pdf" {:handler get-pdf}]]
``` ```
Route with catch-all parameter: Route with catch-all parameter:
```clj ```clj
["/public/*path"] ["/public/*path" {:handler get-file}]
``` ```
```clj ```clj
["/public/{*path}"] ["/public/{*path}" {:handler get-file}]
``` ```
Nested routes: Nested routes:
@ -55,9 +53,9 @@ Nested routes:
```clj ```clj
["/api" ["/api"
["/admin" {:middleware [::admin]} ["/admin" {:middleware [::admin]}
["" ::admin] ["" {:name ::admin}]
["/db" ::db]] ["/db" {:name ::db}]]
["/ping" ::ping]] ["/ping" {:name ::ping}]]
``` ```
Same routes flattened: 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: 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 ```clj
[["/api/:version"] [["/api/:version" {...}]
["/files/file-:number"] ["/files/file-:number" {...}]
["/user/:user-id/orders"]] ["/user/:user-id/orders" {...}]]
``` ```
Bracket path-parameters can start and stop anywhere in the path-string, the following character is used as a terminator. Bracket path-parameters can start and stop anywhere in the path-string, the following character is used as a terminator.
```clj ```clj
[["/api/{version}"] [["/api/{version}" {...}]
["/files/{name}.{extension}"] ["/files/{name}.{extension}" {...}]
["/user/{user-id}/orders"]] ["/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: Having multiple terminators after a bracket path-path parameter with identical path prefix will cause a compile-time error at router creation:
```clj ```clj
[["/files/file-{name}.pdf"] ;; terminator \. [["/files/file-{name}.pdf" {...}] ;; terminator \.
["/files/file-{name}-{version}.pdf"]] ;; terminator \- ["/files/file-{name}-{version}.pdf" {...}]] ;; terminator \-
``` ```
### Slash Free Routing ### Slash Free Routing
```clj ```clj
[["broker.{customer}.{device}.{*data}"] [["broker.{customer}.{device}.{*data}" {...}]
["events.{target}.{type}"]] ["events.{target}.{type}" {...}]]
``` ```
### Generating routes ### Generating routes
@ -132,7 +130,7 @@ Routes are just data, so it's easy to create them programmatically:
### Explicit path-parameter syntax ### Explicit path-parameter syntax
Router options `:syntax` allows the path-parameter syntax to be explicitely defined. It takes a keyword or set of keywords as a value. Valid values are `:colon` and `:bracket`. Default value is `#{:colon :bracket}`. Router options `:syntax` allows the path-parameter syntax to be explicitly defined. It takes a keyword or set of keywords as a value. Valid values are `:colon` and `:bracket`. Default value is `#{:colon :bracket}`.
With defaults: With defaults:

View file

@ -63,14 +63,6 @@ Route names:
; [:user/ping :user/user] ; [:user/ping :user/user]
``` ```
The compiled route tree:
```clj
(r/routes router)
; [["/api/ping" {:name :user/ping} nil]
; ["/api/user/:id" {:name :user/user} nil]]
```
### Composing ### Composing
As routes are defined as plain data, it's easy to merge multiple route trees into a single router As routes are defined as plain data, it's easy to merge multiple route trees into a single router
@ -85,9 +77,10 @@ As routes are defined as plain data, it's easy to merge multiple route trees int
["/ping" ::ping] ["/ping" ::ping]
["/db" ::db]]) ["/db" ::db]])
(r/router (def router
(r/router
[admin-routes [admin-routes
user-routes]) user-routes]))
``` ```
Merged route tree: Merged route tree:
@ -109,6 +102,6 @@ When router is created, the following steps are done:
* route arguments are expanded (via `:expand` option) * route arguments are expanded (via `:expand` option)
* routes are coerced (via `:coerce` options) * routes are coerced (via `:coerce` options)
* route tree is compiled (via `:compile` options) * route tree is compiled (via `:compile` options)
* [route conflicts](advanced/route_conflicts.md) are resolved (via `:conflicts` options) * [route conflicts](route_conflicts.md) are resolved (via `:conflicts` options)
* optionally, route data is validated (via `:validate` options) * optionally, route data is validated (via `:validate` options)
* [router implementation](../advanced/different_routers.md) is automatically selected (or forced via `:router` options) and created * [router implementation](../advanced/different_routers.md) is automatically selected (or forced via `:router` options) and created

View file

@ -12,7 +12,8 @@
metosin/reitit-swagger-ui metosin/reitit-swagger-ui
metosin/reitit-frontend metosin/reitit-frontend
metosin/reitit-sieppari metosin/reitit-sieppari
metosin/reitit-pedestal] metosin/reitit-pedestal
fi.metosin/reitit-openapi]
:cljdoc.doc/tree :cljdoc.doc/tree
[["Introduction" {:file "doc/README.md"}] [["Introduction" {:file "doc/README.md"}]
["Basics" {} ["Basics" {}
@ -28,9 +29,10 @@
["Coercion Explained" {:file "doc/coercion/coercion.md"}] ["Coercion Explained" {:file "doc/coercion/coercion.md"}]
["Plumatic Schema" {:file "doc/coercion/schema_coercion.md"}] ["Plumatic Schema" {:file "doc/coercion/schema_coercion.md"}]
["Clojure.spec" {:file "doc/coercion/clojure_spec_coercion.md"}] ["Clojure.spec" {:file "doc/coercion/clojure_spec_coercion.md"}]
["Data-specs" {:file "doc/coercion/data_spec_coercion.md"}]] ["Data-specs" {:file "doc/coercion/data_spec_coercion.md"}]
["Malli" {:file "doc/coercion/malli_coercion.md"}]]
["Ring" {} ["Ring" {}
["Ring-router" {:file "doc/ring/ring.md"}] ["Ring Router" {:file "doc/ring/ring.md"}]
["Reverse-routing" {:file "doc/ring/reverse_routing.md"}] ["Reverse-routing" {:file "doc/ring/reverse_routing.md"}]
["Default handler" {:file "doc/ring/default_handler.md"}] ["Default handler" {:file "doc/ring/default_handler.md"}]
["Slash handler" {:file "doc/ring/slash_handler.md"}] ["Slash handler" {:file "doc/ring/slash_handler.md"}]
@ -46,6 +48,7 @@
["Route Data Validation" {:file "doc/ring/route_data_validation.md"}] ["Route Data Validation" {:file "doc/ring/route_data_validation.md"}]
["Compiling Middleware" {:file "doc/ring/compiling_middleware.md"}] ["Compiling Middleware" {:file "doc/ring/compiling_middleware.md"}]
["Swagger Support" {:file "doc/ring/swagger.md"}] ["Swagger Support" {:file "doc/ring/swagger.md"}]
["OpenAPI Support" {:file "doc/ring/openapi.md"}]
["RESTful form methods" {:file "doc/ring/RESTful_form_methods.md"}]] ["RESTful form methods" {:file "doc/ring/RESTful_form_methods.md"}]]
["HTTP" {} ["HTTP" {}
["Interceptors" {:file "doc/http/interceptors.md"}] ["Interceptors" {:file "doc/http/interceptors.md"}]

View file

@ -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). 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 ## Example

View file

@ -1,6 +1,6 @@
# Data-spec Coercion # 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 ```clj
(require '[reitit.coercion.spec]) (require '[reitit.coercion.spec])

View file

@ -2,6 +2,10 @@
[Malli](https://github.com/metosin/malli) is data-driven Schema library for Clojure/Script. [Malli](https://github.com/metosin/malli) is data-driven Schema library for Clojure/Script.
## Default Syntax
By default, [Vector Syntax](https://github.com/metosin/malli#vector-syntax) is used:
```clj ```clj
(require '[reitit.coercion.malli]) (require '[reitit.coercion.malli])
(require '[reitit.coercion :as coercion]) (require '[reitit.coercion :as coercion])
@ -13,7 +17,7 @@
:coercion reitit.coercion.malli/coercion :coercion reitit.coercion.malli/coercion
:parameters {:path [:map :parameters {:path [:map
[:company string?] [:company string?]
[:user-id int?]]}] [:user-id int?]]}}]
{:compile coercion/compile-request-coercers})) {:compile coercion/compile-request-coercers}))
(defn match-by-path-and-coerce! [path] (defn match-by-path-and-coerce! [path]
@ -43,3 +47,78 @@ Failing coercion:
(match-by-path-and-coerce! "/metosin/users/ikitommi") (match-by-path-and-coerce! "/metosin/users/ikitommi")
; => ExceptionInfo Request coercion failed... ; => ExceptionInfo Request coercion failed...
``` ```
## Lite Syntax
Same using [Lite Syntax](https://github.com/metosin/malli#lite):
```clj
(def router
(r/router
["/:company/users/:user-id" {:name ::user-view
:coercion reitit.coercion.malli/coercion
:parameters {:path {:company string?
:user-id int?}}}]
{:compile coercion/compile-request-coercers}))
```
## Configuring coercion
Using `create` with options to create the coercion instead of `coercion`:
```clj
(require '[malli.util :as mu])
(reitit.coercion.malli/create
{:transformers {:body {:default reitit.coercion.malli/default-transformer-provider
:formats {"application/json" reitit.coercion.malli/json-transformer-provider}}
:string {:default reitit.coercion.malli/string-transformer-provider}
:response {:default reitit.coercion.malli/default-transformer-provider
:formats {"application/json" reitit.coercion.malli/json-transformer-provider}}}
;; set of keys to include in error messages
:error-keys #{:type :coercion :in #_:schema :value #_:errors :humanized #_:transformed}
;; support lite syntax?
:lite true
;; schema identity function (default: close all map schemas)
:compile mu/closed-schema
;; validate request & response
:validate true
;; top-level short-circuit to disable request & response coercion
:enabled true
;; strip-extra-keys (affects only predefined transformers)
:strip-extra-keys true
;; add/set default values
;; Can be false, true or a map of options to pass to malli.transform/default-value-transformer,
;; for example {:malli.transform/add-optional-keys true}
: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})}}})
```

View file

@ -13,30 +13,47 @@
./scripts/test.sh cljs ./scripts/test.sh cljs
``` ```
## Documentation ## Formatting
The documentation is built with [gitbook](https://toolchain.gitbook.com). To preview your changes locally:
```bash ```bash
npm install -g gitbook-cli clojure-lsp format
gitbook install clojure-lsp clean-ns
gitbook serve
``` ```
## To bump up version: ## Documentation
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! We use [Break Versioning][breakver]. Remember our promise: patch-level bumps never include breaking changes!
[breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md [breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md
```bash ```bash
# new version # create a release commit
./scripts/set-version "1.0.0" ./scripts/set-version "1.0.0"
./scripts/lein-modules install
# works # !!! update the changelog
lein test
# deploy to clojars git add -u
./scripts/lein-modules do clean, deploy clojars git commit -m "Release 1.0.0"
# push the commit
git push
# !!! check that tests pass on CI
``` ```
* 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.

View file

@ -8,9 +8,13 @@ history events
- Stateful wrapper for easy use of history integration - Stateful wrapper for easy use of history integration
- Optional [controller extension](./controllers.md) - 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 ## Core functions
`reitit.frontend` provides few useful functions wrapping core functions: `reitit.frontend` provides some useful functions wrapping core functions:
`match-by-path` version which parses a URI using JavaScript, including `match-by-path` version which parses a URI using JavaScript, including
query-string, and also [coerces the parameters](../coercion/coercion.md). query-string, and also [coerces the parameters](../coercion/coercion.md).
@ -23,7 +27,8 @@ enabled.
`match-by-name` and `match-by-name!` with optional `path-paramers` and `match-by-name` and `match-by-name!` with optional `path-paramers` and
logging errors to `console.warn` instead of throwing errors to prevent 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 ## Next

View file

@ -2,8 +2,19 @@
Reitit includes two browser history integrations. Reitit includes two browser history integrations.
Functions follow HTML5 History API: `push-state` to change route, `replace-state` Main functions are `navigate` and `set-query`. Navigate is used to navigate
to change route without leaving previous entry in browser history. to named routes, and the options parameter can be used to control all
parameters and if `pushState` or `replaceState` should be used to control
browser history stack. The `set-query` function can be used to change
or modify query parameters for the current route, it takes either map of
new query params or function from old params to the new params.
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 router
@ -30,6 +41,7 @@ event handling:
```clj ```clj
(rfe/start! (rfe/start!
router router
on-navigate-fn
{:use-fragment false {:use-fragment false
:ignore-anchor-click? (fn [router e el uri] :ignore-anchor-click? (fn [router e el uri]
;; Add additional check on top of the default checks ;; Add additional check on top of the default checks
@ -53,7 +65,7 @@ event handler for page change events.
## History manipulation ## 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: go back or forwards, but calling History API functions directly should work:
``` ```

59
doc/frontend/coercion.md Normal file
View 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}}}
```

View file

@ -77,9 +77,9 @@ route data is merged. Consider this route tree:
```cljs ```cljs
["/" {:controllers [{:start (fn [_] (js/console.log "root start"))}]} ["/" {:controllers [{:start (fn [_] (js/console.log "root start"))}]}
["/item/:id" ["/item/:id"
{:controllers [{:params (fn [match] (get-in match [:path-params :id])) {:controllers [{:parameters {:path [:id]}
:start (fn [item-id] (js/console.log "item start" item-id)) :start (fn [parameters] (js/console.log "item start" (-> parameters :path :id)))
:stop (fn [item-id] (js/console.log "item stop" item-id))}]}]] :stop (fn [parameters] (js/console.log "item stop" (-> parameters :path :id)))}]}]]
``` ```

View file

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

View file

@ -1,18 +1,18 @@
# Interceptors # Interceptors
Reitit also support for [interceptors](http://pedestal.io/reference/interceptors) as an alternative to using middleware. Basic interceptor handling is implemented in `reitit.interceptor` package. There is no interceptor executor shipped, but you can use libraries like [Pedestal Interceptor](https://github.com/pedestal/pedestal/tree/master/interceptor) or [Sieppari](https://github.com/metosin/sieppari) to execute the chains. Reitit has also support for [interceptors](http://pedestal.io/reference/interceptors) as an alternative to using middleware. Basic interceptor handling is implemented in `reitit.interceptor` package. There is no interceptor executor shipped, but you can use libraries like [Pedestal Interceptor](https://github.com/pedestal/pedestal/tree/master/interceptor) or [Sieppari](https://github.com/metosin/sieppari) to execute the chains.
## Reitit-http ## Reitit-http
```clj ```clj
[metosin/reitit-http "0.5.0"] [metosin/reitit-http "0.10.0"]
``` ```
An module for http-routing using interceptors instead of middleware. Builds on top of the [`reitit-ring`](../ring/ring.md) module having all the same features. A module for http-routing using interceptors instead of middleware. Builds on top of the [`reitit-ring`](../ring/ring.md) module having all the same features.
The differences: The differences:
* `:interceptors` key in used in route data instead of `:middleware` * `:interceptors` key used in route data instead of `:middleware`
* `reitit.http/http-router` requires an extra option `:executor` of type `reitit.interceptor/Executor` to execute the interceptor chain * `reitit.http/http-router` requires an extra option `:executor` of type `reitit.interceptor/Executor` to execute the interceptor chain
* optionally, a routing interceptor can be used - it enqueues the matched interceptors into the context. See `reitit.http/routing-interceptor` for details. * optionally, a routing interceptor can be used - it enqueues the matched interceptors into the context. See `reitit.http/routing-interceptor` for details.

View file

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

View file

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

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

View file

@ -76,7 +76,7 @@ The routing sample taken from [bide](https://github.com/funcool/bide) README:
(r/match-by-path routes "/workspace/1/1"))) (r/match-by-path routes "/workspace/1/1")))
``` ```
Based on the [perf tests](https://github.com/metosin/reitit/tree/master/perf-test/clj/reitit/perf/bide_perf_test.clj), the first (static path) lookup is 300-500x faster and the second (wildcard path) lookup is 18-110x faster that the other tested routing libs (Ataraxy, Bidi, Compojure and Pedestal). Based on the [perf tests](https://github.com/metosin/reitit/blob/master/perf-test/clj/reitit/bide_perf_test.clj), the first (static path) lookup is 300-500x faster and the second (wildcard path) lookup is 18-110x faster that the other tested routing libs (Ataraxy, Bidi, Compojure and Pedestal).
But, the example is too simple for any real benchmark. Also, some of the libraries always match on the `:request-method` too and by doing so, do more work than just match by path. Compojure does most work also by invoking the handler. But, the example is too simple for any real benchmark. Also, some of the libraries always match on the `:request-method` too and by doing so, do more work than just match by path. Compojure does most work also by invoking the handler.
@ -114,7 +114,7 @@ A quick poke to [the fast routers in Go](https://github.com/julienschmidt/go-htt
### Faster! ### Faster!
By default, `reitit.ring/ring-router`, `reitit.http/ring-router` and `reitit.http/routing-interceptor` inject both `Match` and `Router` into the request. You can remove the injections setting options `:inject-match?` and `:inject-router?` to `false`. This saves some tens of nanos (with the hw described above). By default, `reitit.ring/router`, `reitit.http/router` and `reitit.http/routing-interceptor` inject both `Match` and `Router` into the request. You can remove the injections setting options `:inject-match?` and `:inject-router?` to `false`. This saves some tens of nanos (with the hw described above).
```clj ```clj
(require '[reitit.ring :as ring]) (require '[reitit.ring :as ring])

View file

@ -8,9 +8,10 @@ We can do this with middleware in reitit like this:
```clj ```clj
(defn- hidden-method (defn- hidden-method
[request] [request]
(keyword (some-> (or (get-in request [:form-params "_method"]) ;; look for "_method" field in :form-params
(or (get-in request [:form-params "_method"]) ;; look for "_method" field in :form-params (get-in request [:multipart-params "_method"])) ;; or in :multipart-params
(get-in request [:multipart-params "_method"])))) ;; or in :multipart-params clojure.string/lower-case
keyword))
(def wrap-hidden-method (def wrap-hidden-method
{:name ::wrap-hidden-method {:name ::wrap-hidden-method

View file

@ -5,12 +5,14 @@ Basic coercion is explained in detail [in the Coercion Guide](../coercion/coerci
The following request parameters are currently supported: The following request parameters are currently supported:
| type | request source | | type | request source |
|-----------|------------------| |--------------|--------------------------------------------------|
| `:query` | `:query-params` | | `:query` | `:query-params` |
| `:body` | `:body-params` | | `:body` | `:body-params` |
| `:request` | `:body-params`, allows per-content-type coercion |
| `:form` | `:form-params` | | `:form` | `:form-params` |
| `:header` | `:header-params` | | `:header` | `:header-params` |
| `:path` | `:path-params` | | `:path` | `:path-params` |
| `:multipart` | `:multipart-params`, see [Default Middleware](default_middleware.md) |
To enable coercion, the following things need to be done: To enable coercion, the following things need to be done:
@ -35,11 +37,11 @@ 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. 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`. 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`.
Handler can access the coerced parameters can be read under `:parameters` key in the request. Handlers can access the coerced parameters via the `:parameters` key in the request.
```clj ```clj
(require '[reitit.coercion.schema]) (require '[reitit.coercion.schema])
@ -52,7 +54,8 @@ Handler can access the coerced parameters can be read under `:parameters` key in
:parameters {:query {:x s/Int} :parameters {:query {:x s/Int}
:body {:y s/Int} :body {:y s/Int}
:path {:z 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]}] :handler (fn [{:keys [parameters]}]
(let [total (+ (-> parameters :query :x) (let [total (+ (-> parameters :query :x)
(-> parameters :body :y) (-> parameters :body :y)
@ -60,6 +63,54 @@ Handler can access the coerced parameters can be read under `:parameters` key in
{:status 200 {:status 200
:body {:total total}}))}) :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]]}
```
### Differences in behaviour for different parameters
All parameter coercions *except* `:body`:
1. Allow keys outside the schema (by opening up the schema using eg. `malli.util/open-schema`)
2. Keywordize the keys (ie. header & query parameter names) of the input before coercing
In contrast, the `:body` coercion:
1. Uses the specified schema
* depending on the coercion, it can be configured as open or closed, see specific coercion docs for details
2. Does not keywordize the keys of the input before coercion
* however, coercions like malli might do the keywordization when coercing json bodies, depending on configuration
This admittedly confusing behaviour is retained currently due to
backwards compatibility reasons. It can be configured by passing
option `:reitit.coercion/parameter-coercion` to `reitit.ring/router`
or `reitit.coercion/compile-request-coercers`. See also:
`reitit.coercion/default-parameter-coercion`.
## Coercion Middleware ## Coercion Middleware
@ -71,7 +122,7 @@ Defining a coercion for a route data doesn't do anything, as it's just data. We
### Full example ### Full example
Here's an full example for applying coercion with Reitit, Ring and Schema: Here is a full example for applying coercion with Reitit, Ring and Schema:
```clj ```clj
(require '[reitit.ring.coercion :as rrc]) (require '[reitit.ring.coercion :as rrc])
@ -148,9 +199,56 @@ Invalid response:
; :in [:response :body]}} ; :in [:response :body]}}
``` ```
## Per-content-type coercion
You can also specify request and response body schemas per
content-type. These are also read by the [OpenAPI
feature](./openapi.md) when generating api docs. The syntax for this
is:
```clj
(def app
(ring/ring-handler
(ring/router
["/api"
["/example" {:post {:coercion reitit.coercion.schema/coercion
:request {:content {"application/json" {:schema {:y s/Int}}
"application/edn" {:schema {:z s/Int}}
;; default if no content-type matches:
:default {:schema {:yy s/Int}}}}
:responses {200 {:content {"application/json" {:schema {:w s/Int}}
"application/edn" {:schema {:x s/Int}}
:default {:schema {:ww s/Int}}}}}
:handler ...}}]]
{:data {:muuntaja muuntaja.core/instance
:middleware [reitit.ring.middleware.muuntaja/format-middleware
reitit.ring.coercion/coerce-exceptions-middleware
reitit.ring.coercion/coerce-request-middleware
reitit.ring.coercion/coerce-response-middleware]}})))
```
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 ## Pretty printing spec errors
Spec problems are exposed as-is into request & response coercion errors, enabling pretty-printers like [expound](https://github.com/bhb/expound) to be used: 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:
```clj ```clj
(require '[reitit.ring :as ring]) (require '[reitit.ring :as ring])
@ -216,7 +314,7 @@ Spec problems are exposed as-is into request & response coercion errors, enablin
### Optimizations ### Optimizations
The coercion middleware are [compiled against a route](compiling_middleware.md). In the middleware compilation step the actual coercer implementations are constructed for the defined models. Also, the middleware doesn't mount itself if a route doesn't have `:coercion` and `:parameters` or `:responses` defined. The coercion middlewares are [compiled against a route](compiling_middleware.md). In the middleware compilation step the actual coercer implementations are constructed for the defined models. Also, the middleware doesn't mount itself if a route doesn't have `:coercion` and `:parameters` or `:responses` defined.
We can query the compiled middleware chain for the routes: We can query the compiled middleware chain for the routes:

View file

@ -1,12 +1,12 @@
# Compiling Middleware # Compiling Middleware
The [dynamic extensions](dynamic_extensions.md) is a easy way to extend the system. To enable fast lookups into route data, we can compile them into any shape (records, functions etc.) we want, enabling fast access at request-time. The [dynamic extensions](dynamic_extensions.md) are an easy way to extend the system. To enable fast lookup of route data, we can compile them into any shape (records, functions etc.), enabling fast access at request-time.
But, we can do much better. As we know the exact route that middleware/interceptor is linked to, we can pass the (compiled) route information into the middleware at creation-time. It can do local reasoning: extract and transform relevant data just for it and pass the optimized data into the actual request-handler via a closure - yielding much faster runtime processing. Middleware can also decide not to mount itself by returning `nil`. Why mount a `wrap-enforce-roles` middleware for a route if there are no roles required for it? But, we can do much better. As we know the exact route that a middleware/interceptor is linked to, we can pass the (compiled) route information into the middleware at creation-time. It can do local reasoning: Extract and transform relevant data just for it and pass the optimized data into the actual request-handler via a closure - yielding much faster runtime processing. A middleware can also decide not to mount itself by returning `nil`. (E.g. Why mount a `wrap-enforce-roles` middleware for a route if there are no roles required for it?)
To enable this we use [middleware records](data_driven_middleware.md) `:compile` key instead of the normal `:wrap`. `:compile` expects a function of `route-data router-opts => ?IntoMiddleware`. To enable this we use [middleware records](data_driven_middleware.md) `:compile` key instead of the normal `:wrap`. `:compile` expects a function of `route-data router-opts => ?IntoMiddleware`.
To demonstrate the two approaches, below are response coercion middleware written as normal ring middleware function and as middleware record with `:compile`. To demonstrate the two approaches, below is the response coercion middleware written as normal ring middleware function and as middleware record with `:compile`.
## Normal Middleware ## Normal Middleware
@ -27,8 +27,8 @@ To demonstrate the two approaches, below are response coercion middleware writte
coercion (-> match :data :coercion) coercion (-> match :data :coercion)
opts (-> match :data :opts)] opts (-> match :data :opts)]
(if (and coercion responses) (if (and coercion responses)
(let [coercers (response-coercers coercion responses opts)] (let [coercer (response-coercer coercion responses opts)]
(coerce-response coercers request response)) (coercer request response))
response))) response)))
([request respond raise] ([request respond raise]
(let [method (:request-method request) (let [method (:request-method request)
@ -37,8 +37,8 @@ To demonstrate the two approaches, below are response coercion middleware writte
coercion (-> match :data :coercion) coercion (-> match :data :coercion)
opts (-> match :data :opts)] opts (-> match :data :opts)]
(if (and coercion responses) (if (and coercion responses)
(let [coercers (response-coercers coercion responses opts)] (let [coercer (response-coercer coercion responses opts)]
(handler request #(respond (coerce-response coercers request %)))) (handler request #(respond (coercer request %))))
(handler request respond raise)))))) (handler request respond raise))))))
``` ```
@ -60,13 +60,13 @@ To demonstrate the two approaches, below are response coercion middleware writte
:spec ::rs/responses :spec ::rs/responses
:compile (fn [{:keys [coercion responses]} opts] :compile (fn [{:keys [coercion responses]} opts]
(if (and coercion responses) (if (and coercion responses)
(let [coercers (coercion/response-coercers coercion responses opts)] (let [coercer (coercion/response-coercer coercion responses opts)]
(fn [handler] (fn [handler]
(fn (fn
([request] ([request]
(coercion/coerce-response coercers request (handler request))) (coercer request (handler request)))
([request respond raise] ([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. It has 50% less code, it's much easier to reason about and is much faster.

View file

@ -1,8 +1,8 @@
# Content Negotiation # Content Negotiation
Wrapper for [Muuntaja](https://github.com/metosin/muuntaja) middleware for content-negotiation, request decoding and response encoding. Takes explicit configuration via `:muuntaja` key in route data. Emit's [swagger](swagger.md) `:produces` and `:consumes` definitions automatically based on the Muuntaja configuration. Wrapper for [Muuntaja](https://github.com/metosin/muuntaja) middleware for content negotiation, request decoding and response encoding. Takes explicit configuration via `:muuntaja` key in route data. Emits [swagger](swagger.md) `:produces` and `:consumes` definitions automatically based on the Muuntaja configuration.
Negotiates a request body based on `Content-Type` header and response body based on `Accept`, `Accept-Charset` headers. Publishes the negotiation results as `:muuntaja/request` and `:muuntaja/response` keys into the request. Negotiates a request body based on `Content-Type` header and response body based on `Accept` and `Accept-Charset` headers. Publishes the negotiation results as `:muuntaja/request` and `:muuntaja/response` keys into the request.
Decodes the request body into `:body-params` using the `:muuntaja/request` key in request if the `:body-params` doesn't already exist. Decodes the request body into `:body-params` using the `:muuntaja/request` key in request if the `:body-params` doesn't already exist.
@ -84,10 +84,12 @@ Server: Jetty(9.2.21.v20170120)
<kikka>kukka</kikka> <kikka>kukka</kikka>
``` ```
You can also specify request and response schemas per content-type. See [Coercion](coercion.md) and [OpenAPI Support](openapi.md).
## Changing default parameters ## Changing default parameters
The current JSON formatter used by `reitit` already have the option to parse keys as `keyword` which is a sane default in Clojure. However, if you would like to parse all the `double` as `bigdecimal` you'd need to change an option of the [JSON formatter](https://github.com/metosin/jsonista) The current JSON formatter used by `reitit` already has the option to parse keys as `keyword` which is a sane default in Clojure. However, if you would like to parse all the `double` as `bigdecimal` you'd need to change an option of the [JSON formatter](https://github.com/metosin/jsonista)
```clj ```clj
@ -102,7 +104,7 @@ The current JSON formatter used by `reitit` already have the option to parse key
Now you should change the `m/instance` installed in the router with the `new-muuntaja-instance`. Now you should change the `m/instance` installed in the router with the `new-muuntaja-instance`.
You can find more options for [JSON](https://cljdoc.org/d/metosin/jsonista/0.2.5/api/jsonista.core#object-mapper) and [EDN]. Here you can find more options for [JSON](https://cljdoc.org/d/metosin/jsonista/0.2.5/api/jsonista.core#object-mapper) and EDN.
## Adding custom encoder ## Adding custom encoder
@ -125,14 +127,14 @@ The example below is from `muuntaja` explaining how to add a custom encoder to p
``` ```
## Adding all together ## Putting it all together
If you inspect `m/default-options` it's only a map, therefore you can compose your new muuntaja instance with as many options as you need it. If you inspect `m/default-options` you'll find it's only a map. This means you can compose your new muuntaja instance with as many options as you need.
```clj ```clj
(def new-muuntaja (def new-muuntaja
(m/create (m/create
(-> m/default-options (-> m/default-options
(assoc-in [:formats "application/json" :decoder-opts :bigdecimals] true) (assoc-in [:formats "application/json" :decoder-opts :bigdecimals] true)
(assoc-in [:formats "application/json" :encoder-opts :data-format] "yyyy-MM-dd")))) (assoc-in [:formats "application/json" :encoder-opts :date-format] "yyyy-MM-dd"))))
``` ```

View file

@ -1,19 +1,21 @@
# Data-driven Middleware # Data-driven Middleware
Ring [defines middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware) as a function of type `handler & args => request => response`. It's relatively easy to understand and enables good performance. Downside is that the middleware-chain is just a opaque function, making things like debugging and composition hard. It's too easy to apply the middleware in wrong order. 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: Reitit defines middleware as data:
1. Middleware can be defined as first-class data entries 1. A middleware can be defined as first-class data entries
2. Middleware can be mounted as a [duct-style](https://github.com/duct-framework/duct/wiki/Configuration) vector (of middleware) 2. A middleware can be mounted as a [duct-style](https://github.com/duct-framework/duct/wiki/Configuration) vector (of middlewares)
4. Middleware can be optimized & [compiled](compiling_middleware.md) against an endpoint 4. A middleware can be optimized & [compiled](compiling_middleware.md) against an endpoint
3. Middleware chain can be transformed by the router 3. A middleware chain can be transformed by the router
## Middleware as data ## Middleware as data
All values in the `:middleware` vector in the route data are expanded into `reitit.middleware/Middleware` Records with using the `reitit.middleware/IntoMiddleware` Protocol. By default, functions, maps and `Middleware` records are allowed. All values in the `:middleware` vector of route data are expanded into `reitit.middleware/Middleware` Records by using the `reitit.middleware/IntoMiddleware` Protocol. By default, functions, maps and `Middleware` records are allowed.
Records can have arbitrary keys, but the following keys have a special purpose: Records can have arbitrary keys, but the following keys have special purpose:
| key | description | | key | description |
| ---------------|-------------| | ---------------|-------------|
@ -22,13 +24,13 @@ Records can have arbitrary keys, but the following keys have a special purpose:
| `:wrap` | The actual middleware function of `handler & args => request => response` | `:wrap` | The actual middleware function of `handler & args => request => response`
| `:compile` | Middleware compilation function, see [compiling middleware](compiling_middleware.md). | `:compile` | Middleware compilation function, see [compiling middleware](compiling_middleware.md).
Middleware Records are accessible in their raw form in the compiled route results, thus available for inventories, creating api-docs etc. Middleware Records are accessible in their raw form in the compiled route results, and thus are available for inventories, creating api-docs, etc.
For the actual request processing, the Records are unwrapped into normal functions and composed into a middleware function chain, yielding zero runtime penalty. For the actual request processing, the Records are unwrapped into normal functions and composed into a middleware function chain, yielding zero runtime penalty.
### Creating Middleware ### Creating Middleware
The following produce identical middleware runtime function. The following examples produce identical middleware runtime functions.
### Function ### Function
@ -77,7 +79,7 @@ The following produce identical middleware runtime function.
:handler handler}}]]))) :handler handler}}]])))
``` ```
All the middleware are applied correctly: All the middlewares are applied correctly:
```clj ```clj
(app {:request-method :get, :uri "/api/ping"}) (app {:request-method :get, :uri "/api/ping"})
@ -86,7 +88,7 @@ All the middleware are applied correctly:
## Compiling middleware ## Compiling middleware
Middleware can be optimized against an endpoint using [middleware compilation](compiling_middleware.md). Middlewares can be optimized against an endpoint using [middleware compilation](compiling_middleware.md).
## Ideas for the future ## Ideas for the future

View file

@ -1,6 +1,6 @@
# Default handler # Default handler
By default, if no routes match, `nil` is returned, which is not valid response in Ring: By default, if no routes match, `nil` is returned, which is not a valid response in Ring:
```clj ```clj
(require '[reitit.ring :as ring]) (require '[reitit.ring :as ring])

View file

@ -1,10 +1,10 @@
# Default Middleware # Default Middleware
```clj ```clj
[metosin/reitit-middleware "0.5.0"] [metosin/reitit-middleware "0.10.0"]
``` ```
Any Ring middleware can be used with `reitit-ring`, but using data-driven middleware is preferred as they are easier to manage and in many cases, yield better performance. `reitit-middleware` contains a set of common ring middleware, lifted into data-driven middleware. 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.
* [Parameter Handling](#parameters-handling) * [Parameter Handling](#parameters-handling)
* [Exception Handling](#exception-handling) * [Exception Handling](#exception-handling)
@ -17,12 +17,14 @@ 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 `reitit.ring.middleware.parameters/parameters-middleware` to capture query- and form-params. Wraps
`ring.middleware.params/wrap-params`. `ring.middleware.params/wrap-params`.
**NOTE**: 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.
## Exception Handling ## Exception Handling
See [Exception Handling with Ring](exceptions.md). See [Exception Handling with Ring](exceptions.md).
## Content Negotiation
See [Content Negotiation](content_negotiation.md).
## Multipart Request Handling ## Multipart Request Handling
Wrapper for [Ring Multipart Middleware](https://github.com/ring-clojure/ring/blob/master/ring-core/src/ring/middleware/multipart_params.clj). Emits swagger `:consumes` definitions automatically. Wrapper for [Ring Multipart Middleware](https://github.com/ring-clojure/ring/blob/master/ring-core/src/ring/middleware/multipart_params.clj). Emits swagger `:consumes` definitions automatically.
@ -55,4 +57,4 @@ Partial sample output:
## Example app ## Example app
See an example app with the default middleware in action: https://github.com/metosin/reitit/blob/master/examples/ring-swagger/src/example/server.clj. See an example app with the default middleware in action: <https://github.com/metosin/reitit/blob/master/examples/ring-malli-swagger/src/example/server.clj>.

View file

@ -1,8 +1,8 @@
# Dynamic Extensions # Dynamic Extensions
`ring-handler` injects the `Match` into a request and it can be extracted at runtime with `reitit.ring/get-match`. This can be used to build ad-hoc extensions to the system. `ring-handler` injects the `Match` into a request and it can be extracted at runtime with `reitit.ring/get-match`. This can be used to build ad hoc extensions to the system.
Example middleware to guard routes based on user roles: This example shows a middleware to guard routes based on user roles:
```clj ```clj
(require '[reitit.ring :as ring]) (require '[reitit.ring :as ring])

View file

@ -1,10 +1,10 @@
# Exception Handling with Ring # Exception Handling with Ring
```clj ```clj
[metosin/reitit-middleware "0.5.0"] [metosin/reitit-middleware "0.10.0"]
``` ```
Exceptions thrown in router creation can be [handled with custom exception handler](../basics/error_messages.md). By default, exceptions thrown at runtime from a handler or a middleware are not caught by the `reitit.ring/ring-handler`. A good practise is a have an top-level exception handler to log and format the errors for clients. Exceptions thrown in router creation can be [handled with custom exception handler](../basics/error_messages.md). By default, exceptions thrown at runtime from a handler or a middleware are not caught by the `reitit.ring/ring-handler`. A good practice is to have a top-level exception handler to log and format errors for clients.
```clj ```clj
(require '[reitit.ring.middleware.exception :as exception]) (require '[reitit.ring.middleware.exception :as exception])
@ -36,7 +36,7 @@ A preconfigured middleware using `exception/default-handlers`. Catches:
### `exception/create-exception-middleware` ### `exception/create-exception-middleware`
Creates the exception-middleware with custom options. Takes a map of `identifier => exception request => response` that is used to select the exception handler for the thrown/raised exception identifier. Exception identifier is either a `Keyword` or a Exception Class. Creates the exception-middleware with custom options. Takes a map of `identifier => exception request => response` that is used to select the exception handler for the thrown/raised exception identifier. Exception identifier is either a `Keyword` or an Exception Class.
The following handlers are available by default: The following handlers are available by default:
@ -55,7 +55,7 @@ The handler is selected from the options map by exception identifier in the foll
2) Class of exception 2) Class of exception
3) `:type` ancestors of exception ex-data 3) `:type` ancestors of exception ex-data
4) Super Classes of exception 4) Super Classes of exception
5) The ::default handler 5) The `::default` handler
```clj ```clj
;; type hierarchy ;; type hierarchy
@ -94,7 +94,7 @@ The handler is selected from the options map by exception identifier in the foll
(def app (def app
(ring/ring-handler (ring/ring-handler
(ring/router (ring/router
["/fail" (fn [_] (throw (ex-info "fail" {:type ::failue})))] ["/fail" (fn [_] (throw (ex-info "fail" {:type ::failure})))]
{:data {:middleware [exception-middleware]}}))) {:data {:middleware [exception-middleware]}})))
(app {:request-method :get, :uri "/fail"}) (app {:request-method :get, :uri "/fail"})
@ -102,6 +102,6 @@ The handler is selected from the options map by exception identifier in the foll
; => {:status 500, ; => {:status 500,
; :body {:message "default" ; :body {:message "default"
; :exception clojure.lang.ExceptionInfo ; :exception clojure.lang.ExceptionInfo
; :data {:type :user/failue} ; :data {:type :user/failure}
; :uri "/fail"}} ; :uri "/fail"}}
``` ```

View file

@ -52,6 +52,20 @@ Router creation fails fast if the registry doesn't contain the middleware:
;| :bonus | reitit.ring_test$wrap_bonus@59fddabb | ;| :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? ## 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`. 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`.

214
doc/ring/openapi.md Normal file
View file

@ -0,0 +1,214 @@
# OpenAPI Support
**Stability: alpha**
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
[ring-spec-swagger](../../examples/ring-spec-swagger)
examples also
have OpenAPI documentation.
## OpenAPI data
The following route data keys contribute to the generated swagger specification:
| key | description |
| ---------------|-------------|
| :openapi | map of any openapi data. Can contain keys like `:deprecated`.
| :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:
| key | description |
| --------------|-------------|
| :parameters | optional input parameters for a route, in a format defined by the coercion
| :request | optional description of body parameters, possibly per content-type
| :responses | optional descriptions of responses, in a format defined by coercion
## Per-content-type coercions
Use `:request` coercion (instead of `:body`) to unlock
per-content-type coercions. This also lets you specify multiple named
examples. See [Coercion](coercion.md) for more info. See also [the
openapi example](../../examples/openapi).
```clj
["/pizza"
{:get {:summary "Fetch a pizza | Multiple content-types, multiple examples"
:responses {200 {:description "Fetch a pizza as json or EDN"
:content {"application/json" {:schema [:map
[:color :keyword]
[:pineapple :boolean]]
:examples {:white {:description "White pizza with pineapple"
:value {:color :white
:pineapple true}}
:red {:description "Red pizza"
:value {:color :red
:pineapple false}}}}
"application/edn" {:schema [:map
[:color :keyword]
[:pineapple :boolean]]
:examples {:red {:description "Red pizza with pineapple"
:value (pr-str {:color :red :pineapple true})}}}}}}
```
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.
You can use the `:openapi` route data key of the `create-openapi-handler` route
to populate the top level of the OpenAPI spec.
Example:
```
["/openapi.json"
{:get {:handler (openapi/create-openapi-handler)
:openapi {:info {:title "my nice api" :version "0.0.1"}}
:no-doc true}}]
```
If you need to post-process the generated spec, just wrap the handler with a custom `Middleware` or an `Interceptor`.
## 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. See `reitit.swagger-ui/create-swagger-ui-handle`
## 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.

View file

@ -5,14 +5,14 @@
Read more about the [Ring Concepts](https://github.com/ring-clojure/ring/wiki/Concepts). Read more about the [Ring Concepts](https://github.com/ring-clojure/ring/wiki/Concepts).
```clj ```clj
[metosin/reitit-ring "0.5.0"] [metosin/reitit-ring "0.10.0"]
``` ```
## `reitit.ring/ring-router` ## `reitit.ring/router`
`ring-router` is a higher order router, which adds support for `:request-method` based routing, [handlers](https://github.com/ring-clojure/ring/wiki/Concepts#handlers) and [middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware). `reitit.ring/router` is a higher order router, which adds support for `:request-method` based routing, [handlers](https://github.com/ring-clojure/ring/wiki/Concepts#handlers) and [middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware).
It accepts the following options: It accepts the following options:
| key | description | | key | description |
| ----------------------------------------|-------------| | ----------------------------------------|-------------|
@ -33,7 +33,7 @@ Example router:
["/ping" {:get handler}])) ["/ping" {:get handler}]))
``` ```
Match contains `:result` compiled by the `ring-router`: Match contains `:result` compiled by `reitit.ring/router`:
```clj ```clj
(require '[reitit.core :as r]) (require '[reitit.core :as r])
@ -49,11 +49,11 @@ Match contains `:result` compiled by the `ring-router`:
## `reitit.ring/ring-handler` ## `reitit.ring/ring-handler`
Given a `ring-router`, optional default-handler & options, `ring-handler` function will return a valid ring handler supporting both synchronous and [asynchronous](https://www.booleanknot.com/blog/2016/07/15/asynchronous-ring.html) request handling. The following options are available: Given a router from `reitit.ring/router`, optional default-handler & options, `ring-handler` function will return a valid ring handler supporting both synchronous and [asynchronous](https://www.booleanknot.com/blog/2016/07/15/asynchronous-ring.html) request handling. The following options are available:
| key | description | | key | description |
| ------------------|-------------| | ------------------|-------------|
| `:middleware` | Optional sequence of middleware that wrap the ring-handler" | `:middleware` | Optional sequence of middlewares that wrap the ring-handler
| `:inject-match?` | Boolean to inject `match` into request under `:reitit.core/match` key (default true) | `:inject-match?` | Boolean to inject `match` into request under `:reitit.core/match` key (default true)
| `:inject-router?` | Boolean to inject `router` into request under `:reitit.core/router` key (default true) | `:inject-router?` | Boolean to inject `router` into request under `:reitit.core/router` key (default true)
@ -141,7 +141,7 @@ Name-based reverse routing:
# Middleware # 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` 1. normal ring middleware function `handler -> request -> response`
2. vector of middleware function `[handler args*] -> request -> response` and it's arguments 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 (def app
(ring/ring-handler (ring/ring-handler
(ring/router (ring/router
["/api" {:middleware [[mw :api]]} ["/api" {:middleware [[wrap :api]]}
["/get" {:get handler}]]) ["/get" {:get handler}]])
nil nil
{:middleware [[mw :top]]})) {:middleware [[wrap :top]]}))
(app {:request-method :get, :uri "/api/get"}) (app {:request-method :get, :uri "/api/get"})
; {:status 200, :body [:top :api :ok]} ; {: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!

View file

@ -1,8 +1,12 @@
# Static Resources (Clojure Only) # Static Resources (Clojure Only)
Static resources can be served using `reitit.ring/create-resource-handler`. It takes optionally an options map and returns a ring handler to serve files from Classpath. Static resources can be served by using the following two functions:
There are two options to serve the files. * `reitit.ring/create-resource-handler`, which returns a Ring handler that serves files from classpath, and
* `reitit.ring/create-file-handler`, which returns a Ring handler that servers files from file system
There are two ways to mount the handlers.
The examples below use `reitit.ring/create-resource-handler`, but `reitit.ring/create-file-handler` works the same way.
## Internal routes ## Internal routes
@ -33,7 +37,9 @@ To serve static files with conflicting routes, e.g. `"/*"`, one needs to disable
## External routes ## External routes
A better way to serve files from conflicting paths, e.g. `"/*"`, is to serve them from the default-handler. One can compose multiple default locations using `ring-handler`. This way, they are only served if none of the actual routes have matched. A better way to serve files from conflicting paths, e.g. `"/*"`, is to serve them from the default-handler.
One can compose multiple default locations using `reitit.ring/ring-handler`.
This way, they are only served if none of the actual routes have matched.
```clj ```clj
(ring/ring-handler (ring/ring-handler
@ -46,21 +52,21 @@ A better way to serve files from conflicting paths, e.g. `"/*"`, is to serve the
## Configuration ## Configuration
`reitit.ring/create-resource-handler` takes optionally an options map to configure how the files are being served. `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 | | key | description |
| -----------------|-------------| | --------------------|-------------|
| :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:` | :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:`
| :root | optional resource root, defaults to `\"public\"` | :root | optional resource root, defaults to `\"public\"`
| :path | optional path to mount the handler to. Works only if mounted outside of a router. | :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 | :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-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) | :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)
### TODO ### TODO
* support for things like `:cache`, `:etag`, `:last-modified?`, and `:gzip` * support for things like `:cache`, `:etag`, `:last-modified?`, and `:gzip`
* support for ClojureScript * support for ClojureScript
* serve from file-system

View file

@ -1,12 +1,14 @@
# Swagger Support # Swagger Support
``` ```
[metosin/reitit-swagger "0.5.0"] [metosin/reitit-swagger "0.10.0"]
``` ```
Reitit supports [Swagger2](https://swagger.io/) documentation, thanks to [schema-tools](https://github.com/metosin/schema-tools) and [spec-tools](https://github.com/metosin/spec-tools). Documentation is extracted from route definitions, coercion `:parameters` and `:responses` and from a set of new documentation keys. 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.
To enable swagger-documentation for a ring-router: See also: [OpenAPI support](openapi.md).
To enable swagger-documentation for a Ring router:
1. annotate your routes with swagger-data 1. annotate your routes with swagger-data
2. mount a swagger-handler to serve the swagger-spec 2. mount a swagger-handler to serve the swagger-spec
@ -23,6 +25,7 @@ The following route data keys contribute to the generated swagger specification:
| :tags | optional set of string or keyword tags for an endpoint api docs | :tags | optional set of string or keyword tags for an endpoint api docs
| :summary | optional short string summary of an endpoint | :summary | optional short string summary of an endpoint
| :description | optional long description of an endpoint. Supports http://spec.commonmark.org/ | :description | optional long description of an endpoint. Supports http://spec.commonmark.org/
| :operationId | optional string specifying the unique ID of an Operation
Coercion keys also contribute to the docs: Coercion keys also contribute to the docs:
@ -44,10 +47,10 @@ If you need to post-process the generated spec, just wrap the handler with a cus
[Swagger-ui](https://github.com/swagger-api/swagger-ui) is a user interface to visualize and interact with the Swagger specification. To make things easy, there is a pre-integrated version of the swagger-ui as a separate module. [Swagger-ui](https://github.com/swagger-api/swagger-ui) is a user interface to visualize and interact with the Swagger specification. To make things easy, there is a pre-integrated version of the swagger-ui as a separate module.
``` ```
[metosin/reitit-swagger-ui "0.5.0"] [metosin/reitit-swagger-ui "0.10.0"]
``` ```
`reitit.swagger-ui/create-swagger-ui-hander` can be used to create a ring-handler to serve the swagger-ui. It accepts the following options: `reitit.swagger-ui/create-swagger-ui-handler` can be used to create a ring-handler to serve the swagger-ui. It accepts the following options:
| key | description | | key | description |
| -----------------|-------------| | -----------------|-------------|
@ -76,7 +79,7 @@ at the top-level to show responses for all endpoints.
* two routes * two routes
* swagger-spec served from `"/swagger.json"` * swagger-spec served from `"/swagger.json"`
* swagger-ui mounted to `"/api-docs"` * swagger-ui mounted to `"/api-docs"`
* note that for real-world use, you need a [content-negation middleware][muuntaja] - * note that for real-world use, you need a [content-negotiation middleware][muuntaja] -
see the next example see the next example
[muuntaja]: ../ring/default_middleware.md#content-negotiation [muuntaja]: ../ring/default_middleware.md#content-negotiation
@ -142,7 +145,7 @@ Another way to serve the swagger-ui is using the [default handler](default_handl
* missed routes are handled by `create-default-handler` * missed routes are handled by `create-default-handler`
* served via [ring-jetty](https://github.com/ring-clojure/ring/tree/master/ring-jetty-adapter) * served via [ring-jetty](https://github.com/ring-clojure/ring/tree/master/ring-jetty-adapter)
Whole example project is in [`/examples/ring-swagger`](https://github.com/metosin/reitit/tree/master/examples/ring-swagger). Whole example project is in [`/examples/ring-spec-swagger`](https://github.com/metosin/reitit/tree/master/examples/ring-spec-swagger).
```clj ```clj
(ns example.server (ns example.server
@ -283,7 +286,18 @@ Example with:
; ("/common/ping" "/one/ping" "/two/ping" "/two/deep/ping") ; ("/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 * ClojureScript
* example for [Macchiato](https://github.com/macchiato-framework) * example for [Macchiato](https://github.com/macchiato-framework)

View file

@ -1,6 +1,6 @@
# Transforming the Middleware Chain # Transforming the Middleware Chain
There is an extra option in ring-router (actually, in the underlying middleware-router): `:reitit.middleware/transform` to transform the middleware chain per endpoint. Value should be a function or a vector of functions that get a vector of compiled middleware and should return a new vector of middleware. There is an extra option in the Ring router (actually, in the underlying middleware-router): `:reitit.middleware/transform` to transform the middleware chain per endpoint. Value should be a function or a vector of functions that get a vector of compiled middleware and should return a new vector of middleware.
## Example Application ## Example Application
@ -59,7 +59,7 @@ There is an extra option in ring-router (actually, in the underlying middleware-
### Printing Request Diffs ### Printing Request Diffs
```clj ```clj
[metosin/reitit-middleware "0.5.0"] [metosin/reitit-middleware "0.10.0"]
``` ```
Using `reitit.ring.middleware.dev/print-request-diffs` transformation, the request diffs between each middleware are printed out to the console. To use it, add the following router option: 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:
@ -71,4 +71,3 @@ Using `reitit.ring.middleware.dev/print-request-diffs` transformation, the reque
Sample output: Sample output:
![Ring Request Diff](../images/ring-request-diff.png) ![Ring Request Diff](../images/ring-request-diff.png)

53
examples/README.md Normal file
View file

@ -0,0 +1,53 @@
# Examples
## buddy-auth
## frontend-auth
## frontend-controllers
## frontend-links
## 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.
Same as ring-spec-swagger?
Async examples as extra.
## http
Async example.
## just-coercion-with-ring
Bad name.
Coercion example for spec, data-spec, Schema.
No Swagger generation or Malli!
Same as ring-example?
## pedestal-swagger
## pedestal
## ring-example
Coercion example for spec, data-spec, Schema.
No Swagger generation or Malli!
## ring-integrant
## ring-malli-swagger
Coercion with Malli and Swagger generation.
## ring-spec-swagger
Coercion with Spec and Swagger generation.

View file

@ -0,0 +1,49 @@
# Buddy auth example
A sample project that shows how to use [Buddy] authentication with Reitit to implement simple authentication and authorization flows.
* Basic auth
* Token-based authorization with JWT tokens
[Buddy]: https://github.com/funcool/buddy
## Usage
Start a REPL:
```sh
lein repl
```
Start the server:
```clj
(start)
```
Take a look at the annotated example in [server.clj](src/example/server.clj).
You can also try some curl commands:
```sh
# Let's first try without password - this should fail
curl http://localhost:3000/basic-auth
# With password, it should work
curl http://user1:kissa13@localhost:3000/basic-auth
# The response should look something like this:
#
# {"message":"Basic auth succeeded!","user":{"id":1,"roles":["admin","user"],
# "token":"eyJhbGciOiJIUzUxMiJ9.eyJpZCI6MSwicm9sZXMiOlsiYWRtaW4iLCJ1c2VyIl0sImV4cCI6MTU5NTU5NDcxNn0.lPFcLxWMFK4_dCLZs2crPB2rmvwO6f-uRsRYdhaWTAJHGKIQpP8anjbmnz6QlrS_RlI160FVzZohPlmkS9JfIQ"}}
#
# The value in the JSON field `token` is a JWT token. A new one is generated with every call and they expire in two hours.
# We can try token auth then. Copy the token from the response in the next command:
curl -H "Authorization: Token PASTE_YOUR_TOKEN_HERE" http://localhost:3000/token-auth
```
## License
Copyright © Metosin Oy and collaborators.

View file

@ -0,0 +1,7 @@
(defproject buddy-auth "0.1.0-SNAPSHOT"
:description "Reitit Buddy Auth App"
:dependencies [[org.clojure/clojure "1.11.2"]
[ring/ring-jetty-adapter "1.12.1"]
[metosin/reitit "0.10.0"]
[buddy "2.0.0"]]
:repl-options {:init-ns example.server})

View file

@ -0,0 +1,244 @@
(ns example.server
"This example demonstrates how to use Buddy authentication with Reitit
to implement simple authentication and authorization flows.
HTTP Basic authentication is used to authenticate with username and
password. HTTP Basic authentication middleware checks credentials
against a 'database' and if credentials are OK, a signed jwt-token
is created and returned. The token can be used to call endpoints
that require token authentication. Token payload contains users
roles that can be used for authorization.
NOTE: This example is not production-ready."
(:require [buddy.auth :as buddy-auth]
[buddy.auth.backends :as buddy-auth-backends]
[buddy.auth.backends.httpbasic :as buddy-auth-backends-httpbasic]
[buddy.auth.middleware :as buddy-auth-middleware]
[buddy.hashers :as buddy-hashers]
[buddy.sign.jwt :as jwt]
[muuntaja.core :as m]
[reitit.ring :as ring]
[reitit.ring.coercion :as coercion]
[reitit.ring.middleware.muuntaja :as muuntaja]
[ring.adapter.jetty :as jetty]
[ring.middleware.params :as params]))
(def db
"We use a simple map as a db here but in real-world you would
interface with a real data storage in `basic-auth` function."
{"user1"
{:id 1
:password (buddy-hashers/encrypt "kissa13")
:roles ["admin" "user"]}
"user2"
{:id 2
:password (buddy-hashers/encrypt "koira12")
:roles ["user"]}})
(def private-key
"Used for signing and verifying JWT-tokens In real world you'd read
this from an environment variable or some other configuration that's
not included in the source code."
"kana15")
(defn create-token
"Creates a signed jwt-token with user data as payload.
`valid-seconds` sets the expiration span."
[user & {:keys [valid-seconds] :or {valid-seconds 7200}}] ;; 2 hours
(let [payload (-> user
(select-keys [:id :roles])
(assoc :exp (.plusSeconds
(java.time.Instant/now) valid-seconds)))]
(jwt/sign payload private-key {:alg :hs512})))
(def token-backend
"Backend for verifying JWT-tokens."
(buddy-auth-backends/jws {:secret private-key :options {:alg :hs512}}))
(defn basic-auth
"Authentication function called from basic-auth middleware for each
request. The result of this function will be added to the request
under key :identity.
NOTE: Use HTTP Basic authentication always with HTTPS in real setups."
[db request {:keys [username password]}]
(let [user (get db username)]
(if (and user (buddy-hashers/check password (:password user)))
(-> user
(dissoc :password)
(assoc :token (create-token user)))
false)))
(defn create-basic-auth-backend
"Creates basic-auth backend to be used by basic-auth-middleware."
[db]
(buddy-auth-backends-httpbasic/http-basic-backend
{:authfn (partial basic-auth db)}))
(defn create-basic-auth-middleware
"Creates a middleware that authenticates requests using http-basic
authentication."
[db]
(let [backend (create-basic-auth-backend db)]
(fn [handler]
(buddy-auth-middleware/wrap-authentication handler backend))))
(defn token-auth-middleware
"Middleware used on routes requiring token authentication."
[handler]
(buddy-auth-middleware/wrap-authentication handler token-backend))
(defn admin-middleware
"Middleware used on routes requiring :admin role."
[handler]
(fn [request]
(if (-> request :identity :roles set (contains? "admin"))
(handler request)
{:status 403 :body {:error "Admin role required"}})))
(defn auth-middleware
"Middleware used in routes that require authentication. If request is
not authenticated a 401 unauthorized response will be
returned. Buddy checks if request key :identity is set to truthy
value by any previous middleware."
[handler]
(fn [request]
(if (buddy-auth/authenticated? request)
(handler request)
{:status 401 :body {:error "Unauthorized"}})))
(def routes
[["/no-auth"
[""
{:get (fn [_] {:status 200 :body {:message "No auth succeeded!"}})}]]
["/basic-auth"
[""
{:middleware [(create-basic-auth-middleware db) auth-middleware]
:get
(fn [req]
{:status 200
:body
{:message "Basic auth succeeded!"
:user (-> req :identity)}})}]]
["/token-auth"
[""
{:middleware [token-auth-middleware auth-middleware]
:get (fn [_] {:status 200 :body {:message "Token auth succeeded!"}})}]]
["/token-auth-with-admin-role"
[""
{:middleware [token-auth-middleware
auth-middleware
admin-middleware]
:get (fn [_] {:status 200 :body {:message "Token auth with admin role succeeded!"}})}]]])
(def app
(ring/ring-handler
(ring/router
routes
{:data
{:muuntaja m/instance
:middleware ; applied to all routes
[params/wrap-params
muuntaja/format-middleware
coercion/coerce-exceptions-middleware
coercion/coerce-request-middleware
coercion/coerce-response-middleware]}})
(ring/create-default-handler)))
(defn start []
(jetty/run-jetty #'app {:port 3000, :join? false})
(println "server running in port 3000"))
(comment
;; Start server to try with real HTTP clients.
(start)
;; ...or just execute following sexps in the REPL. :)
(def headers {"accept" "application/edn"})
(def read-body (comp read-string slurp :body))
(-> {:headers headers :request-method :get :uri "/no-auth"}
app
read-body)
;; => {:message "No auth succeeded!"}
(-> {:headers headers :request-method :get :uri "/basic-auth"}
app
read-body)
;; => {:error "Unauthorized"}
(-> {:headers headers :request-method :get :uri "/token-auth"}
app
read-body)
;; => {:error "Unauthorized"}
(import java.util.Base64)
(defn ->base64
"Encodes a string as base64."
[s]
(.encodeToString (Base64/getEncoder) (.getBytes s)))
(defn basic-auth-headers [user pass]
(merge headers {:authorization (str "Basic " (->base64 (str user ":" pass)))}))
(def bad-creds (basic-auth-headers "juum" "joo"))
(-> {:headers bad-creds :request-method :get :uri "/basic-auth"}
app
read-body)
;; => {:error "Unauthorized"}
(def admin-creds (basic-auth-headers "user1" "kissa13"))
(-> {:headers admin-creds :request-method :get :uri "/basic-auth"}
app
read-body)
;; {:message "Basic auth succeeded!",
;; :user
;; {:id 1,
;; :roles [:admin :user],
;; :token
;; "eyJhbGciOiJIUzUxMiJ9.eyJp....."
(def admin-token
(-> {:headers admin-creds :request-method :get :uri "/basic-auth"}
app
read-body
:user
:token))
(def user-creds (basic-auth-headers "user2" "koira12"))
(def user-token
(-> {:headers user-creds :request-method :get :uri "/basic-auth"}
app
read-body
:user
:token))
(defn token-auth-headers [token]
(merge headers {:authorization (str "Token " token)}))
(def user-token-headers (token-auth-headers user-token))
(-> {:headers user-token-headers :request-method :get :uri "/token-auth"}
app
read-body)
;; => {:message "Token auth succeeded!"}
(-> {:headers user-token-headers :request-method :get :uri "/token-auth-with-admin-role"}
app
read-body)
;; => {:error "Admin role required"}
(def admin-token-headers (token-auth-headers admin-token))
(-> {:headers admin-token-headers :request-method :get :uri "/token-auth-with-admin-role"}
app
read-body)
;; => {:message "Token auth with admin role succeeded!"}
)

View file

@ -1,23 +1,25 @@
(defproject frontend "0.1.0-SNAPSHOT" (defproject frontend-auth "0.1.0-SNAPSHOT"
:description "FIXME: write description" :description "FIXME: write description"
:url "http://example.com/FIXME" :url "http://example.com/FIXME"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"} :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"] [ring-server "0.5.0"]
[reagent "0.8.1"] [reagent "1.2.0"]
[ring "1.7.1"] [ring "1.12.1"]
[hiccup "1.0.5"] [hiccup "1.0.5"]
[org.clojure/clojurescript "1.10.439"] [org.clojure/clojurescript "1.11.132"]
[metosin/reitit "0.5.0"] [metosin/reitit "0.10.0"]
[metosin/reitit-schema "0.5.0"] [metosin/reitit-schema "0.10.0"]
[metosin/reitit-frontend "0.5.0"] [metosin/reitit-frontend "0.10.0"]
[cljsjs/react "17.0.2-0"]
[cljsjs/react-dom "17.0.2-0"]
;; Just for pretty printting the match ;; Just for pretty printting the match
[fipp "0.6.14"]] [fipp "0.6.14"]]
:plugins [[lein-cljsbuild "1.1.7"] :plugins [[lein-cljsbuild "1.1.8"]
[lein-figwheel "0.5.18"]] [lein-figwheel "0.5.20"]]
:source-paths [] :source-paths []
:resource-paths ["resources" "target/cljsbuild"] :resource-paths ["resources" "target/cljsbuild"]

View file

@ -1,5 +1,6 @@
(ns frontend.core (ns frontend.core
(:require [reagent.core :as r] (:require [reagent.core :as r]
[reagent.dom :as rd]
[reitit.frontend :as rf] [reitit.frontend :as rf]
[reitit.frontend.easy :as rfe] [reitit.frontend.easy :as rfe]
[reitit.frontend.controllers :as rfc] [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 (assoc new-match :controllers (rfc/apply-controllers (:controllers (:match state)) new-match)))
(assoc state :match new-match)))))) (assoc state :match new-match))))))
{:use-fragment true}) {:use-fragment true})
(r/render [main-view] (.getElementById js/document "app"))) (rd/render [main-view] (.getElementById js/document "app")))
(init!) (init!)

View file

@ -1,23 +1,25 @@
(defproject frontend "0.1.0-SNAPSHOT" (defproject frontend-controllers "0.1.0-SNAPSHOT"
:description "FIXME: write description" :description "FIXME: write description"
:url "http://example.com/FIXME" :url "http://example.com/FIXME"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"} :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"] [ring-server "0.5.0"]
[reagent "0.8.1"] [reagent "1.2.0"]
[ring "1.7.1"] [ring "1.12.1"]
[hiccup "1.0.5"] [hiccup "1.0.5"]
[org.clojure/clojurescript "1.10.439"] [org.clojure/clojurescript "1.11.132"]
[metosin/reitit "0.5.0"] [metosin/reitit "0.10.0"]
[metosin/reitit-schema "0.5.0"] [metosin/reitit-schema "0.10.0"]
[metosin/reitit-frontend "0.5.0"] [metosin/reitit-frontend "0.10.0"]
[cljsjs/react "17.0.2-0"]
[cljsjs/react-dom "17.0.2-0"]
;; Just for pretty printting the match ;; Just for pretty printting the match
[fipp "0.6.14"]] [fipp "0.6.14"]]
:plugins [[lein-cljsbuild "1.1.7"] :plugins [[lein-cljsbuild "1.1.8"]
[lein-figwheel "0.5.18"]] [lein-figwheel "0.5.20"]]
:source-paths [] :source-paths []
:resource-paths ["resources" "target/cljsbuild"] :resource-paths ["resources" "target/cljsbuild"]

View file

@ -1,5 +1,6 @@
(ns frontend.core (ns frontend.core
(:require [reagent.core :as r] (:require [reagent.core :as r]
[reagent.dom :as rd]
[reitit.frontend :as rf] [reitit.frontend :as rf]
[reitit.frontend.easy :as rfe] [reitit.frontend.easy :as rfe]
[reitit.frontend.controllers :as rfc] [reitit.frontend.controllers :as rfc]
@ -18,11 +19,18 @@
[:div [:div
[:ul [:ul
[:li [:a {:href (rfe/href ::item {:id 1})} "Item 1"]] [:li [:a {:href (rfe/href ::item {:id 1})} "Item 1"]]
[:li [:a {:href (rfe/href ::item {:id 2} {:foo "bar"})} "Item 2"]]] [:li [:a {:href (rfe/href ::item {:id 2} {:foo "bar"} "zzz")} "Item 2"]]]
(if id (when id
[:h2 "Selected item " id]) [:h2 "Selected item " id])
(if (:foo query) [:p "Query params: " [:pre (pr-str query)]]
[:p "Optional foo query param: " (:foo query)])])) [:ul
[:li [:a {:on-click #(rfe/set-query {:a 1})} "set a=1"]]
[:li [:a {:on-click #(rfe/set-query {:a 2} {:replace true})} "set a=2 and replaceState"]]
[:li [:a {:on-click (fn [_] (rfe/set-query #(assoc % :foo "zzz")))} "add foo=zzz"]]]
[:button
{:on-click #(rfe/navigate ::item {:path-params {:id 3}
:query-params {:foo "aaa"}})}
"Navigate example, go to item 3"]]))
(defonce match (r/atom nil)) (defonce match (r/atom nil))
@ -31,9 +39,8 @@
[:ul [:ul
[:li [:a {:href (rfe/href ::frontpage)} "Frontpage"]] [:li [:a {:href (rfe/href ::frontpage)} "Frontpage"]]
[:li [:li
[:a {:href (rfe/href ::item-list)} "Item list"] [:a {:href (rfe/href ::item-list)} "Item list"]]]
]] (when @match
(if @match
(let [view (:view (:data @match))] (let [view (:view (:data @match))]
[view @match])) [view @match]))
[:pre (with-out-str (fedn/pprint @match))]]) [:pre (with-out-str (fedn/pprint @match))]])
@ -63,7 +70,8 @@
["/:id" ["/:id"
{:name ::item {:name ::item
:parameters {:path {:id s/Int} :parameters {:path {:id s/Int}
:query {(s/optional-key :foo) s/Keyword}} :query {(s/optional-key :a) s/Int
(s/optional-key :foo) s/Keyword}}
:controllers [{:parameters {:path [:id]} :controllers [{:parameters {:path [:id]}
:start (fn [{:keys [path]}] :start (fn [{:keys [path]}]
(js/console.log "start" "item controller" (:id path))) (js/console.log "start" "item controller" (:id path)))
@ -81,6 +89,6 @@
(if new-match (if new-match
(assoc new-match :controllers (rfc/apply-controllers (:controllers old-match) new-match)))))) (assoc new-match :controllers (rfc/apply-controllers (:controllers old-match) new-match))))))
{:use-fragment true}) {:use-fragment true})
(r/render [current-page] (.getElementById js/document "app"))) (rd/render [current-page] (.getElementById js/document "app")))
(init!) (init!)

View file

@ -1,24 +1,26 @@
(defproject frontend "0.1.0-SNAPSHOT" (defproject frontend-links "0.1.0-SNAPSHOT"
:description "FIXME: write description" :description "FIXME: write description"
:url "http://example.com/FIXME" :url "http://example.com/FIXME"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"} :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"] [ring-server "0.5.0"]
[reagent "0.8.1"] [reagent "1.2.0"]
[ring "1.7.1"] [ring "1.12.1"]
[hiccup "1.0.5"] [hiccup "1.0.5"]
[org.clojure/clojurescript "1.10.520"] [org.clojure/clojurescript "1.10.520"]
[metosin/reitit "0.5.0"] [metosin/reitit "0.10.0"]
[metosin/reitit-spec "0.5.0"] [metosin/reitit-spec "0.10.0"]
[metosin/reitit-frontend "0.5.0"] [metosin/reitit-frontend "0.10.0"]
[cljsjs/react "17.0.2-0"]
[cljsjs/react-dom "17.0.2-0"]
;; Just for pretty printting the match ;; Just for pretty printting the match
[fipp "0.6.14"]] [fipp "0.6.14"]]
:plugins [[lein-cljsbuild "1.1.7"] :plugins [[lein-cljsbuild "1.1.8"]
[lein-figwheel "0.5.18"] [lein-figwheel "0.5.20"]
[cider/cider-nrepl "0.21.1"]] [cider/cider-nrepl "0.47.1"]]
:repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]} :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}

View file

@ -2,6 +2,7 @@
(:require [clojure.string :as string] (:require [clojure.string :as string]
[fipp.edn :as fedn] [fipp.edn :as fedn]
[reagent.core :as r] [reagent.core :as r]
[reagent.dom :as rd]
[reitit.coercion.spec :as rss] [reitit.coercion.spec :as rss]
[reitit.frontend :as rf] [reitit.frontend :as rf]
[reitit.frontend.easy :as rfe] [reitit.frontend.easy :as rfe]
@ -137,7 +138,7 @@
(fn [m] (reset! current-match m)) (fn [m] (reset! current-match m))
;; set to false to enable HistoryAPI ;; set to false to enable HistoryAPI
{:use-fragment true}) {:use-fragment true})
(r/render [current-page] (.getElementById js/document "app"))) (rd/render [current-page] (.getElementById js/document "app")))
(init!) (init!)

View 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

View file

@ -0,0 +1 @@
../../../modules/reitit-core

View file

@ -0,0 +1 @@
../../../modules/reitit-frontend

View file

@ -0,0 +1 @@
../../../modules/reitit-schema

View 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.10.0"]
[metosin/reitit-malli "0.10.0"]
[metosin/reitit-frontend "0.10.0"]
[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})

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

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

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

View file

@ -1,24 +1,27 @@
(defproject frontend "0.1.0-SNAPSHOT" (defproject frontend-prompt "0.1.0-SNAPSHOT"
:description "FIXME: write description" :description "FIXME: write description"
:url "http://example.com/FIXME" :url "http://example.com/FIXME"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"} :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"] [ring-server "0.5.0"]
[reagent "0.8.1"] [reagent "1.2.0"]
[ring "1.7.1"] [ring "1.12.1"]
[hiccup "1.0.5"] [hiccup "1.0.5"]
[org.clojure/clojurescript "1.10.520"] [org.clojure/clojurescript "1.11.132"]
[metosin/reitit "0.5.0"] [metosin/reitit "0.10.0"]
[metosin/reitit-spec "0.5.0"] [metosin/reitit-spec "0.10.0"]
[metosin/reitit-frontend "0.5.0"] [metosin/reitit-frontend "0.10.0"]
[cljsjs/react "17.0.2-0"]
[cljsjs/react-dom "17.0.2-0"]
;; Just for pretty printting the match ;; 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]} :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}

View file

@ -1,6 +1,7 @@
(ns frontend.core (ns frontend.core
(:require [fipp.edn :as fedn] (:require [fipp.edn :as fedn]
[reagent.core :as r] [reagent.core :as r]
[reagent.dom :as rd]
[reitit.coercion.spec :as rss] [reitit.coercion.spec :as rss]
[reitit.frontend :as rf] [reitit.frontend :as rf]
[reitit.frontend.easy :as rfe])) [reitit.frontend.easy :as rfe]))
@ -63,6 +64,6 @@
on-navigate on-navigate
;; set to false to enable HistoryAPI ;; set to false to enable HistoryAPI
{:use-fragment true}) {:use-fragment true})
(r/render [current-page] (.getElementById js/document "app"))) (rd/render [current-page] (.getElementById js/document "app")))
(init!) (init!)

View file

@ -1,13 +1,15 @@
(defproject frontend-re-frame "0.1.0-SNAPSHOT" (defproject frontend-re-frame "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.11.2"]
[org.clojure/clojurescript "1.10.520"] [org.clojure/clojurescript "1.11.132"]
[metosin/reitit "0.5.0"] [metosin/reitit "0.10.0"]
[reagent "0.8.1"] [reagent "1.2.0"]
[re-frame "0.10.6"]] [re-frame "0.10.6"]
[cljsjs/react "17.0.2-0"]
[cljsjs/react-dom "17.0.2-0"]]
:plugins [[lein-cljsbuild "1.1.7"] :plugins [[lein-cljsbuild "1.1.8"]
[lein-figwheel "0.5.18"] [lein-figwheel "0.5.20"]
[cider/cider-nrepl "0.21.1"]] [cider/cider-nrepl "0.47.1"]]
:repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]} :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}
:min-lein-version "2.5.3" :min-lein-version "2.5.3"
@ -33,7 +35,7 @@
{:builds {:builds
[{:id "dev" [{:id "dev"
:source-paths ["src/cljs"] :source-paths ["src/cljs"]
:figwheel {:on-jsload "frontend-re-frame.core/mount-root"} :figwheel true
:compiler {:main frontend-re-frame.core :compiler {:main frontend-re-frame.core
:output-to "resources/public/js/compiled/app.js" :output-to "resources/public/js/compiled/app.js"
:output-dir "resources/public/js/compiled/out" :output-dir "resources/public/js/compiled/out"

View file

@ -2,12 +2,9 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset='utf-8'> <meta charset='utf-8'>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script src="js/compiled/app.js"></script> <script src="js/compiled/app.js"></script>
<script>frontend_re_frame.core.init();</script>
</body> </body>
</html> </html>

View file

@ -1,27 +1,33 @@
(ns frontend-re-frame.core (ns frontend-re-frame.core
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[reagent.core :as reagent] [reagent.dom :as rd]
[reitit.core :as r] [reitit.core :as r]
[reitit.coercion.spec :as rss] [reitit.coercion.spec :as rss]
[reitit.frontend :as rf] [reitit.frontend :as rf]
[reitit.frontend.controllers :as rfc] [reitit.frontend.controllers :as rfc]
[reitit.frontend.easy :as rfe])) [reitit.frontend.easy :as rfe]))
;;; Effects ;;;
;; Triggering navigation from events.
(re-frame/reg-fx :push-state
(fn [route]
(apply rfe/push-state route)))
;;; Events ;;; ;;; Events ;;;
(re-frame/reg-event-db (re-frame/reg-event-db ::initialize-db
::initialize-db (fn [db _]
(fn [_ _] (if db
{:current-route nil})) db
{:current-route nil})))
(re-frame/reg-event-fx (re-frame/reg-event-fx ::push-state
::navigate (fn [_ [_ & route]]
(fn [db [_ & route]] {:push-state route}))
;; See `navigate` effect in routes.cljs
{::navigate! route}))
(re-frame/reg-event-db (re-frame/reg-event-db ::navigated
::navigated
(fn [db [_ new-match]] (fn [db [_ new-match]]
(let [old-match (:current-route db) (let [old-match (:current-route db)
controllers (rfc/apply-controllers (:controllers old-match) new-match)] controllers (rfc/apply-controllers (:controllers old-match) new-match)]
@ -29,8 +35,7 @@
;;; Subscriptions ;;; ;;; Subscriptions ;;;
(re-frame/reg-sub (re-frame/reg-sub ::current-route
::current-route
(fn [db] (fn [db]
(:current-route db))) (:current-route db)))
@ -41,7 +46,7 @@
[:h1 "This is home page"] [:h1 "This is home page"]
[:button [:button
;; Dispatch navigate event that triggers a (side)effect. ;; Dispatch navigate event that triggers a (side)effect.
{:on-click #(re-frame/dispatch [::navigate ::sub-page2])} {:on-click #(re-frame/dispatch [::push-state ::sub-page2])}
"Go to sub-page 2"]]) "Go to sub-page 2"]])
(defn sub-page1 [] (defn sub-page1 []
@ -52,14 +57,6 @@
[:div [:div
[:h1 "This is sub-page 2"]]) [:h1 "This is sub-page 2"]])
;;; Effects ;;;
;; Triggering navigation from events.
(re-frame/reg-fx
::navigate!
(fn [route]
(apply rfe/push-state route)))
;;; Routes ;;; ;;; Routes ;;;
(defn href (defn href
@ -141,13 +138,12 @@
(enable-console-print!) (enable-console-print!)
(println "dev mode"))) (println "dev mode")))
(defn mount-root [] (defn init []
(re-frame/clear-subscription-cache!) (re-frame/clear-subscription-cache!)
(init-routes!) ;; Reset routes on figwheel reload
(reagent/render [router-component {:router router}]
(.getElementById js/document "app")))
(defn ^:export init []
(re-frame/dispatch-sync [::initialize-db]) (re-frame/dispatch-sync [::initialize-db])
(dev-setup) (dev-setup)
(mount-root)) (init-routes!) ;; Reset routes on figwheel reload
(rd/render [router-component {:router router}]
(.getElementById js/document "app")))
(init)

View file

@ -10,4 +10,4 @@ Go with browser to http://localhost:3449
## License ## License
Copyright © 2018 Metosin Oy Copyright © Metosin Oy and collaborators

View file

@ -4,25 +4,27 @@
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"} :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"] [ring-server "0.5.0"]
[reagent "0.8.1"] [reagent "1.2.0"]
[ring "1.7.1"] [ring "1.12.1"]
[hiccup "1.0.5"] [hiccup "1.0.5"]
[org.clojure/clojurescript "1.10.439"] [org.clojure/clojurescript "1.11.132"]
[metosin/reitit "0.5.0"] [metosin/reitit "0.10.0"]
[metosin/reitit-spec "0.5.0"] [metosin/reitit-spec "0.10.0"]
[metosin/reitit-frontend "0.5.0"] [metosin/reitit-frontend "0.10.0"]
[cljsjs/react "17.0.2-0"]
[cljsjs/react-dom "17.0.2-0"]
;; Just for pretty printting the match ;; Just for pretty printting the match
[fipp "0.6.14"]] [fipp "0.6.23"]]
:plugins [[lein-cljsbuild "1.1.7"] :plugins [[lein-cljsbuild "1.1.8"]
[lein-figwheel "0.5.18"]] [lein-figwheel "0.5.20"]]
:source-paths [] :source-paths []
:resource-paths ["resources" "target/cljsbuild"] :resource-paths ["resources" "target/cljsbuild"]
:profiles {:dev {:dependencies [[binaryage/devtools "0.9.10"]]}} :profiles {:dev {:dependencies [[binaryage/devtools "1.0.2"]]}}
:cljsbuild :cljsbuild
{:builds {:builds

View file

@ -1,5 +1,6 @@
(ns frontend.core (ns frontend.core
(:require [reagent.core :as r] (:require [reagent.core :as r]
[reagent.dom :as rd]
[reitit.frontend :as rf] [reitit.frontend :as rf]
[reitit.frontend.easy :as rfe] [reitit.frontend.easy :as rfe]
[reitit.coercion.spec :as rss] [reitit.coercion.spec :as rss]
@ -77,6 +78,6 @@
(fn [m] (reset! match m)) (fn [m] (reset! match m))
;; set to false to enable HistoryAPI ;; set to false to enable HistoryAPI
{:use-fragment true}) {:use-fragment true})
(r/render [current-page] (.getElementById js/document "app"))) (rd/render [current-page] (.getElementById js/document "app")))
(init!) (init!)

View file

@ -1,4 +1,4 @@
# Http with Swagger example # Http with Swagger/OpenAPI example
## Usage ## Usage
@ -7,6 +7,10 @@
(start) (start)
``` ```
- Swagger spec served at <http://localhost:3000/swagger.json>
- Openapi spec served at <http://localhost:3000/openapi.json>
- Swagger UI served at <http://localhost:3000/>
To test the endpoints using [httpie](https://httpie.org/): To test the endpoints using [httpie](https://httpie.org/):
```bash ```bash
@ -20,4 +24,4 @@ http GET :3000/async results==1 seed==reitit
## License ## License
Copyright © 2018 Metosin Oy Copyright © 2018-2023 Metosin Oy

View file

@ -1,7 +1,8 @@
(defproject ring-example "0.1.0-SNAPSHOT" (defproject http-swagger "0.1.0-SNAPSHOT"
:description "Reitit Http App with Swagger" :description "Reitit Http App with Swagger"
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.11.2"]
[ring/ring-jetty-adapter "1.7.1"] [ring/ring-jetty-adapter "1.12.1"]
[aleph "0.4.7-alpha5"] [aleph "0.7.1"]
[metosin/reitit "0.5.0"]] [metosin/reitit "0.10.0"]
[metosin/ring-swagger-ui "5.9.0"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -4,6 +4,7 @@
[reitit.coercion.spec] [reitit.coercion.spec]
[reitit.swagger :as swagger] [reitit.swagger :as swagger]
[reitit.swagger-ui :as swagger-ui] [reitit.swagger-ui :as swagger-ui]
[reitit.openapi :as openapi]
[reitit.http.coercion :as coercion] [reitit.http.coercion :as coercion]
[reitit.dev.pretty :as pretty] [reitit.dev.pretty :as pretty]
[reitit.interceptor.sieppari :as sieppari] [reitit.interceptor.sieppari :as sieppari]
@ -42,11 +43,26 @@
[["/swagger.json" [["/swagger.json"
{:get {:no-doc true {:get {:no-doc true
:swagger {:info {:title "my-api" :swagger {:info {:title "my-api"
:description "with reitit-http"}} :description "swagger-docs with reitit-http"
:version "0.0.1"}
;; used in /secure APIs below
:securityDefinitions {"auth" {:type :apiKey
:in :header
:name "Example-Api-Key"}}}
:handler (swagger/create-swagger-handler)}}] :handler (swagger/create-swagger-handler)}}]
["/openapi.json"
{:get {:no-doc true
:openapi {:info {:title "my-api"
:description "openapi3-docs with reitit-http"
:version "0.0.1"}
;; used in /secure APIs below
:components {:securitySchemes {"auth" {:type :apiKey
:in :header
:name "Example-Api-Key"}}}}
:handler (openapi/create-openapi-handler)}}]
["/files" ["/files"
{:swagger {:tags ["files"]}} {:tags #{"files"}}
["/upload" ["/upload"
{:post {:summary "upload a file" {:post {:summary "upload a file"
@ -60,7 +76,8 @@
["/download" ["/download"
{:get {:summary "downloads a file" {:get {:summary "downloads a file"
:swagger {:produces ["image/png"]} :swagger {:produces ["image/png"]}
:responses {200 {:description "image"}} :responses {200 {:description "an image"
:content {"image/png" {:schema any?}}}}
:handler (fn [_] :handler (fn [_]
{:status 200 {:status 200
:headers {"Content-Type" "image/png"} :headers {"Content-Type" "image/png"}
@ -68,7 +85,7 @@
(io/resource "reitit.png"))})}}]] (io/resource "reitit.png"))})}}]]
["/async" ["/async"
{:get {:swagger {:tags ["async"]} {:get {:tags #{"async"}
:summary "fetches random users asynchronously over the internet" :summary "fetches random users asynchronously over the internet"
:parameters {:query (s/keys :req-un [::results] :opt-un [::seed])} :parameters {:query (s/keys :req-un [::results] :opt-un [::seed])}
:responses {200 {:body any?}} :responses {200 {:body any?}}
@ -85,7 +102,7 @@
:body results})))}}] :body results})))}}]
["/math" ["/math"
{:swagger {:tags ["math"]}} {:tags #{"math"}}
["/plus" ["/plus"
{:get {:summary "plus with data-spec query parameters" {:get {:summary "plus with data-spec query parameters"
@ -113,7 +130,22 @@
:responses {200 {:body (s/keys :req-un [::total])}} :responses {200 {:body (s/keys :req-un [::total])}}
:handler (fn [{{{:keys [x y]} :body} :parameters}] :handler (fn [{{{:keys [x y]} :body} :parameters}]
{:status 200 {:status 200
:body {:total (- x y)}})}}]]] :body {:total (- x y)}})}}]]
["/secure"
{:tags #{"secure"}
:openapi {:security [{"auth" []}]}
:swagger {:security [{"auth" []}]}}
["/get"
{:get {:summary "endpoint authenticated with a header"
:responses {200 {:body {:secret string?}}
401 {:body {:error string?}}}
:handler (fn [request]
;; In a real app authentication would be handled by middleware
(if (= "secret" (get-in request [:headers "example-api-key"]))
{:status 200
:body {:secret "I am a marmot"}}
{:status 401
:body {:error "unauthorized"}}))}}]]]
{;:reitit.interceptor/transform dev/print-context-diffs ;; pretty context diffs {;:reitit.interceptor/transform dev/print-context-diffs ;; pretty context diffs
;;:validate spec/validate ;; enable spec validation for route data ;;:validate spec/validate ;; enable spec validation for route data
@ -123,6 +155,8 @@
:muuntaja m/instance :muuntaja m/instance
:interceptors [;; swagger feature :interceptors [;; swagger feature
swagger/swagger-feature swagger/swagger-feature
;; openapi feature
openapi/openapi-feature
;; query-params & form-params ;; query-params & form-params
(parameters/parameters-interceptor) (parameters/parameters-interceptor)
;; content-negotiation ;; content-negotiation
@ -143,6 +177,9 @@
(swagger-ui/create-swagger-ui-handler (swagger-ui/create-swagger-ui-handler
{:path "/" {:path "/"
:config {:validatorUrl nil :config {:validatorUrl nil
:urls [{:name "swagger", :url "swagger.json"}
{:name "openapi", :url "openapi.json"}]
:urls.primaryName "openapi"
:operationsSorter "alpha"}}) :operationsSorter "alpha"}})
(ring/create-default-handler)) (ring/create-default-handler))
{:executor sieppari/executor})) {:executor sieppari/executor}))

View file

@ -1,9 +1,9 @@
(defproject ring-example "0.1.0-SNAPSHOT" (defproject http "0.1.0-SNAPSHOT"
:description "Reitit Ring App with Swagger" :description "Reitit Ring App with Swagger"
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.11.2"]
[org.clojure/core.async "0.4.490"] [org.clojure/core.async "1.6.681"]
[funcool/promesa "1.9.0"] [funcool/promesa "11.0.678"]
[manifold "0.1.8"] [manifold "0.4.2"]
[ring/ring-jetty-adapter "1.7.1"] [ring/ring-jetty-adapter "1.12.1"]
[metosin/reitit "0.5.0"]] [metosin/reitit "0.10.0"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -1,5 +1,5 @@
(defproject just-coercion-with-ring "0.1.0-SNAPSHOT" (defproject just-coercion-with-ring "0.1.0-SNAPSHOT"
:description "Reitit coercion with vanilla ring" :description "Reitit coercion with vanilla ring"
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.11.2"]
[ring/ring-jetty-adapter "1.7.1"] [ring/ring-jetty-adapter "1.12.1"]
[metosin/reitit "0.5.0"]]) [metosin/reitit "0.10.0"]])

View file

@ -0,0 +1,18 @@
# OpenAPI 3 feature showcase
## Usage
```clj
> lein repl
(start)
```
- Swagger UI served at <http://localhost:3000/>
- Openapi spec served at <http://localhost:3000/openapi.json>
- See [src/example/server.clj](src/example/server.clj) for details
<img src="https://raw.githubusercontent.com/metosin/reitit/master/examples/openapi/openapi.png" />
## License
Copyright © 2023 Metosin Oy

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

View file

@ -0,0 +1,10 @@
(defproject openapi "0.1.0-SNAPSHOT"
:description "Reitit OpenAPI example"
:dependencies [[org.clojure/clojure "1.11.2"]
[metosin/jsonista "0.3.8"]
[ring/ring-jetty-adapter "1.12.1"]
[metosin/reitit "0.10.0"]
[metosin/ring-swagger-ui "5.9.0"]
[org.slf4j/slf4j-simple "2.0.9"]]
:repl-options {:init-ns example.server}
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}})

View file

@ -0,0 +1,227 @@
(ns example.server
(:require [reitit.ring :as ring]
[reitit.ring.spec]
[reitit.coercion.malli]
[reitit.openapi :as openapi]
[reitit.ring.malli]
[reitit.swagger-ui :as swagger-ui]
[reitit.ring.coercion :as coercion]
[reitit.dev.pretty :as pretty]
[reitit.ring.middleware.muuntaja :as muuntaja]
[reitit.ring.middleware.exception :as exception]
[reitit.ring.middleware.multipart :as multipart]
[reitit.ring.middleware.parameters :as parameters]
[ring.adapter.jetty :as jetty]
[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
[["/openapi.json"
{:get {:no-doc true
:openapi {:info {:title "my-api"
:description "openapi3 docs with [malli](https://github.com/metosin/malli) and reitit-ring"
:version "0.0.1"}
;; used in /secure APIs below
:components {:securitySchemes {"auth" {:type :apiKey
:in :header
:name "Example-Api-Key"}}}}
:handler (openapi/create-openapi-handler)}}]
["/pizza"
{:get {:summary "Fetch a pizza | Multiple content-types, multiple examples"
:responses {200 {:description "Fetch a pizza as json or EDN"
:content {"application/json" {:schema [:map
[:format [:enum :json]]
[:color :keyword]
[:pineapple :boolean]]
:examples {:white {:description "White pizza with pineapple"
:value {:format :json
:color :white
:pineapple true}}
:red {:description "Red pizza"
:value {:format :json
:color :red
:pineapple false}}}}
"application/edn" {:schema [:map
[:format [:enum :edn]]
[:color :keyword]
[:pineapple :boolean]]
:examples {:red {:description "Red pizza with pineapple"
:value (pr-str {:format :edn :color :red :pineapple true})}}}}}}
:handler (fn [_request]
(rand-nth [{:status 200
:muuntaja/content-type "application/json"
:body {:format :json
:color :red
:pineapple true}}
{:status 200
:muuntaja/content-type "application/edn"
:body {:format :edn
:color :red
:pineapple true}}]))}
: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" {:schema [:map
[:color :keyword]
[:pineapple :boolean]]
:examples {:purple {:value (pr-str {:color :purple
:pineapple false})}}}}}
: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]
(if (< (Math/random) 0.5)
{:status 200
:body {:success true}}
{:status 500
:body {:error "an error happened"}}))}}]
["/contact"
{:get {:summary "Search for a contact | Customizing via malli properties"
:parameters {:query [:map
[:limit {:title "How many results to return? Optional."
:optional true
: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?]]}
:responses {200 {:content {:default {:schema [:vector
[:map
[:name {:json-schema/example "Heidi"}
string?]
[:email {:json-schema/example "heidi@alps.ch"}
string?]]]}}}}
:handler (fn [_request]
{: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-of :keyword :any]}}}}
:handler (fn [request]
{:status 200
:body (get-in request [:parameters :request])})}}]
["/secure"
{:tags #{"secure"}
:openapi {:security [{"auth" []}]}}
["/get"
{:get {:summary "endpoint authenticated with a header"
:responses {200 {:body [:map [:secret :string]]}
401 {:body [:map [:error :string]]}}
:handler (fn [request]
;; In a real app authentication would be handled by middleware
(if (= "secret" (get-in request [:headers "example-api-key"]))
{:status 200
:body {:secret "I am a marmot"}}
{:status 401
:body {:error "unauthorized"}}))}}]]]
{;;:reitit.middleware/transform dev/print-request-diffs ;; pretty diffs
:validate reitit.ring.spec/validate
:exception pretty/exception
:data {:coercion reitit.coercion.malli/coercion
:muuntaja m/instance
:middleware [openapi/openapi-feature
;; query-params & form-params
parameters/parameters-middleware
;; content-negotiation
muuntaja/format-negotiate-middleware
;; encoding response body
muuntaja/format-response-middleware
;; exception handling
exception/exception-middleware
;; decoding request body
muuntaja/format-request-middleware
;; coercing response bodys
coercion/coerce-response-middleware
;; coercing request parameters
coercion/coerce-request-middleware
;; multipart
multipart/multipart-middleware]}})
(ring/routes
(swagger-ui/create-swagger-ui-handler
{:path "/"
:config {:validatorUrl nil
:urls [{:name "openapi", :url "openapi.json"}]
:urls.primaryName "openapi"
:operationsSorter "alpha"}})
(ring/create-default-handler))))
(defn start []
(jetty/run-jetty #'app {:port 3000, :join? false})
(println "server running in port 3000"))
(comment
(start))

View file

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

View file

@ -0,0 +1,9 @@
(defproject pedestal-malli-swagger-example "0.1.0-SNAPSHOT"
:description "Reitit-http with pedestal"
:dependencies [[org.clojure/clojure "1.11.2"]
[io.pedestal/pedestal.service "0.6.3"]
[io.pedestal/pedestal.jetty "0.6.3"]
[metosin/reitit-malli "0.10.0"]
[metosin/reitit-pedestal "0.10.0"]
[metosin/reitit "0.10.0"]]
:repl-options {:init-ns server})

View file

@ -0,0 +1,164 @@
(ns server
(:require [clojure.java.io :as io]
[io.pedestal.http.route]
[reitit.interceptor]
[reitit.dev.pretty :as pretty]
[reitit.coercion.malli]
[io.pedestal.http]
[reitit.ring]
[reitit.ring.malli]
[reitit.http]
[reitit.pedestal]
[reitit.swagger :as swagger]
[reitit.swagger-ui :as swagger-ui]
[reitit.http.coercion :as coercion]
[reitit.http.interceptors.parameters :as parameters]
[reitit.http.interceptors.muuntaja :as muuntaja]
[reitit.http.interceptors.multipart :as multipart]
[muuntaja.core]
[malli.util :as mu]))
(defn reitit-routes
[_config]
[["/swagger.json" {:get {:no-doc true
:swagger {:info {:title "my-api"
:description "with [malli](https://github.com/metosin/malli) and reitit-ring"}
:tags [{:name "files",
:description "file api"}
{:name "math",
:description "math api"}]}
:handler (swagger/create-swagger-handler)}}]
["/files" {:swagger {:tags ["files"]}}
["/upload"
{:post {:summary "upload a file"
:parameters {:multipart [:map [:file reitit.ring.malli/temp-file-part]]}
:responses {200 {:body [:map
[:name string?]
[:size int?]]}}
:handler (fn [{{{{:keys [filename
size]} :file}
:multipart}
:parameters}]
{:status 200
:body {:name filename
:size size}})}}]
["/download" {:get {:summary "downloads a file"
:swagger {:produces ["image/png"]}
:handler (fn [_]
{:status 200
:headers {"Content-Type" "image/png"}
:body (-> "reitit.png"
(io/resource)
(io/input-stream))})}}]]
["/math" {:swagger {:tags ["math"]}}
["/plus"
{:get {:summary "plus with malli query parameters"
:parameters {:query [:map
[:x
{:title "X parameter"
:description "Description for X parameter"
:json-schema/default 42}
int?]
[:y int?]]}
:responses {200 {:body [:map [:total int?]]}}
:handler (fn [{{{:keys [x
y]}
:query}
:parameters}]
{:status 200
:body {:total (+ x y)}})}
:post {:summary "plus with malli body parameters"
:parameters {:body [:map
[:x
{:title "X parameter"
:description "Description for X parameter"
:json-schema/default 42}
int?]
[:y int?]]}
:responses {200 {:body [:map [:total int?]]}}
:handler (fn [{{{:keys [x
y]}
:body}
:parameters}]
{:status 200
:body {:total (+ x y)}})}}]]])
(defn reitit-ring-routes
[_config]
[(swagger-ui/create-swagger-ui-handler
{:path "/"
:config {:validatorUrl nil
:operationsSorter "alpha"}})
(reitit.ring/create-resource-handler)
(reitit.ring/create-default-handler)])
(defn reitit-router-config
[_config]
{:exception pretty/exception
:data {:coercion (reitit.coercion.malli/create
{:error-keys #{:coercion
:in
:schema
:value
:errors
:humanized}
:compile mu/closed-schema
:strip-extra-keys true
:default-values true
:options nil})
:muuntaja muuntaja.core/instance
:interceptors [swagger/swagger-feature
(parameters/parameters-interceptor)
(muuntaja/format-negotiate-interceptor)
(muuntaja/format-response-interceptor)
(muuntaja/format-request-interceptor)
(coercion/coerce-response-interceptor)
(coercion/coerce-request-interceptor)
(multipart/multipart-interceptor)]}})
(def config
{:env :dev
:io.pedestal.http/routes []
:io.pedestal.http/type :jetty
:io.pedestal.http/port 3000
:io.pedestal.http/join? false
:io.pedestal.http/secure-headers {:content-security-policy-settings
{:default-src "'self'"
:style-src "'self' 'unsafe-inline'"
:script-src "'self' 'unsafe-inline'"}}
::reitit-routes reitit-routes
::reitit-ring-routes reitit-ring-routes
::reitit-router-config reitit-router-config})
(defn reitit-http-router
[{::keys [reitit-routes
reitit-ring-routes
reitit-router-config]
:as config}]
(reitit.pedestal/routing-interceptor
(reitit.http/router
(reitit-routes config)
(reitit-router-config config))
(->> config
reitit-ring-routes
(apply reitit.ring/routes))))
(defonce server (atom nil))
(defn start
[server
config]
(when @server
(io.pedestal.http/stop @server)
(println "server stopped"))
(-> config
io.pedestal.http/default-interceptors
(reitit.pedestal/replace-last-interceptor (reitit-http-router config))
io.pedestal.http/dev-interceptors
io.pedestal.http/create-server
io.pedestal.http/start
(->> (reset! server)))
(println "server running in port 3000"))
#_(start server config)

View file

@ -1,8 +1,8 @@
(defproject ring-example "0.1.0-SNAPSHOT" (defproject ring-example "0.1.0-SNAPSHOT"
:description "Reitit-http with pedestal" :description "Reitit-http with pedestal"
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.11.2"]
[io.pedestal/pedestal.service "0.5.5"] [io.pedestal/pedestal.service "0.6.3"]
[io.pedestal/pedestal.jetty "0.5.5"] [io.pedestal/pedestal.jetty "0.6.3"]
[metosin/reitit-pedestal "0.5.0"] [metosin/reitit-pedestal "0.10.0"]
[metosin/reitit "0.5.0"]] [metosin/reitit "0.10.0"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -58,7 +58,6 @@
["/download" ["/download"
{:get {:summary "downloads a file" {:get {:summary "downloads a file"
:swagger {:produces ["image/png"]} :swagger {:produces ["image/png"]}
:responses {200 {:description "image"}}
:handler (fn [_] :handler (fn [_]
{:status 200 {:status 200
:headers {"Content-Type" "image/png"} :headers {"Content-Type" "image/png"}

View file

@ -1,8 +1,8 @@
(defproject ring-example "0.1.0-SNAPSHOT" (defproject pedestal-example "0.1.0-SNAPSHOT"
:description "Reitit-http with pedestal" :description "Reitit-http with pedestal"
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.11.2"]
[io.pedestal/pedestal.service "0.5.5"] [io.pedestal/pedestal.service "0.6.3"]
[io.pedestal/pedestal.jetty "0.5.5"] [io.pedestal/pedestal.jetty "0.6.3"]
[metosin/reitit-pedestal "0.5.0"] [metosin/reitit-pedestal "0.10.0"]
[metosin/reitit "0.5.0"]] [metosin/reitit "0.10.0"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -1,6 +1,6 @@
(defproject ring-example "0.1.0-SNAPSHOT" (defproject ring-example "0.1.0-SNAPSHOT"
:description "Reitit Ring App" :description "Reitit Ring App"
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.11.2"]
[ring/ring-jetty-adapter "1.7.1"] [ring/ring-jetty-adapter "1.12.1"]
[metosin/reitit "0.5.0"]] [metosin/reitit "0.10.0"]]
:repl-options {:init-ns example.server}) :repl-options {:init-ns example.server})

View file

@ -1,10 +1,10 @@
(defproject ring-integrant-example "0.1.0-SNAPSHOT" (defproject ring-integrant-example "0.1.0-SNAPSHOT"
:description "Reitit Ring App with Integrant" :description "Reitit Ring App with Integrant"
:dependencies [[org.clojure/clojure "1.10.1"] :dependencies [[org.clojure/clojure "1.11.2"]
[ring/ring-jetty-adapter "1.7.1"] [ring/ring-jetty-adapter "1.12.1"]
[metosin/reitit "0.5.0"] [metosin/reitit "0.10.0"]
[integrant "0.7.0"]] [integrant "0.8.1"]]
:main example.server :main example.server
:repl-options {:init-ns user} :repl-options {:init-ns user}
:profiles {:dev {:dependencies [[integrant/repl "0.3.1"]] :profiles {:dev {:dependencies [[integrant/repl "0.3.3"]]
:source-paths ["dev"]}}) :source-paths ["dev"]}})

View file

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

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