mirror of
https://github.com/metosin/reitit.git
synced 2026-02-12 06:15:17 +00:00
Compare commits
15 commits
e8d67f2d1a
...
398c96d11a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
398c96d11a | ||
|
|
d25dca19f6 | ||
|
|
1b02662c78 | ||
|
|
36af88b65e | ||
|
|
152c598858 | ||
|
|
6d9632e85e | ||
|
|
5ff8ba2e3e | ||
|
|
c684c83c99 | ||
|
|
defebb0f1f | ||
|
|
54c0935078 | ||
|
|
cde050f964 | ||
|
|
560c6d7969 | ||
|
|
abe95bfc17 | ||
|
|
d2f44b8015 | ||
|
|
7a707f042b |
60 changed files with 1257 additions and 1611 deletions
26
.github/workflows/release.yml
vendored
Normal file
26
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published # reacts to releases and prereleases, but not their drafts
|
||||
|
||||
jobs:
|
||||
build-and-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: "Setup Java"
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 11
|
||||
- name: "Setup Clojure"
|
||||
uses: DeLaGuardo/setup-clojure@master
|
||||
with:
|
||||
lein: 2.9.5
|
||||
- name: Deploy to Clojars
|
||||
run: ./scripts/lein-modules do clean, deploy clojars
|
||||
env:
|
||||
CLOJARS_USERNAME: metosinci
|
||||
CLOJARS_PASSWORD: "${{ secrets.CLOJARS_DEPLOY_TOKEN }}"
|
||||
15
CHANGELOG.md
15
CHANGELOG.md
|
|
@ -12,12 +12,25 @@ We use [Break Versioning][breakver]. The version numbers follow a `<major>.<mino
|
|||
|
||||
[breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md
|
||||
|
||||
## Unreleased
|
||||
## 0.9.2-rc1 (2025-10-24)
|
||||
|
||||
* 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)
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ modules will continue to be released under `metosin` for compatibility purposes.
|
|||
All main modules bundled:
|
||||
|
||||
```clj
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
Optionally, the parts can be required separately.
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians
|
|||
All bundled:
|
||||
|
||||
```clj
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
Optionally, the parts can be required separately.
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ The default exception formatting uses `reitit.exception/exception`. It produces
|
|||
## Pretty Errors
|
||||
|
||||
```clj
|
||||
[metosin/reitit-dev "0.9.1"]
|
||||
[metosin/reitit-dev "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,14 @@ clojure-lsp clean-ns
|
|||
The documentation lives under `doc` and it is hosted on [cljdoc](https://cljdoc.org). See their
|
||||
documentation for [library authors](https://github.com/cljdoc/cljdoc/blob/master/doc/userguide/for-library-authors.adoc)
|
||||
|
||||
## Updating deps
|
||||
|
||||
|
||||
* `lein ancient upgrade`
|
||||
* Mention non-dev non-test dep upgrades in CHANGELOG.md
|
||||
* `npm update --save`
|
||||
* Make a PR, run CI
|
||||
|
||||
## Making a release
|
||||
|
||||
We use [Break Versioning][breakver]. Remember our promise: patch-level bumps never include breaking changes!
|
||||
|
|
@ -32,25 +40,20 @@ We use [Break Versioning][breakver]. Remember our promise: patch-level bumps nev
|
|||
[breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md
|
||||
|
||||
```bash
|
||||
# new version
|
||||
# create a release commit
|
||||
./scripts/set-version "1.0.0"
|
||||
|
||||
# create a release commit and a tag
|
||||
git add -u
|
||||
# !!! update the changelog
|
||||
|
||||
git add -u
|
||||
git commit -m "Release 1.0.0"
|
||||
git tag 1.0.0
|
||||
|
||||
# works
|
||||
./scripts/lein-modules install
|
||||
lein test
|
||||
|
||||
# deploy to clojars
|
||||
CLOJARS_USERNAME=*** CLOJARS_PASSWORD=*** ./scripts/lein-modules do clean, deploy clojars
|
||||
|
||||
# push the commit and the tag
|
||||
# push the commit
|
||||
git push
|
||||
git push --tags
|
||||
|
||||
# !!! check that tests pass on CI
|
||||
```
|
||||
|
||||
* Remembor to update the changelog!
|
||||
* Create a new release on github at <https://github.com/metosin/reitit/releases>
|
||||
* This will trigger the automated release workflow <https://github.com/metosin/reitit/actions/workflows/release.yml>
|
||||
* Announce the release at least on #reitit in Clojurians.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Default Interceptors
|
||||
|
||||
```clj
|
||||
[metosin/reitit-interceptors "0.9.1"]
|
||||
[metosin/reitit-interceptors "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
Just like the [ring default middleware](../ring/default_middleware.md), but for interceptors.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Reitit has also support for [interceptors](http://pedestal.io/reference/intercep
|
|||
## Reitit-http
|
||||
|
||||
```clj
|
||||
[metosin/reitit-http "0.9.1"]
|
||||
[metosin/reitit-http "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
A module for http-routing using interceptors instead of middleware. Builds on top of the [`reitit-ring`](../ring/ring.md) module having all the same features.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
[Pedestal](http://pedestal.io/) is a backend web framework for Clojure. `reitit-pedestal` provides an alternative routing engine for Pedestal.
|
||||
|
||||
```clj
|
||||
[metosin/reitit-pedestal "0.9.1"]
|
||||
[metosin/reitit-pedestal "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
Why should one use reitit instead of the Pedestal [default routing](http://pedestal.io/reference/routing-quick-reference)?
|
||||
|
|
@ -26,8 +26,8 @@ A minimalistic example on how to to swap the default-router with a reitit router
|
|||
```clj
|
||||
; [io.pedestal/pedestal.service "0.5.5"]
|
||||
; [io.pedestal/pedestal.jetty "0.5.5"]
|
||||
; [metosin/reitit-pedestal "0.9.1"]
|
||||
; [metosin/reitit "0.9.1"]
|
||||
; [metosin/reitit-pedestal "0.9.2-rc1"]
|
||||
; [metosin/reitit "0.9.2-rc1"]
|
||||
|
||||
(require '[io.pedestal.http :as server])
|
||||
(require '[reitit.pedestal :as pedestal])
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Sieppari
|
||||
|
||||
```clj
|
||||
[metosin/reitit-sieppari "0.9.1"]
|
||||
[metosin/reitit-sieppari "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
[Sieppari](https://github.com/metosin/sieppari) is a new and fast interceptor implementation for Clojure, with pluggable async supporting [core.async](https://github.com/clojure/core.async), [Manifold](https://github.com/ztellman/manifold) and [Promesa](http://funcool.github.io/promesa/latest).
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ There is an extra option in http-router (actually, in the underlying interceptor
|
|||
### Printing Context Diffs
|
||||
|
||||
```clj
|
||||
[metosin/reitit-interceptors "0.9.1"]
|
||||
[metosin/reitit-interceptors "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Default Middleware
|
||||
|
||||
```clj
|
||||
[metosin/reitit-middleware "0.9.1"]
|
||||
[metosin/reitit-middleware "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Exception Handling with Ring
|
||||
|
||||
```clj
|
||||
[metosin/reitit-middleware "0.9.1"]
|
||||
[metosin/reitit-middleware "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
Read more about the [Ring Concepts](https://github.com/ring-clojure/ring/wiki/Concepts).
|
||||
|
||||
```clj
|
||||
[metosin/reitit-ring "0.9.1"]
|
||||
[metosin/reitit-ring "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
## `reitit.ring/router`
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Swagger Support
|
||||
|
||||
```
|
||||
[metosin/reitit-swagger "0.9.1"]
|
||||
[metosin/reitit-swagger "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
Reitit supports [Swagger2](https://swagger.io/) documentation, thanks to [schema-tools](https://github.com/metosin/schema-tools) and [spec-tools](https://github.com/metosin/spec-tools). Documentation is extracted from route definitions, coercion `:parameters` and `:responses` and from a set of new documentation keys.
|
||||
|
|
@ -47,7 +47,7 @@ If you need to post-process the generated spec, just wrap the handler with a cus
|
|||
[Swagger-ui](https://github.com/swagger-api/swagger-ui) is a user interface to visualize and interact with the Swagger specification. To make things easy, there is a pre-integrated version of the swagger-ui as a separate module.
|
||||
|
||||
```
|
||||
[metosin/reitit-swagger-ui "0.9.1"]
|
||||
[metosin/reitit-swagger-ui "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
`reitit.swagger-ui/create-swagger-ui-handler` can be used to create a ring-handler to serve the swagger-ui. It accepts the following options:
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ There is an extra option in the Ring router (actually, in the underlying middlew
|
|||
### Printing Request Diffs
|
||||
|
||||
```clj
|
||||
[metosin/reitit-middleware "0.9.1"]
|
||||
[metosin/reitit-middleware "0.9.2-rc1"]
|
||||
```
|
||||
|
||||
Using `reitit.ring.middleware.dev/print-request-diffs` transformation, the request diffs between each middleware are printed out to the console. To use it, add the following router option:
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@
|
|||
:description "Reitit Buddy Auth App"
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
[buddy "2.0.0"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit-schema "0.9.1"]
|
||||
[metosin/reitit-frontend "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
[metosin/reitit-schema "0.9.2-rc1"]
|
||||
[metosin/reitit-frontend "0.9.2-rc1"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit-schema "0.9.1"]
|
||||
[metosin/reitit-frontend "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
[metosin/reitit-schema "0.9.2-rc1"]
|
||||
[metosin/reitit-frontend "0.9.2-rc1"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.10.520"]
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit-spec "0.9.1"]
|
||||
[metosin/reitit-frontend "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
[metosin/reitit-spec "0.9.2-rc1"]
|
||||
[metosin/reitit-frontend "0.9.2-rc1"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit-malli "0.9.1"]
|
||||
[metosin/reitit-frontend "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
[metosin/reitit-malli "0.9.2-rc1"]
|
||||
[metosin/reitit-frontend "0.9.2-rc1"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit-spec "0.9.1"]
|
||||
[metosin/reitit-frontend "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
[metosin/reitit-spec "0.9.2-rc1"]
|
||||
[metosin/reitit-frontend "0.9.2-rc1"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
(defproject frontend-re-frame "0.1.0-SNAPSHOT"
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
[reagent "1.2.0"]
|
||||
[re-frame "0.10.6"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
[ring "1.12.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit-spec "0.9.1"]
|
||||
[metosin/reitit-frontend "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
[metosin/reitit-spec "0.9.2-rc1"]
|
||||
[metosin/reitit-frontend "0.9.2-rc1"]
|
||||
[cljsjs/react "17.0.2-0"]
|
||||
[cljsjs/react-dom "17.0.2-0"]
|
||||
;; Just for pretty printting the match
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[aleph "0.7.1"]
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
[metosin/ring-swagger-ui "5.9.0"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@
|
|||
[funcool/promesa "11.0.678"]
|
||||
[manifold "0.4.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.1"]]
|
||||
[metosin/reitit "0.9.2-rc1"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
:description "Reitit coercion with vanilla ring"
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.1"]])
|
||||
[metosin/reitit "0.9.2-rc1"]])
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[metosin/jsonista "0.3.8"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
[metosin/ring-swagger-ui "5.9.0"]]
|
||||
:repl-options {:init-ns example.server}
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}})
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[io.pedestal/pedestal.service "0.6.3"]
|
||||
[io.pedestal/pedestal.jetty "0.6.3"]
|
||||
[metosin/reitit-malli "0.9.1"]
|
||||
[metosin/reitit-pedestal "0.9.1"]
|
||||
[metosin/reitit "0.9.1"]]
|
||||
[metosin/reitit-malli "0.9.2-rc1"]
|
||||
[metosin/reitit-pedestal "0.9.2-rc1"]
|
||||
[metosin/reitit "0.9.2-rc1"]]
|
||||
:repl-options {:init-ns server})
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[io.pedestal/pedestal.service "0.6.3"]
|
||||
[io.pedestal/pedestal.jetty "0.6.3"]
|
||||
[metosin/reitit-pedestal "0.9.1"]
|
||||
[metosin/reitit "0.9.1"]]
|
||||
[metosin/reitit-pedestal "0.9.2-rc1"]
|
||||
[metosin/reitit "0.9.2-rc1"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[io.pedestal/pedestal.service "0.6.3"]
|
||||
[io.pedestal/pedestal.jetty "0.6.3"]
|
||||
[metosin/reitit-pedestal "0.9.1"]
|
||||
[metosin/reitit "0.9.1"]]
|
||||
[metosin/reitit-pedestal "0.9.2-rc1"]
|
||||
[metosin/reitit "0.9.2-rc1"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -2,5 +2,5 @@
|
|||
:description "Reitit Ring App"
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.1"]]
|
||||
[metosin/reitit "0.9.2-rc1"]]
|
||||
:repl-options {:init-ns example.server})
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
:description "Reitit Ring App with Integrant"
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
[integrant "0.8.1"]]
|
||||
:main example.server
|
||||
:repl-options {:init-ns user}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[metosin/jsonista "0.3.8"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.1"]]
|
||||
[metosin/reitit "0.9.2-rc1"]]
|
||||
:repl-options {:init-ns example.server}
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}})
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[metosin/jsonista "0.3.8"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
[metosin/ring-swagger-ui "5.9.0"]]
|
||||
:repl-options {:init-ns example.server}
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}})
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
:description "Reitit Ring App with Swagger"
|
||||
:dependencies [[org.clojure/clojure "1.11.2"]
|
||||
[ring/ring-jetty-adapter "1.12.1"]
|
||||
[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit "0.9.2-rc1"]
|
||||
[metosin/ring-swagger-ui "5.9.0"]]
|
||||
:repl-options {:init-ns example.server}
|
||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]}})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-core "0.9.1"
|
||||
(defproject metosin/reitit-core "0.9.2-rc1"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -198,9 +198,8 @@
|
|||
(:path route)))
|
||||
|
||||
(defn throw-on-missing-path-params [template required path-params]
|
||||
(when-not (every? #(contains? path-params %) required)
|
||||
(let [defined (-> path-params keys set)
|
||||
missing (set/difference required defined)]
|
||||
(let [missing (set (remove #(get path-params %) required))]
|
||||
(when-not (empty? missing)
|
||||
(ex/fail!
|
||||
(str "missing path-params for route " template " -> " missing)
|
||||
{:path-params path-params, :required required}))))
|
||||
|
|
|
|||
121
modules/reitit-core/src/reitit/regex.cljc
Normal file
121
modules/reitit-core/src/reitit/regex.cljc
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
(ns reitit.regex
|
||||
(:require [clojure.set :as set]
|
||||
[clojure.string :as str]
|
||||
[reitit.core :as r]))
|
||||
|
||||
(defn compile-regex-route
|
||||
"Given a route vector [path route-data], returns a map with:
|
||||
- :pattern: a compiled regex pattern built from the path segments,
|
||||
- :group-keys: vector of parameter keys in order,
|
||||
- :route-data: the provided route data,
|
||||
- :original-segments: original path segments for path generation,
|
||||
- :template: the original path template for Match objects."
|
||||
[[path route-data]]
|
||||
(let [;; Normalize route-data to ensure it's a map with :name
|
||||
route-data (if (keyword? route-data)
|
||||
{:name route-data}
|
||||
route-data)
|
||||
|
||||
;; Store the original path template for Match objects
|
||||
template (if (str/starts-with? path "/")
|
||||
path
|
||||
(str "/" path))
|
||||
|
||||
;; Handle paths with or without leading slashes
|
||||
normalized-path (cond-> path
|
||||
(str/starts-with? path "/") (subs 1))
|
||||
|
||||
;; Split into segments, handling empty paths
|
||||
segments (if (empty? normalized-path)
|
||||
[]
|
||||
(str/split normalized-path #"/"))
|
||||
|
||||
;; Store original segments for path generation
|
||||
original-segments segments
|
||||
|
||||
compiled-segments
|
||||
(map (fn [seg]
|
||||
(if (str/starts-with? seg ":")
|
||||
(let [param-key (keyword (subs seg 1))
|
||||
param-regex (get-in route-data [:parameters :path param-key])]
|
||||
(if (and param-regex (instance? java.util.regex.Pattern param-regex))
|
||||
(str "(" (.pattern ^java.util.regex.Pattern param-regex) ")")
|
||||
;; Fallback: match any non-slash characters.
|
||||
"([^/]+)"))
|
||||
(java.util.regex.Pattern/quote seg)))
|
||||
segments)
|
||||
|
||||
;; Create the pattern string, handling special case for root path
|
||||
pattern-str (if (empty? segments)
|
||||
"^/?$" ;; Match root path with optional trailing slash
|
||||
(str "^/" (str/join "/" compiled-segments) "$"))
|
||||
|
||||
group-keys (->> segments
|
||||
(filter #(str/starts-with? % ":"))
|
||||
(map #(keyword (subs % 1)))
|
||||
(vec))]
|
||||
|
||||
{:pattern (re-pattern pattern-str)
|
||||
:group-keys group-keys
|
||||
:route-data route-data
|
||||
:original-segments original-segments
|
||||
:template template}))
|
||||
|
||||
(defn- generate-path
|
||||
"Generate a path from a route and path parameters."
|
||||
[route path-params]
|
||||
(if (empty? (:original-segments route))
|
||||
"/"
|
||||
(str "/" (str/join "/"
|
||||
(map (fn [segment]
|
||||
(if (str/starts-with? segment ":")
|
||||
(let [param-key (keyword (subs segment 1))]
|
||||
(get path-params param-key ""))
|
||||
segment))
|
||||
(:original-segments route))))))
|
||||
|
||||
(defrecord RegexRouter [compiled-routes]
|
||||
r/Router
|
||||
(router-name [_] :regex-router)
|
||||
|
||||
(routes [_]
|
||||
(mapv (fn [{:keys [route-data original-segments]}]
|
||||
[(str "/" (str/join "/" original-segments)) route-data])
|
||||
compiled-routes))
|
||||
|
||||
(compiled-routes [_] compiled-routes)
|
||||
|
||||
(options [_] {})
|
||||
|
||||
(route-names [_]
|
||||
(keep (comp :name :route-data) compiled-routes))
|
||||
|
||||
(match-by-path [_ path]
|
||||
(some (fn [{:keys [pattern group-keys route-data template]}]
|
||||
(when-let [matches (re-matches pattern path)]
|
||||
(let [params (zipmap group-keys (rest matches))]
|
||||
(r/->Match template route-data nil params path))))
|
||||
compiled-routes))
|
||||
|
||||
(match-by-name [this name]
|
||||
(r/match-by-name this name {}))
|
||||
|
||||
(match-by-name [router name path-params]
|
||||
(when-let [{:keys [group-keys route-data template] :as route}
|
||||
(first (filter #(= name (get-in % [:route-data :name])) (r/compiled-routes router)))]
|
||||
;; Check if all required params are provided
|
||||
(let [required-params (set group-keys)
|
||||
provided-params (set (keys path-params))]
|
||||
(if (every? #(contains? provided-params %) required-params)
|
||||
;; All required params provided, return a Match
|
||||
(let [path (generate-path route path-params)]
|
||||
(r/->Match template route-data nil path-params path))
|
||||
;; Some required params missing, return a PartialMatch
|
||||
(let [missing (set/difference required-params provided-params)]
|
||||
(r/->PartialMatch template route-data nil path-params missing)))))))
|
||||
|
||||
(defn create-regex-router
|
||||
"Create a RegexRouter from a vector of routes.
|
||||
Each route should be a vector [path route-data]."
|
||||
[routes]
|
||||
(->RegexRouter (mapv compile-regex-route routes)))
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-dev "0.9.1"
|
||||
(defproject metosin/reitit-dev "0.9.2-rc1"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-frontend "0.9.1"
|
||||
(defproject metosin/reitit-frontend "0.9.2-rc1"
|
||||
:description "Reitit: Clojurescript frontend routing core"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-http "0.9.1"
|
||||
(defproject metosin/reitit-http "0.9.2-rc1"
|
||||
:description "Reitit: HTTP routing with interceptors"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-interceptors "0.9.1"
|
||||
(defproject metosin/reitit-interceptors "0.9.2-rc1"
|
||||
:description "Reitit, common interceptors bundled"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-malli "0.9.1"
|
||||
(defproject metosin/reitit-malli "0.9.2-rc1"
|
||||
:description "Reitit: Malli coercion"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-middleware "0.9.1"
|
||||
(defproject metosin/reitit-middleware "0.9.2-rc1"
|
||||
:description "Reitit, common middleware bundled"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject fi.metosin/reitit-openapi "0.9.1"
|
||||
(defproject fi.metosin/reitit-openapi "0.9.2-rc1"
|
||||
:description "Reitit: OpenAPI-support"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-pedestal "0.9.1"
|
||||
(defproject metosin/reitit-pedestal "0.9.2-rc1"
|
||||
:description "Reitit + Pedestal Integration"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-ring "0.9.1"
|
||||
(defproject metosin/reitit-ring "0.9.2-rc1"
|
||||
:description "Reitit: Ring routing"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-schema "0.9.1"
|
||||
(defproject metosin/reitit-schema "0.9.2-rc1"
|
||||
:description "Reitit: Plumatic Schema coercion"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-sieppari "0.9.1"
|
||||
(defproject metosin/reitit-sieppari "0.9.2-rc1"
|
||||
:description "Reitit: Sieppari Interceptors"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-spec "0.9.1"
|
||||
(defproject metosin/reitit-spec "0.9.2-rc1"
|
||||
:description "Reitit: clojure.spec coercion"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-swagger-ui "0.9.1"
|
||||
(defproject metosin/reitit-swagger-ui "0.9.2-rc1"
|
||||
:description "Reitit: Swagger-ui support"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-swagger "0.9.1"
|
||||
(defproject metosin/reitit-swagger "0.9.2-rc1"
|
||||
:description "Reitit: Swagger-support"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit "0.9.1"
|
||||
(defproject metosin/reitit "0.9.2-rc1"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
|||
2145
package-lock.json
generated
2145
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -2,13 +2,13 @@
|
|||
"name": "reitit",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@seriousme/openapi-schema-validator": "^2.4.0",
|
||||
"@seriousme/openapi-schema-validator": "^2.7.0",
|
||||
"karma": "^6.4.4",
|
||||
"karma-chrome-launcher": "^3.2.0",
|
||||
"karma-cli": "^2.0.0",
|
||||
"karma-cljs-test": "^0.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"shadow-cljs": "^2.28.22"
|
||||
"shadow-cljs": "^3.2.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
83
project.clj
83
project.clj
|
|
@ -1,4 +1,4 @@
|
|||
(defproject metosin/reitit-parent "0.9.1"
|
||||
(defproject metosin/reitit-parent "0.9.2-rc1"
|
||||
:description "Snappy data-driven router for Clojure(Script)"
|
||||
:url "https://github.com/metosin/reitit"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
@ -6,7 +6,8 @@
|
|||
:test-paths ["test/clj" "test/cljc"]
|
||||
:deploy-repositories [["clojars" {:url "https://repo.clojars.org"
|
||||
:username :env/clojars_username
|
||||
:password :env/clojars_password}]]
|
||||
:password :env/clojars_password
|
||||
:sign-releases false}]]
|
||||
:repositories [["clojars" {:url "https://repo.clojars.org"
|
||||
:username :env/clojars_username
|
||||
:password :env/clojars_password}]]
|
||||
|
|
@ -15,48 +16,47 @@
|
|||
:metadata {:doc/format :markdown}}
|
||||
:scm {:name "git"
|
||||
:url "https://github.com/metosin/reitit"}
|
||||
;; TODO: need to verify that the code actually worked with Java1.8, see #242
|
||||
;; Ring 1.13.1 drops support for Java 1.8 so lets target 11
|
||||
:javac-options ["-Xlint:unchecked" "-target" "11" "-source" "11"]
|
||||
:managed-dependencies [[metosin/reitit "0.9.1"]
|
||||
[metosin/reitit-core "0.9.1"]
|
||||
[metosin/reitit-dev "0.9.1"]
|
||||
[metosin/reitit-spec "0.9.1"]
|
||||
[metosin/reitit-malli "0.9.1"]
|
||||
[metosin/reitit-schema "0.9.1"]
|
||||
[metosin/reitit-ring "0.9.1"]
|
||||
[metosin/reitit-middleware "0.9.1"]
|
||||
[metosin/reitit-http "0.9.1"]
|
||||
[metosin/reitit-interceptors "0.9.1"]
|
||||
[metosin/reitit-swagger "0.9.1"]
|
||||
[fi.metosin/reitit-openapi "0.9.1"]
|
||||
[metosin/reitit-swagger-ui "0.9.1"]
|
||||
[metosin/reitit-frontend "0.9.1"]
|
||||
[metosin/reitit-sieppari "0.9.1"]
|
||||
[metosin/reitit-pedestal "0.9.1"]
|
||||
:managed-dependencies [[metosin/reitit "0.9.2-rc1"]
|
||||
[metosin/reitit-core "0.9.2-rc1"]
|
||||
[metosin/reitit-dev "0.9.2-rc1"]
|
||||
[metosin/reitit-spec "0.9.2-rc1"]
|
||||
[metosin/reitit-malli "0.9.2-rc1"]
|
||||
[metosin/reitit-schema "0.9.2-rc1"]
|
||||
[metosin/reitit-ring "0.9.2-rc1"]
|
||||
[metosin/reitit-middleware "0.9.2-rc1"]
|
||||
[metosin/reitit-http "0.9.2-rc1"]
|
||||
[metosin/reitit-interceptors "0.9.2-rc1"]
|
||||
[metosin/reitit-swagger "0.9.2-rc1"]
|
||||
[fi.metosin/reitit-openapi "0.9.2-rc1"]
|
||||
[metosin/reitit-swagger-ui "0.9.2-rc1"]
|
||||
[metosin/reitit-frontend "0.9.2-rc1"]
|
||||
[metosin/reitit-sieppari "0.9.2-rc1"]
|
||||
[metosin/reitit-pedestal "0.9.2-rc1"]
|
||||
[metosin/ring-swagger-ui "5.20.0"]
|
||||
[metosin/spec-tools "0.10.7"]
|
||||
[metosin/schema-tools "0.13.1"]
|
||||
[metosin/muuntaja "0.6.11"]
|
||||
[metosin/jsonista "0.3.13"]
|
||||
[metosin/sieppari "0.0.0-alpha13"]
|
||||
[metosin/malli "0.18.0"]
|
||||
[metosin/malli "0.19.2"]
|
||||
|
||||
;; https://clojureverse.org/t/depending-on-the-right-versions-of-jackson-libraries/5111
|
||||
[com.fasterxml.jackson.core/jackson-core "2.18.2"]
|
||||
[com.fasterxml.jackson.core/jackson-databind "2.18.2"]
|
||||
[com.fasterxml.jackson.core/jackson-core "2.20.0"]
|
||||
[com.fasterxml.jackson.core/jackson-databind "2.20.0"]
|
||||
|
||||
[meta-merge "1.0.0"]
|
||||
[fipp "0.6.27" :exclusions [org.clojure/core.rrb-vector]]
|
||||
[fipp "0.6.29" :exclusions [org.clojure/core.rrb-vector]]
|
||||
;; Deep-diff uses this version, override olders versiom from fipp.
|
||||
[org.clojure/core.rrb-vector "0.2.0"]
|
||||
[expound "0.9.0"]
|
||||
[lambdaisland/deep-diff "0.0-47"]
|
||||
[com.bhauman/spell-spec "0.1.2"]
|
||||
[mvxcvi/arrangement "2.1.0"]
|
||||
[ring/ring-core "1.14.1"]
|
||||
[ring/ring-core "1.15.3"]
|
||||
|
||||
[io.pedestal/pedestal.service "0.6.4"]]
|
||||
[io.pedestal/pedestal.service "0.6.4" :upgrade false]]
|
||||
|
||||
:plugins [[jonase/eastwood "1.4.3"]
|
||||
;[lein-virgil "0.1.7"]
|
||||
|
|
@ -90,8 +90,8 @@
|
|||
:java-source-paths ["modules/reitit-core/java-src"]
|
||||
|
||||
:dependencies [[org.clojure/clojure "1.11.4"]
|
||||
[thheller/shadow-cljs "2.28.22"]
|
||||
[org.clojure/clojurescript "1.11.132"]
|
||||
[thheller/shadow-cljs "3.2.1"]
|
||||
[org.clojure/clojurescript "1.12.42"]
|
||||
|
||||
;; modules dependencies
|
||||
[metosin/schema-tools "0.13.1"]
|
||||
|
|
@ -99,16 +99,16 @@
|
|||
[metosin/muuntaja "0.6.11"]
|
||||
[metosin/sieppari "0.0.0-alpha13"]
|
||||
[metosin/jsonista "0.3.13"]
|
||||
[metosin/malli "0.18.0"]
|
||||
[metosin/malli "0.19.2"]
|
||||
[lambdaisland/deep-diff "0.0-47"]
|
||||
[meta-merge "1.0.0"]
|
||||
[com.bhauman/spell-spec "0.1.2"]
|
||||
[expound "0.9.0"]
|
||||
[fipp "0.6.27"]
|
||||
[fipp "0.6.29"]
|
||||
|
||||
[orchestra "2021.01.01-1"]
|
||||
|
||||
[ring "1.14.1"]
|
||||
[ring "1.15.3"]
|
||||
[ikitommi/immutant-web "3.0.0-alpha1"]
|
||||
[metosin/ring-http-response "0.9.5"]
|
||||
[metosin/ring-swagger-ui "5.20.0"]
|
||||
|
|
@ -117,16 +117,17 @@
|
|||
[criterium "0.4.6"]
|
||||
[org.clojure/test.check "1.1.1"]
|
||||
[org.clojure/tools.namespace "1.5.0"]
|
||||
[com.gfredericks/test.chuck "0.2.14"]
|
||||
[nubank/matcher-combinators "3.9.1"]
|
||||
[com.gfredericks/test.chuck "0.2.15"]
|
||||
[nubank/matcher-combinators "3.9.2"]
|
||||
|
||||
[io.pedestal/pedestal.service "0.6.4"]
|
||||
;; TODO: adapt to breaking changes in pedestal 0.7 and 0.8
|
||||
[io.pedestal/pedestal.service "0.6.4" :upgrade false]
|
||||
|
||||
[org.clojure/core.async "1.7.701"]
|
||||
[org.clojure/core.async "1.8.741"]
|
||||
[manifold "0.4.3"]
|
||||
[funcool/promesa "11.0.678"]
|
||||
|
||||
[com.clojure-goes-fast/clj-async-profiler "1.6.1"]
|
||||
[com.clojure-goes-fast/clj-async-profiler "1.6.2"]
|
||||
[ring-cors "0.1.13"]
|
||||
|
||||
[com.bhauman/rebel-readline "0.1.5"]]}
|
||||
|
|
@ -135,18 +136,18 @@
|
|||
"-Xmx4096m"
|
||||
"-Dclojure.compiler.direct-linking=true"]
|
||||
:test-paths ["perf-test/clj"]
|
||||
:dependencies [[compojure "1.7.1"]
|
||||
[ring/ring-defaults "0.6.0"]
|
||||
:dependencies [[compojure "1.7.2"]
|
||||
[ring/ring-defaults "0.7.0"]
|
||||
[ikitommi/immutant-web "3.0.0-alpha1"]
|
||||
[io.pedestal/pedestal.service "0.6.4"]
|
||||
[io.pedestal/pedestal.jetty "0.6.4"]
|
||||
[io.pedestal/pedestal.service "0.6.4" :upgrade false]
|
||||
[io.pedestal/pedestal.jetty "0.6.4" :upgrade false]
|
||||
[calfpath "0.8.1"]
|
||||
[org.clojure/core.async "1.7.701"]
|
||||
[org.clojure/core.async "1.8.741"]
|
||||
[manifold "0.4.3"]
|
||||
[funcool/promesa "11.0.678"]
|
||||
[metosin/sieppari]
|
||||
[yada "1.2.16"]
|
||||
[aleph "0.8.3"]
|
||||
[aleph "0.9.3"]
|
||||
[ataraxy "0.4.3"]
|
||||
[bidi "2.1.6"]
|
||||
[janus "1.3.2"]]}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
|
||||
(testing "routers handling wildcard paths"
|
||||
(are [r name]
|
||||
(testing "wild"
|
||||
|
||||
(testing (str name)
|
||||
(testing "simple"
|
||||
(let [router (r/router ["/api" ["/ipa" ["/:size" ::beer]]] {:router r})]
|
||||
(is (= name (r/router-name router)))
|
||||
|
|
@ -52,10 +51,17 @@
|
|||
:path-params nil})
|
||||
(r/match-by-name router ::beer)))
|
||||
(is (r/partial-match? (r/match-by-name router ::beer)))
|
||||
(is (r/partial-match? (r/match-by-name router ::beer {:size nil}))
|
||||
"nil counts as missing")
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo
|
||||
#"^missing path-params for route /api/ipa/:size -> \#\{:size\}$"
|
||||
(r/match-by-name! router ::beer))))))
|
||||
(r/match-by-name! router ::beer)))
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo
|
||||
#"^missing path-params for route /api/ipa/:size -> \#\{:size\}$"
|
||||
(r/match-by-name! router ::beer {:size nil}))
|
||||
"nil counts as missing"))))
|
||||
|
||||
(testing "decode %-encoded path params"
|
||||
(let [router (r/router [["/one-param-path/:param1" ::one]
|
||||
|
|
|
|||
|
|
@ -901,13 +901,11 @@
|
|||
:request {:description "body description"
|
||||
:content {"application/json" {:schema {:x int?, :y int?}
|
||||
:examples {"1+1" {:value {:x 1, :y 1}}
|
||||
"1+2" {:value {:x 1, :y 2}}}
|
||||
:openapi {:example {:x 2, :y 2}}}}}
|
||||
"1+2" {:value {:x 1, :y 2}}}}}}
|
||||
:responses {200 {:description "success"
|
||||
:content {"application/json" {:schema {:total int?}
|
||||
:examples {"2" {:value {:total 2}}
|
||||
"3" {:value {:total 3}}}
|
||||
:openapi {:example {:total 4}}}}}}
|
||||
"3" {:value {:total 3}}}}}}}
|
||||
:handler (fn [request]
|
||||
(let [{:keys [x y]} (-> request :parameters :body)]
|
||||
{:status 200, :body {:total (+ x y)}}))}}]]]
|
||||
|
|
@ -925,16 +923,14 @@
|
|||
:required [:x :y],
|
||||
:additionalProperties false},
|
||||
:examples {"1+1" {:value {:x 1, :y 1}}
|
||||
"1+2" {:value {:x 1, :y 2}}},
|
||||
:example {:x 2, :y 2}}}},
|
||||
"1+2" {:value {:x 1, :y 2}}}}}},
|
||||
:responses {200 {:description "success",
|
||||
:content {"application/json" {:schema {:type "object",
|
||||
:properties {:total {:type "integer"}},
|
||||
:required [:total],
|
||||
:additionalProperties false},
|
||||
:examples {"2" {:value {:total 2}},
|
||||
"3" {:value {:total 3}}},
|
||||
:example {:total 4}}}}},
|
||||
"3" {:value {:total 3}}}}}}},
|
||||
:summary "plus with body"}}}
|
||||
(:paths spec)))
|
||||
(is (nil? (validate spec))))
|
||||
|
|
|
|||
278
test/cljc/reitit/regex_test.cljc
Normal file
278
test/cljc/reitit/regex_test.cljc
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
(ns reitit.regex-test
|
||||
(:require [clojure.test :refer [deftest is testing]]
|
||||
[reitit.core :as r]
|
||||
[reitit.regex :as rt.regex]))
|
||||
|
||||
(defn re-=
|
||||
"A custom equality function that handles regex patterns specially.
|
||||
Returns true if a and b are equal, with special handling for regex patterns.
|
||||
Also handles comparing records by their map representation."
|
||||
[a b]
|
||||
(cond
|
||||
;; Handle record comparison by using their map representation
|
||||
(and (instance? clojure.lang.IRecord a)
|
||||
(instance? clojure.lang.IRecord b))
|
||||
(re-= (into {} a) (into {} b))
|
||||
|
||||
;; If both are regex patterns, compare their string representations
|
||||
(and (instance? java.util.regex.Pattern a)
|
||||
(instance? java.util.regex.Pattern b))
|
||||
(= (str a) (str b))
|
||||
|
||||
;; If one is a regex and the other isn't, they're not equal
|
||||
(or (instance? java.util.regex.Pattern a)
|
||||
(instance? java.util.regex.Pattern b))
|
||||
false
|
||||
|
||||
;; For maps, compare each key-value pair using regex-aware-equals
|
||||
(and (map? a) (map? b))
|
||||
(and (= (set (keys a)) (set (keys b)))
|
||||
(every? #(re-= (get a %) (get b %)) (keys a)))
|
||||
|
||||
;; For sequences, compare each element using regex-aware-equals
|
||||
(and (sequential? a) (sequential? b))
|
||||
(and (= (count a) (count b))
|
||||
(every? identity (map re-= a b)))
|
||||
|
||||
;; For sets, convert to sequences and compare
|
||||
(and (set? a) (set? b))
|
||||
(re-= (seq a) (seq b))
|
||||
|
||||
;; For everything else, use regular equality
|
||||
:else
|
||||
(= a b)))
|
||||
|
||||
(def routes
|
||||
(rt.regex/create-regex-router
|
||||
[["" ::home]
|
||||
[":item-id" {:name ::item
|
||||
:parameters {:path {:item-id #"[a-z]{16,20}"}}}]
|
||||
["inbox" ::inbox]
|
||||
["teams" ::teams]
|
||||
["teams/:team-id-b58/members" {:name ::->members
|
||||
:parameters {:path {:team-id-b58 #"[a-z]"}}}]]))
|
||||
|
||||
(deftest regex-match-by-path-test
|
||||
(testing "Basic path matching"
|
||||
(is (= (r/map->Match {:path "/"
|
||||
:path-params {}
|
||||
:data {:name ::home}
|
||||
:template "/"
|
||||
:result nil})
|
||||
(r/match-by-path routes "/")))
|
||||
|
||||
(is (= (r/map->Match {:path "/inbox"
|
||||
:path-params {}
|
||||
:data {:name ::inbox}
|
||||
:template "/inbox"
|
||||
:result nil})
|
||||
(r/match-by-path routes "/inbox")))
|
||||
|
||||
(is (= (r/map->Match {:path "/teams"
|
||||
:path-params {}
|
||||
:data {:name ::teams}
|
||||
:template "/teams"
|
||||
:result nil})
|
||||
(r/match-by-path routes "/teams"))))
|
||||
|
||||
(testing "Path with regex parameter"
|
||||
(let [valid-id "abcdefghijklmnopq"] ; 17 lowercase letters
|
||||
(is (re-= (r/map->Match {:path (str "/" valid-id)
|
||||
:path-params {:item-id valid-id}
|
||||
:data {:name ::item
|
||||
:parameters {:path {:item-id #"[a-z]{16,20}"}}},
|
||||
:template "/:item-id"
|
||||
:result nil})
|
||||
(r/match-by-path routes (str "/" valid-id)))))
|
||||
|
||||
;; Invalid parameter cases
|
||||
(is (nil? (r/match-by-path routes "/abcdefg")) "Too short")
|
||||
(is (nil? (r/match-by-path routes "/abcdefghijklmnopqRST")) "Contains uppercase")
|
||||
(is (nil? (r/match-by-path routes "/abcdefghijklmn1234")) "Contains digits"))
|
||||
|
||||
(testing "Nested path with parameter"
|
||||
(is (re-= (r/map->Match {:path "/teams/a/members"
|
||||
:path-params {:team-id-b58 "a"}
|
||||
:data {:name ::->members
|
||||
:parameters {:path {:team-id-b58 #"[a-z]"}}}
|
||||
:template "/teams/:team-id-b58/members"
|
||||
:result nil})
|
||||
(r/match-by-path routes "/teams/a/members")))
|
||||
|
||||
(is (nil? (r/match-by-path routes "/teams/abc/members")) "Multiple characters")
|
||||
(is (nil? (r/match-by-path routes "/teams/1/members")) "Digit instead of letter"))
|
||||
|
||||
(testing "Non-matching paths"
|
||||
(is (nil? (r/match-by-path routes "/unknown")))
|
||||
(is (nil? (r/match-by-path routes "/team"))) ; 'team' not 'teams'
|
||||
(is (nil? (r/match-by-path routes "/teams/extra/segments/here")))))
|
||||
|
||||
(deftest regex-match-by-name-test
|
||||
(testing "Basic match-by-name functionality"
|
||||
;; Root path
|
||||
(is (re-= (r/map->Match {:path "/"
|
||||
:path-params {}
|
||||
:data {:name ::home}
|
||||
:template "/"
|
||||
:result nil})
|
||||
(r/match-by-name routes ::home)))
|
||||
|
||||
;; Static paths
|
||||
(is (re-= (r/map->Match {:path "/inbox"
|
||||
:path-params {}
|
||||
:data {:name ::inbox}
|
||||
:template "/inbox"
|
||||
:result nil})
|
||||
(r/match-by-name routes ::inbox)))
|
||||
|
||||
(is (re-= (r/map->Match {:path "/teams"
|
||||
:path-params {}
|
||||
:data {:name ::teams}
|
||||
:template "/teams"
|
||||
:result nil})
|
||||
(r/match-by-name routes ::teams)))
|
||||
|
||||
;; Path with parameter
|
||||
(let [valid-id "abcdefghijklmnopq"]
|
||||
(is (re-= (r/map->Match {:path (str "/" valid-id)
|
||||
:path-params {:item-id valid-id}
|
||||
:data {:name ::item
|
||||
:parameters {:path {:item-id #"[a-z]{16,20}"}}}
|
||||
:template "/:item-id"
|
||||
:result nil})
|
||||
(r/match-by-name routes ::item {:item-id valid-id}))))
|
||||
|
||||
;; Nested path with parameter
|
||||
(is (re-= (r/map->Match {:path "/teams/a/members"
|
||||
:path-params {:team-id-b58 "a"}
|
||||
:data {:name ::->members
|
||||
:parameters {:path {:team-id-b58 #"[a-z]"}}}
|
||||
:template "/teams/:team-id-b58/members"
|
||||
:result nil})
|
||||
(r/match-by-name routes ::->members {:team-id-b58 "a"}))))
|
||||
|
||||
(testing "Path round-trip matching"
|
||||
;; Test that paths generated by match-by-name can be successfully matched by match-by-path
|
||||
(let [valid-id "abcdefghijklmnopq"
|
||||
match (r/match-by-name routes ::item {:item-id valid-id})
|
||||
path (:path match)]
|
||||
|
||||
(is (some? path) "Should generate a valid path")
|
||||
(is (re-= match (r/match-by-path routes path))
|
||||
"match-by-path should find the same route that generated the path"))
|
||||
|
||||
(let [match (r/match-by-name routes ::->members {:team-id-b58 "a"})
|
||||
path (:path match)]
|
||||
|
||||
(is (some? path) "Should generate a valid path")
|
||||
(is (re-= match (r/match-by-path routes path))
|
||||
"match-by-path should find the same route that generated the path")))
|
||||
|
||||
(testing "Partial match with missing parameters"
|
||||
;; Test that routes with missing parameters return PartialMatch
|
||||
(let [partial-match (r/match-by-name routes ::item {})]
|
||||
(is (instance? reitit.core.PartialMatch partial-match)
|
||||
"Should return a PartialMatch when params are missing")
|
||||
(is (= #{:item-id} (:required partial-match))
|
||||
"PartialMatch should indicate the required parameters")
|
||||
(is (re-= (r/map->PartialMatch {:template "/:item-id"
|
||||
:data {:name ::item
|
||||
:parameters {:path {:item-id #"[a-z]{16,20}"}}}
|
||||
:path-params {}
|
||||
:required #{:item-id}
|
||||
:result nil})
|
||||
partial-match)))
|
||||
|
||||
;; Test for a nested path with missing parameters
|
||||
(let [partial-match (r/match-by-name routes ::->members {})]
|
||||
(is (instance? reitit.core.PartialMatch partial-match)
|
||||
"Should return a PartialMatch for nested paths too")
|
||||
(is (= #{:team-id-b58} (:required partial-match))
|
||||
"PartialMatch should indicate the required parameters")))
|
||||
|
||||
(testing "Match with invalid parameters"
|
||||
;; Invalid parameters (that don't match the regex) still produce a Match
|
||||
(let [match (r/match-by-name routes ::item {:item-id "too-short"})
|
||||
path (:path match)]
|
||||
|
||||
(is (instance? reitit.core.Match match)
|
||||
"Should produce a Match even with invalid parameters")
|
||||
(is (= "/too-short" path)
|
||||
"Path should contain the provided parameter value")
|
||||
(is (nil? (r/match-by-path routes path))
|
||||
"Path with invalid parameter shouldn't be matchable by match-by-path")))
|
||||
|
||||
(testing "Non-existent routes"
|
||||
(is (nil? (r/match-by-name routes ::non-existent))
|
||||
"Should return nil for non-existent routes")))
|
||||
|
||||
(deftest regex-router-edge-cases-test
|
||||
(testing "Empty router"
|
||||
(let [empty-router (rt.regex/create-regex-router [])]
|
||||
(is (nil? (r/match-by-path empty-router "/any/path")))))
|
||||
|
||||
(testing "Handling trailing slashes"
|
||||
(is (nil? (r/match-by-path routes "/inbox/")))
|
||||
|
||||
(let [router-with-trailing-slash (rt.regex/create-regex-router [["inbox/" ::inbox-with-slash]])]
|
||||
(is (nil? (r/match-by-path router-with-trailing-slash "/inbox/")))
|
||||
(is (some? (r/match-by-path router-with-trailing-slash "/inbox")))))
|
||||
|
||||
(testing "Complex path patterns"
|
||||
(let [complex-router (rt.regex/create-regex-router
|
||||
[["articles/:year/:month/:slug"
|
||||
{:name ::article
|
||||
:parameters {:path {:year #"\d{4}"
|
||||
:month #"\d{2}"
|
||||
:slug #"[a-z0-9\-]+"}}}]
|
||||
["files/:path*"
|
||||
{:name ::file-path}]])]
|
||||
|
||||
;; Test article route with valid params
|
||||
(let [match (r/match-by-name complex-router ::article
|
||||
{:year "2023" :month "02" :slug "test-article"})]
|
||||
(is (instance? reitit.core.Match match)
|
||||
"Should return a Match for complex routes with valid params")
|
||||
(is (= "/articles/2023/02/test-article" (:path match))
|
||||
"Path should be constructed correctly"))
|
||||
|
||||
;; Test match-by-path with the generated path
|
||||
(let [match (r/match-by-path complex-router "/articles/2023/02/test-article")]
|
||||
(is (some? match)
|
||||
"Should match a valid article path")
|
||||
(is (= {:year "2023", :month "02", :slug "test-article"}
|
||||
(:path-params match))
|
||||
"Should extract all parameters correctly"))
|
||||
|
||||
;; Test invalid path
|
||||
(is (nil? (r/match-by-path complex-router "/articles/202/02/test-article"))
|
||||
"Should not match an invalid year (3 digits)")
|
||||
|
||||
;; Test partial params
|
||||
(let [partial-match (r/match-by-name complex-router ::article {:year "2023"})]
|
||||
(is (instance? reitit.core.PartialMatch partial-match)
|
||||
"Should return PartialMatch when some params are missing")
|
||||
(is (= #{:month :slug} (:required partial-match))
|
||||
"Should indicate which params are missing")))))
|
||||
|
||||
(deftest custom-router-features-test
|
||||
(testing "Router information access"
|
||||
;; Test that router information methods work properly
|
||||
(is (= :regex-router (r/router-name routes))
|
||||
"Should return the correct router name")
|
||||
|
||||
(is (seq (r/routes routes))
|
||||
"Should return the list of routes")
|
||||
|
||||
(is (= (set [::home ::item ::inbox ::teams ::->members])
|
||||
(set (r/route-names routes)))
|
||||
"Should return all route names"))
|
||||
|
||||
(testing "Compiled routes access"
|
||||
(let [compiled (r/compiled-routes routes)]
|
||||
(is (seq compiled)
|
||||
"Should return compiled routes")
|
||||
(is (every? :pattern compiled)
|
||||
"Every compiled route should have a pattern")
|
||||
(is (every? :route-data compiled)
|
||||
"Every compiled route should have route data"))))
|
||||
Loading…
Reference in a new issue