Giter VIP home page Giter VIP logo

reitit's People

Contributors

alexanderjamesking avatar bsless avatar caioaao avatar cap10morgan avatar dancek avatar dawranliou avatar deraen avatar dharrigan avatar fraxu avatar frwdrik avatar heyarne avatar ikitommi avatar jahson avatar johannesloetzsch avatar keoko avatar koura avatar martinklepsch avatar miikka avatar mvarela avatar olimsaidov avatar opqdonut avatar piotr-yuxuan avatar prestancedesign avatar stig avatar timokramer avatar valerauko avatar vharmain avatar wandersoncferreira avatar zelark avatar zengxinhui avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reitit's Issues

Post body parameters are not being coerced by spec

Apologies if I'm just doing something stupid, but in your ring-swagger example, if I just change these lines:

-         {:get {:summary "plus with spec"
-                :parameters {:query {:x int?, :y int?}}
+         {:post {:summary "plus with spec"
+                :parameters {:body {:x keyword?, :y int?}}
                 :responses {200 {:body {:total int?}}}
                 :handler (fn [{{{:keys [x y]} :query} :parameters}]
                            {:status 200
-                            :body {:total (+ x y)}})}}]]
+                            :body {:total (+ 1 y)}})}}]]

I get a spec error:

{
  "spec": "(spec-tools.core/spec {:spec (clojure.spec.alpha/keys :req-un [:$spec10430/x :$spec10430/y]), :type :map, :keys #{:y :x}, :keys/req #{:y :x}})",
  "problems": [
    {
      "path": [
        "x"
      ],
      "pred": "clojure.core/keyword?",
      "val": "string",
      "via": [
        "$spec10430/x"
      ],
      "in": [
        "x"
      ]
    }
  ],
  "type": "reitit.coercion/request-coercion",
  "coercion": "spec",
  "value": {
    "x": "string",
    "y": 0
  },
  "in": [
    "request",
    "body-params"
  ]
}

Changing the :body to :query fixes the problem (and I get a keyword as expected). So coercion is working, just not for :body?

Attaching the Swagger UI showing the data posted and the response.

Swagger UI.pdf

Result of ring-handler doesn't conform to spec

It seems that the thing that is returned by reitit.ring/ring-handler doesn't conform to the :ring/handler spec:

(s/explain :ring/handler (rr/ring-handler (rr/router ["" #(prn "")])))
In: [:args 0] val: :sync fails spec: :ring/request at: [:sync+async :fn :sync :args :request] predicate: map?
In: [:args 0] val: :sync fails spec: :ring/request at: [:sync+async :fn :async :args :request] predicate: map?
In: [:ret] val: [:async nil] fails spec: :ring/response at: [:sync+async :fn :sync :ret] predicate: map?
val: nil fails spec: :ring/response at: [:sync :ret] predicate: map?
val: ({:server-port 1, :server-name "", :remote-addr "", :uri "/", :scheme :http, :protocol "HTTP/1.1", :headers {}, :request-method :a} #object[clojure.spec.alpha$fspec_impl$reify__2451$fn__2454 0x658e90f6 "clojure.spec.alpha$fspec_impl$reify__2451$fn__2454@658e90f6"] #object[clojure.spec.alpha$fspec_impl$reify__2451$fn__2454 0x343045e0 "clojure.spec.alpha$fspec_impl$reify__2451$fn__2454@343045e0"]) fails spec: :ring.async/handler at: [:async] predicate: (apply fn),  Assert failed: In: [0] val: nil fails spec: :ring/response at: [:response] predicate: map?

(pvalid? argspec args)

This is a simple example to show the issue, I also see the problem with a more complex (and working) ring handler.

Interceptor-router

Either an example or a real implementation with accumulated :interceptor &/ :handler meta-data to create a Pedestal-style routing interceptor.

Home for common data-driven Middleware

Where should be host ring-middleware that have been wrapped as data-driven Middleware? For example, data-driven version of Muuntaja Middleware should done one of the following:

  1. emit data for the swagger to use (:produces & :consumes)
  2. read route data to guide which formats are available (:produces & :consumes or just :formats)

Alternatives:

  1. PR the changes into original repos => not likely to be accepted
  2. Create new reitit-modules from each of the common middleware. Would be quite small
  3. Create a single big reitit-middleware module with most of the common things within (params, security, headers, body, cors etc)
  4. promote data-driven middleware to @weavejester so that they could be hosted on ring repo
  5. something else

Mounted middleware could publish data to the route-meta

We could extract information from middleware and publish them to handler they are linked to. Kekkonen does this for interceptors already, allowing input & output parameters to be read from interceptors.

As a bigger solution, it should be possible to accumulate any data from middleware into handlers, for example, all keys with namespace middleware. Middleware wouldn't even need to have the :wrap defined, just accumulate data. Sweet.

(def wrap-load-user-from-token
  "add's requirement for a `:x-token` header parameter,
  publishes `:user` key under request"
  (middleware/create
    {:name ::wrap-load-user
     :middleware/parameters {:header {:x-token string?}}
     :provides {:user}
     :wrap ...}))

Still, not sure if it's a good idea. Accumulating data from 3rd party middleware into route-tree...

Support for Swagger/OpenAPI

Proposal:

  • metosin/reitit-swagger (and metosin/reitit-openapi) module
  • :responses and :parameters data is read from coercion
  • other keys: :no-docs (disable docs), :swagger (any swagger-data)
  • swagger-middleware that doesn't participate in request processing but provides specs for the new keys for route data validation
  • swagger-spec-handler that produces the swagger-spec
  • [:swagger :id] defines the api(docs). All routes with the same id are part of the apidcs
    • enables multiple apidocs per routing tree. Value can be keyword or set of keyword
    • enables route to be part of multiple api-docs

Draft with data-specs:

(require '[reitit.ring :as ring])
(require '[reitit.ring.swagger :as swagger])
(require '[reitit.ring.coercion :as rrc])
(require '[reitit.coercion.spec :as spec])

(def app
  (ring/ring-handler
    (ring/router
      ["/api"
       ;; identify a swagger api
       ;; there can be several in a routing tree
       {:swagger {:id :math}}

       ;; the (undocumented) swagger spec endpoint
       ["/swagger.json"
        {:get {:no-doc true
               :swagger {:info {:title "my-api"}}
               :handler swagger/swagger-spec-handler}}]

       ["/minus"
        {:get {:summary "minus"
               :parameters {:query {:x int?, :y int?}}
               :responses {200 {:body {:total int?}}}
               :handler (fn [{{{:keys [x y]} :query} :parameters}]
                          {:status 200, :body {:total (- x y)}})}}]

       ["/plus"
        {:get {:summary "plus"
               :parameters {:query {:x int?, :y int?}}
               :responses {200 {:body {:total int?}}}
               :handler (fn [{{{:keys [x y]} :query} :parameters}]
                          {:status 200, :body {:total (+ x y)}})}}]]

      {:data {:middleware [;; does not particiate in request processing
                           ;; just defines specs for the extra keys
                           swagger/swagger-middleware
                           rrc/coerce-exceptions-middleware
                           rrc/coerce-request-middleware
                           rrc/coerce-response-middleware]
              :coercion spec/coercion}})))

Coerce both query & path parameters by default

Currently, reitit.coercion/coerce! coerces just :path-parameters. We could add support to coerce also :query-parameters as it's a common case in routing. :query-parameters could be injeted into Match or passed as a optional second argument into coerce!.

Currently:

(defn coerce!
  "Returns a map of coerced input parameters using pre-compiled
  coercers under `:result` (provided by [[compile-request-coercers]].
  If coercion or parameters are not defined, return `nil`"
  [match]
  (if-let [result (:result match)]
    (coerce-request result {:path-params (:path-params match)})))

Validate route-tree not to contain duplicate paths

Like the Pedestal prefix-tree router does. This would open up nice possibilities to optimise mixed trees with both wildcard & wildcard-free routes: the free-ones could be lookup-matched first. Also, if prefix-tree router is imported, we should test it's (ported) perf too. Might be best if tree has lot's of wildcard-routes...

Clojure.spec coercion of route data

Many routing components (router, middleware, interceptors) can contribute to route data structure. All should be able to contribute partial clojure.specs which should be merged to get the effective spec for route data.

e.g.

  • ring-handler defines keys like :get, :post, :middleware etc.
  • coercion middleware defines keys like :parameters and :responses
  • authentication middleware could define keys like ::role

=> effective spec from route would be a s/merged s/keys specs, with the keys: :get, :post, :middleware, :parameters, :responses.

=> these should be enforced at router creation, optionally failing on invalid keys, optionally with figwheel-style awesome keyword proposals.

Headers not appearing in handler

Using the code from the example and printing the payload, the header for accept: application/json is correctly changed, but the new Authorization header is not passed through to headers.

Handler Code

(defn handler [r]
  (clojure.pprint/pprint r)
  {:status 200 :body "ok"})

(def app
  (ring/ring-handler
    (ring/router
      ["/ping" {:get {:handler handler}}]
      {:data {:middleware [ring.middleware.params/wrap-params
                           muuntaja.middleware/wrap-format
                           rrc/coerce-exceptions-middleware
                           rrc/coerce-request-middleware
                           rrc/coerce-response-middleware]}})))

curl command

curl -v -H "Accept: application/json" -H "Authorization Token=...." http://0.0.0.0:8080/ping

headers value from print

 :headers
 {"accept" "application/json",
  "user-agent" "curl/7.56.1",
  "host" "0.0.0.0:8080"},

URL Query and Fragment Strings break match-by-path

Query and fragment strings cause wierd values for path parameters at the end of URLs, and cause routes to fail to match. In my opinion, path and fragment strings should either be coerced as :query-params and :fragment-id, extracted as :query-string and :fragment-string, or at least trimmed and ignored prior to matching. :path-params shouldn't include the query string appended to the final parameter. I am unsure of whether the :path key should trim or retain the query string.

(reitit/match-by-path 
  (reitit/router [["/foo" :foo] ["/bar/:id" :bar]]) 
  "/bar/100?a=b&c=d#foo")
;; Returns:
#_
#reitit.core.Match{:template "/bar/:id",
                   :data {:name :bar},
                   :result nil,
                   :path-params {:id "100?a=b&c=d#foo"},
                   :path "/bar/100?a=b&c=d#foo"}
(reitit/match-by-path 
  (reitit/router [["/foo" :foo] ["/bar/:id/baz" :bar]])
  "/bar/100/baz?a=b&c=d#foo")
;;Returns:
#_
nil

Handle both trailing & missing backslashes on routes

Both extra & missing backslashes should be handled somehow - fast & easy to use. Maybe new router option(s)?

Currently, for a router:

(r/router
  [["/dashboard" ::dashboard]
   ["/files/*" ::files]])

Both /dashboard/ and /files fail

httprouter has some logic to handle these.

Currently, clients can either strip or add a training / if a match is not found, but we should do better.

Frontend-router

Either an example or a real implementation for browser-app routing.

Encoding automatically response data

Currently, in compojure-api, all response bodies with clojure collections are automatically encoded into a format negotiated with the use (usin Muuntaja). If a return/response data is defined, ALL DATA is encoded.

response is not encoded:

(GET โ€œ/pingโ€ []
  (ok))

response is encoded:

(GET โ€œ/pingโ€ []
  :return (s/maybe s/Str)
  (ok))

currently, in reitit-ring with muuntaja, only collections are encoded. Returning nil from an endpoint will cause the data not to be encoded: no content-type is set and the body is an empty string. This blows up the client parsers.

alternatives

1) encode all bodies, always

;; yes
["/ping" {:handler (constantly {:status 500 :body {:a 1}})}]

;; yes
["/ping" {:handler (constantly {:status 200})}]

2) encode just collections

;; yes
["/ping" {:handler (constantly {:status 500 :body {:a 1}})}]

;; no
["/ping" {:handler (constantly {:status 200})}]

3) encode collections + nil

;; yes
["/ping" {:handler (constantly {:status 500 :body {:a 1}})}]

;; yes
["/ping" {:handler (constantly {:status 200})}]

;; no
["/ping" {:handler (constantly {:status 500 :body 123})}]

4) encode by default only collections, also body if a any :responses is defined in route data

;; yes
["/ping" {:handler (constantly {:status 500 :body {:a 1}})}]

;; yes
["/ping" {:responses {200 {:body any?}}
          :handler (constantly {:status 500})}]

;; no
["/ping" {:handler (constantly {:status 200})}]

5) encode by default only collections, also body if a any :responses is defined the returned status

  • bit slower as we need to read the status from a request
;; yes
["/ping" {:handler (constantly {:status 500 :body {:a 1}})}]

;; no
["/ping" {:responses {200 {:body any?}}
          :handler (constantly {:status 500})}]

;; no
["/ping" {:handler (constantly {:status 200})}]

Serving Resources with Reitit

Trying to swap out Compojure and Bidi for Reitit.

How should I go about serving resource paths or static file paths with Reitit? Compojure has resources which calls its resource-response and add-mime-type helpers.

Is there an equivalent in Reitit?

Ring-router

Either an example or a real implementation with accumulated :middleware + :handler compiled into Ring-handler.

How to prevent conflicts on index route?

I want to match "/users" -> :user/list and "/users/123" -> :user/detail 123, but Reitit does not seem to string an empty string as a terminating match. For example:

(r/router
  ["/"
   ["users/"
     ["" :user/list]
     [":id" :user/detail]]])
=>
clojure.lang.ExceptionInfo: Router contains conflicting routes:    
                               /users/
                            -> /users/:id

However, it works if I differentiate with a trailing slash:

(r/router
  ["/"
   ["users" :user/list]
   ["users/:id" :user/detail]]) ;; note slash differentiates

But now "/users/" won't match to :user/list.

If that's allowed, I would expect this to work:

(r/router
  ["/"
   ["users/" :user/list] ;; note slash here
   ["users/:id" :user/detail]])
=>
clojure.lang.ExceptionInfo: Router contains conflicting routes:                       
                               /users/
                            -> /users/:id

But :id matches on an empty string. I tried using a regex without luck.

Then I tried:

(r/router
["/"
["users/list" :user/list]
["users/:id" :user/detail]])
=>
clojure.lang.ExceptionInfo: Router contains conflicting routes:
/users/list
-> /users/:id


How do I constrain a parameter?

Middleware registry / lookup

It could be useful to be able to reference Middleware by their distinct names instead of instances. Names would be looked from explicit registry. Registry could be a map (name -> middleware) or via a registry construcing function , consuming a vector of Middleware instances. :name of Middleware could be used as a key.

Something like:

(extend-protocol IntoMiddleware
  #?(:clj  clojure.lang.Keyword
     :cljs cljs.core.Keyword)
  (into-middleware [this _ {:keys [middleware-registry]}]
    (into-middleware (middleware-registry this))))

allowing:

(require '[reitit.ring :as ring])

(ring/ring-router
  ["/api" {:middleware [::api-format]}
   ["/ping" (constantly {:status 200, :body {:ping "pong"}})]]
  {:middleware-registry {::api-format ....})

Related: macchiato-framework/macchiato-core#9

Ring handler response coercion

With most Clojure ring-web libs, one can return anything from a handler and it get's coerced into a valid Ring Response:

Reitit-ring should support something like this too, as otherwise, one has to always return a valid ring response map from handlers.

Suggestion

  • have a protocol IntoResponse and a middleware in reitit.ring to do this

Options how to integrate:

Easy

  • by default, prepend that into all middleware chains by the ring-router
  • configurable via ring-router options ::ring/wrap-handler?

Simple

  • user must prepend the default middleware into al chains (via ::middleware/transform option)
  • better control of what is happening?

Support Macchiato middleware-metadata

Just a sample code or a separate ns for it?

(require '[reitit.middleware :as middleware])

(extend-protocol middleware/IntoMiddleware

  #?(:clj  clojure.lang.Fn
     :cljs function)
  (into-middleware [this _ _]
    (let [macchiato-meta (-> this meta :macchiato/middleware)]
      (middleware/map->Middleware
        (merge
          macchiato-meta
          {:wrap this})))))

related: macchiato-framework/macchiato-core#6

Support Vars in router

I'm trying to have my router dynamically update when I recompile the sub-routes in another namespace.
I thought that using a var might give me that but it looks like it doesn't.

(r/match-by-path (r/router 
                    ["/foo"
                     #'m/router])
                   path)

No implementation of method: :expand of protocol:
#'reitit.core/Expand found for class: clojure.lang.Var

I'm not sure this makes sense at all. But that's a concern that might maybe be adressed.

Support for 404

Right now, reitit doesn't seem to have direct support for the 404 scenario. What I'm doing is creating a catch-all route, and then ignoring route conflicts by setting the :conflicts opt.

For example:

(def router
  (ring/ring-handler
    (ring/router
      [["/api"
        ["/query/:name" hello-n]
        ["/goodbye"     goodbye]
        ["/hello"       hello]]
       ["/*path"        not-found]]
      {:conflicts (comp println reitit.core/conflicts-str)})))

The downside of this is that I no longer get nice handy exceptions on route conflicts. :/

It would be nice to maybe have a 404 option in the opts map:

(def router
  (ring/ring-handler
    (ring/router
      ["/api"
        ["/query/:name" hello-n]
        ["/goodbye"     goodbye]
        ["/hello"       hello]]
      {:not-found not-found-handler}))) ; Something like this

I can put a pull-request together for this, if you want.

Separate Coercion from ring

It's also needed in the ClojureScript routing. r.g.

(router
   [["" ::frontpage]
    ["/users/:user-id" {:name ::user
                        :parameters {:user-id int?}}]]
  {:coercion reitit.coercion.spec/SpecCoercion})

Support for OpenAPI3

This spans over multiple repos, but can be done in parts.

Schema -> OpenAPI3

Spec -> OpenAPI3

  • metosin/spec-tools#105
  • should be easy, a new ns just like spec-tools.swagger overriding some JSON Schema parts (if any!)

Malli -> OpenAPI3

Reitit Coercion

  • currently, there is only one body-coercion for request and one for response. We should add support for content-type based coercion:

Coercion for all content-types

(require '[reitit.ring :as ring])
(require '[reitit.ring.coercion])
(require '[reitit.coercion.spec])
(require '[muuntaja.middleware])

(ring/ring-handler
  (ring/router
    ["/plus"
     {:get {:parameters {:body {:x int?, :y int?}}
            :responses {200 {:body {:total pos-int?}}}
            :handler (fn [{{{:keys [x y]} :body} :parameters}]
                       {:status 200, :body (+ x y)})}}]
    {:data {:middleware [muuntaja.middleware/wrap-format
                         reitit.ring.coercion/coerce-request-middleware
                         reitit.ring.coercion/coerce-response-middleware]
            :coercion reitit.coercion.spec/coercion}}))

Coercion per content-type

  • new type :content (from OpenAPI3) which allows content-type->model mappings.
  • should be disallowed with Swagger
(ring/ring-handler
  (ring/router
    ["/plus"
     {:get {:parameters {:content {"application/json" {:x neg-int?, :y neg-int?}
                                   "application/edn" {:x pos-int?, :y pos-int?}}}
            :responses {200 {:content {"application/json" {:json int?}
                                       "application/edn" {:edn int?}}}}
            :handler (fn [{{{:keys [x y]} :body} :parameters :as request}]
                       (let [content-type (-> request :muuntaja/response :format)
                             body (case content-type
                                    "application/json" {:json (+ x y)}
                                    "application/edn" {:edn (+ x y)})]
                         {:status 200, :body body}))}}]
    {:data {:middleware [muuntaja.middleware/wrap-format
                         reitit.ring.coercion/coerce-request-middleware
                         reitit.ring.coercion/coerce-response-middleware]
            :coercion reitit.coercion.spec/coercion}}))

... having both :body and :content could be allowed, the :body could be used as a default if the content-type doesn't match.

Support middleware & interceptor compilation (against match)

Examples like https://github.com/metosin/reitit#meta-data-based-extensions inspect the route :match at runtime. As the middleware/interceptor - chain is known at router creation time, we can compile the middleware/interceptors in advance for bette perf: captured values via closures are a order of magnitude faster to access than map lookups.

need to figure out the syntax for this:

current

;; function
{:middleware [#(wrap % :kikka)]}

;; generator
{:middleware [[wrap :kikka]}

maybe

;; generator with a special marker to inject the match
{:middleware [[ring/coerce-parameters :spec ::ring/match]]}

;; generator-generator: match => params => middleware
{:middleware [(ring/middleware ring/coerce-parameters [:spec]]}

How to best define endpoint / top-level swagger-data

adding things like :info to top-level route data causes the data to be merged to all endpoints, effecting broken swagger-data. Options:

  1. strip out non-endpoint data from endpoints in swagger-handler
  2. add the top-level data only to swagger-handler, so it's not merged to endpoints.

Body options always shown as JSON in Swagger UI

Expected behaviour

After setting the handler option :config {:jsonEditor true} body parameters should be expanded into individual fields instead of one JSON input.

Actual behaviour

Even with the option set, Swagger UI displays the parameters in one JSON textfield.

create resource handler static files too many redirect

http://localhost:3000/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html

end up not serving at all

Optional middleware dependency resolution

Middleware could have keys like :provides and :requires with common pseudo-values like :parsed-body, :catch-exceptions or project-spesific ::authenticated-user etc. Middleware-chain could be automatically constructed based on these or if some dependency is missing, the router can't be constructed (with descriptive message).

Like Anger-interceptor but for Middleware.

Not sure if this is a good feature thou.

If implemented, should work together with Macchiato's middleware resolution.

Interceptors (reitit/http-module)

Like ring-router but with enchanced pedestal-style interceptors. Related to #10. We don't need a interceptor interpreter here, can do that later.

Can't make the Interceptors truely portable without changes to Pedestal (context keys (queue, error etc) keys are namespaced in P.

Current draft:

(require '[reitit.http.coercion :as rhc])
(require '[reitit.http :as http])
(require '[reitit.coercion.spec])
(require '[clojure.set :as set])

(def auth-interceptor
  "Interceptor that mounts itself if route has `:roles` data. Expects `:roles`
  to be a set of keyword and the context to have `[:user :roles]` with user roles.
  responds with HTTP 403 if user doesn't have the roles defined, otherwise no-op."
  {:name ::auth
   :compile (fn [{:keys [roles]} _]
              (if (seq roles)
                {:description (str "requires roles " roles)
                 :spec {:roles #{keyword?}}
                 :context-spec {:user {:roles #{keyword}}}
                 :enter (fn [{{user-roles :roles} :user :as ctx}]
                          (if (not (set/subset? roles user-roles))
                            (assoc ctx :response {:status 403, :body "forbidden"})
                            ctx))}))})(require '[clojure.set :as set])

(def app
  (http/http-handler
    (http/router
      ["/api" {:interceptors [auth-interceptor]}
       ["/ping" {:name ::ping
                 :get (constantly
                        {:status 200
                         :body "pong"})}]
       ["/plus/:z" {:name ::plus
                    :post {:parameters {:query {:x int?}
                                        :body {:y int?}
                                        :path {:z int?}}
                           :responses {200 {:body {:total pos-int?}}}
                           :roles #{:admin}
                           :handler (fn [{:keys [parameters]}]
                                      (let [total (+ (-> parameters :query :x)
                                                     (-> parameters :body :y)
                                                     (-> parameters :path :z))]
                                        {:status 200
                                         :body {:total total}}))}}]]
      {:data {:coercion reitit.coercion.spec/coercion
              :interceptors [rhc/coerce-exceptions-interceptor
                             rhc/coerce-request-interceptor
                             rhc/coerce-response-interceptor]}})))

Allow Middleware + router configuration to be bundled?

Router with coercion enabled needs configuration keys :extract-request-format and :extract-response-format options on where to find the :body content-type to select the coercion. e.g. for "application/json" a json-coercion is used, for "application/edn" just validation is applied.

There should be a some defaults for this. Ideas:

0) no default

  • fail-fast if coercion is enabled and the keys are not set. give descriptive error message how they should be enabled. Could companion 2.
  • GOOD: simple
  • BAD: more code to make minimal working example

1) Muuntaja default

  • default should use Muuntaja keys, as it's the go-to formatter for Ring
  • there could be a helper in muuntaja.core to extract these?
  • GOOD: works
  • BAD: default is lib-spesific
:extract-request-format (comp :format :muuntaja/request)
:extract-response-format (comp :format :muuntaja/response)

2) Auto-configuration

  • let's add a :configure key to Middleware, allowing mounted middleware to modify the configuration for the routes it's attached to.
  • a data-driven version of Muuntaja middleware would have the :configure key and would add those keys automatically.
  • GOOD: super-generic
  • BAD: magical?

Reverse routing params conforming

I'd be useful to have coercion to work with reitit/match-by-name. One example is conforming keyword to a string path parameter.
Related spec-tools issue: metosin/spec-tools#112

(r/match-by-name
  (r/router
    ["/olipa/:kerran" ::avaruus])
  ::avaruus
  {:kerran :joskus})
;#Match{:template "/olipa/:kerran",
;       :data {:name :user/avaruus},
;       :result nil,
;       :path-params {:kerran :joskus},   <-- wrong
;       :path "/olipa/:joskus"}           <-- wrong

Ignoring path conflicts doesn't work as documented

From https://github.com/metosin/reitit/blob/35a08b80f35b9538bd2f4d3ccd1d76e898837371/doc/basics/route_conflicts.md#path-conflicts

$ clj
Clojure 1.9.0
user=> (require '[reitit.core :as r])
nil
(def routes
  [["/ping"]
   ["/:user-id/orders"]
   ["/bulk/:bulk-id"]
   ["/public/*path"]
   ["/:version/status"]])
#'user/routes
user=> (r/router routes)
ExceptionInfo Router contains conflicting routes:

   /:user-id/orders
-> /public/*path
-> /bulk/:bulk-id

   /bulk/:bulk-id
-> /:version/status

   /public/*path
-> /:version/status

  clojure.core/ex-info (core.clj:4739)

(r/router
  routes
  {:conflicts nil})
ExceptionInfo Router contains conflicting routes:

   /:user-id/orders
-> /public/*path
-> /bulk/:bulk-id

   /bulk/:bulk-id
-> /:version/status

   /public/*path
-> /:version/status

  clojure.core/ex-info (core.clj:4739)

It looks like if I instead pass an fn, e.g. {:conflicts (constantly nil)} as options, it works.

reitit version 0.1.3

Map-tree router

If all routes are wildcard-free, router should be just a hash lookup.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.