-
Notifications
You must be signed in to change notification settings - Fork 257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
:interceptors
does not flatten vectors like :middleware
-> cannot succinctly compose chains of interceptors
#707
Comments
A solution?Here's another possible workaround (and potentially a route to fixing this here in reitit proper? 🤞 ) ;; Change how vectors are handled so that:
;; (1) the current fn behavior is preserved in the case that the first element is a function
;; (2) otherwise assume the vector contents are more IntoInterceptors
(extend-protocol reitit.interceptor/IntoInterceptor
clojure.lang.APersistentVector
(into-interceptor [form data opts]
(if (fn? (first form))
(reitit.interceptor/into-interceptor (apply (first form) (rest form)) data opts)
(mapv #(reitit.interceptor/into-interceptor % data opts) form))))
;; However that is not sufficient, because the above impl returns nested vectors
;; We need to flatten them with a transform:
(defn flatten-into-interceptors [interceptors]
(remove nil? (flatten interceptors)))
(reitit.core/compiled-routes
(http/router [""
;; remember, api-chain and auth-chain are vectors of IntoInterceptor
["/" {:interceptors [api-chain auth-chain my-interceptor]
:handler dummy-handler}]
;; and we still support the original implementation of into-interceptor for vector
["/old" {:interceptors [[(fn [arg1 arg2]
{:args [arg1 arg2]
:enter (fn [ctx] ctx)}) :arg1 :arg2]]
:handler dummy-handler}]]
{:reitit.interceptor/registry ...optional registry...
;; REQUIRED: transform nested interceptors into a flat list
:reitit.interceptor/transform flatten-into-interceptors}))
👉 If this approach is suitable, I would be willing to write up an impl, tests, docs, and PR it. Notes on the current implementation of into-interceptor for a vectorreitit/modules/reitit-core/src/reitit/interceptor.cljc Lines 51 to 58 in 0f94148
I think this implementation is actually broken on the edge cases. Obviously the intention was to be able to pass a vector where the head is a fn, and the rest was arguments to that function: (defn make-interceptor [v1 v2]
{:enter (fn [ctx]
(assoc ctx :my/args [v1 v2]))})
["/" {:interceptors [[make-interceptor 1 2]]}] However the ["/" {:interceptors [[:wut]]}] You are greeted with the runtime exception ..and if you do the somewhat plausible action of passing a keyword as the fn with a map to yoink from: ["/" {:interceptors [[:wut {:wut {:enter (fn [ctx] .....)}}]]}] Then you get the builtin fail! from that impl: In any case, IMHO, there is plenty of room here to introduce new semantics for the vector variant of IntoInterceptor while maintaining backwards compatibility with the expectation to support |
Given:
Result:
Expected result:
I expect the resulting
interceptors
chain for"/"
to be:Extra
@ikitommi indicated that this should work:
(slack link)
However, looking at the implementation, it cannot work, because the
:interceptors
vector in route data is processed one element at a time:reitit/modules/reitit-core/src/reitit/interceptor.cljc
Lines 103 to 116 in 0f94148
So it is not possible for the
into-interceptor
implementation forclojure.lang.APersistentVector
to flatten the vector, instead it assumes you're doing a delayed function call:[some-interceptor-ctor :a-param :another-param]
->(some-interceptor-ctor :a-param :another-param)
(I'm curious what the usecase for that is btw?)Workarounds
We would like this feature, as defining a stack, or chain, of interceptors that can be re-used and composed is very useful.
Unfortunately there aren't any easy workarounds.
:reitit.interceptor/transform
i.e.,(http/router ["/" {:interceptors [api-chain my-interceptor]}] {:reitit.interceptor/transform custom-transform)
Not possible. Because
into-interceptor
is run on the interceptors vector once before the transformation (see(defn chain..
) above).:compile
i.e.,(http/router ["/" {:interceptors [api-chain my-interceptor]}] {:compile custom-compile-fn)
This is technically possible, but requires redefining:
reitit.http/compile-result
reitit.interceptor/compile-result
reitit.interceptor/chain
...oof 😨
One simple workaround of course is to:
Or for composing multiple chains (also very common):
For experienced clojure developers the above two are of course perfectly serviceable, but a far cry from the readability of the following. When teaching new clojure devs about web dev, one of the first files they see is the routes file and the low-level noise of
concat
is distracting from higher level concepts.Ideally we could write:
While I'm wishing, it would be even nicer if the
api-chain
(aka vectors of IntoInterceptors) could be put in the registry, allowing:And I am pretty sure this would work out-of-the-box, if one IntoInterceptor could expand into multiple IntoInterceptors. Because registry is already defined as a map of keyword => IntoInterceptor.
The text was updated successfully, but these errors were encountered: