Fork me on GitHub
#reitit
<
2019-08-27
>
kenny00:08:01

Is there a way to customize what reitit does with certain handler return values? For example, if I return an Anomaly map, I want reitit to convert it to an HTTP status code and return the data in the body.

danielcompton03:08:15

Is there any way to serve multiple vhosts out of one route tree in Reitit? Or would I need to add my own wrapping function to dispatch on :host?

ikitommi09:08:37

@kenny you can use the interceptor context debugger to see how the context changes, info here https://cljdoc.org/d/metosin/reitit/0.3.9/doc/http/transforming-interceptor-chain#printing-context-diffs

martinklepsch09:08:23

Wow, that’s pretty cool!

partywombat 4
ikitommi09:08:23

the transformation of return values… that should happen at request processing time, best done in an interceptor. If only your handler can return that, just put an custom interceptor to the last in the stack

ikitommi09:08:26

if that can be returned from any of the interceptors, you can either: interleave a custom Anomaly->Context transforming interceptor between each other interceptor or you could extend the AsyncContext protocol in Sieppari to make the interceptor engine do it for you. There are examples for the latter in the Sieppari code base, where core.async, manifold & promesa mappings are done.

ikitommi09:08:47

how to add an interceptor to last in each chain? use an interceptor chain transfomer, they compose too 🙂

ikitommi09:08:20

that happens at router creation time, so kinda fast too.

ikitommi09:08:24

@danielcompton sorry, haven’t done that, so not supported out-of-the-box. Today, you need to wrap the top-level handler into something that routes based on the :host.

ikitommi09:08:01

If that is important, there could be support to plug that into reitit. tested similar already with pohjavirta async dispatch: a custom top-level wrapper that pull the route data from underlaying router and re-creates multiple routers based on some data. In my test there was :server/pool route key to mark in which pool the routes are applied. Can run some routes in a NIO pool, some in the default worker, some in custom bounded pool.

ikitommi09:08:46

something like that could work with the :host; someone should just finalize the extension thingie first 🙂

kenny15:08:06

Is the recommended testing method to simply call your ring handler with a "partial" request map? e.g.

(app {:request-method :get, :uri "/api/admin/users"})
In the past I've had to use request mocking libraries that fill in a bunch of extra info.

ikitommi16:08:30

@kenny we have been mosly using just literal maps without any extra libs, makes it explicit what the request looks like. But whatever works for you. If you async things, then you need to deref the response. Test are documenting ways to do that

4
kenny18:08:39

If I assoc a :response onto the ctx within an interceptor, the chain continues to execute. This is not ideal for an interceptor that does authentication and needs the chain to halt when a request is not authenticated. I have seen https://github.com/metosin/reitit/issues/64 but it appears like it will exhibit the same problem I am running into -- the auth-interceptor will still call the :handler even though it assoc's a :response onto the ctx when roles is not a subset of user-roles.

kenny18:08:07

Here's an example. If I call app, I will get the response from my handler, not my interceptor:

(app {:request-method :get
      :uri            "/ping"})
"auth enter"
"ping"
=> {:status 200, :body "pong2", :headers {}}

ikitommi18:08:28

@kenny you need to empty the :queue to terminate. E.g. (assoc ctx :queue nil). Sorry for the shitty docs on interceptors.

kenny18:08:32

This worked. Thanks

kenny18:08:38

Won't this remove all other :leave interceptors though?

ikitommi18:08:12

no, they are pushed to :stack once executed. When queue is empty the stack will be run (leaves or errors)

kenny18:08:12

It looks like it will. This means that all the coercion stuff won't work.

ikitommi18:08:53

hmm. should not

kenny18:08:25

Hmm, I'm getting this

java.lang.IllegalArgumentException: No implementation of method: :write-body-to-stream of protocol: #'ring.core.protocols/StreamableResponseBody found for class: clojure.lang.PersistentArrayMap
Once I set the :queue to nil. I've seen this before when either a content-type is set to text/plain or the coercion interceptor isn't there.

kenny18:08:46

After doing this in my interceptor:

(assoc ctx
  :response {:status  403
             :body    {:error "forbidden"}}
  ;; empty the interceptor queue to stop processing
  :queue nil)

ikitommi18:08:23

you should have the response formatter before that in the chain

ikitommi18:08:10

Look at the http-swagger example app with the context diffing on. It has a "good chain in order".

ikitommi18:08:20

Sadly, the order matters here

kenny18:08:25

Ah, right. That did it.

kenny18:08:59

So really all my custom interceptors should go below those?

ikitommi18:08:43

depending on what they do. If they need response formatting, they should be after the formatter

ikitommi18:08:34

Exception handler should be just after response formatter so it captures all things, but gets formatting itself.

4
ikitommi18:08:09

Sieppari will get a 1.0.0 release, hopefully soon. With docs on par rest of the reitit.

4
kenny19:08:44

I am getting a StackOverflowError when using a spec defined in edn-query-language (https://github.com/edn-query-language/eql/blob/281553d5cd3666e547b2570b9eae32f2f22152b6/src/edn_query_language/core.cljc#L204). My route has the :parameters set to {:body ::eql-body-params}. Where ::eql-body-params is defined as:

(s/def ::q ::eql/query)
(s/def ::eql-body-params
  (s/keys :req-un [::q]))
Guessing there is some sort of bug or incompatibility of spec-tools with certain specs.

ikitommi14:08:41

spec-tools is external to spec itself, and uses best-effort parsing of specs to figure out what to do. please add an issue to spec-tools if you can isolate a small/minimum thing that fails.

kenny14:08:36

Yeah I'm starting to realize that. I'll try to make a min repro. Really wish spec2 was out because this sort of stuff is super valuable for us.

ikitommi14:08:48

proposed a patch to spec some years ago to add support for this, no luck. there is an open issue about a generic walker, hopefully pops up some day. Would be happy to get rid of most of the archeology from spec-tools.

kenny14:08:35

I've seen that one. Isn't spec2 supposed to have some sort of ast to better support these use cases?

kenny21:08:12

How do you enable CORS?

danielcompton21:08:42

@kenny could you use a standard Ring middleware for CORS?

kenny21:08:31

Perhaps. Trying to follow along here https://github.com/metosin/reitit/issues/143. It's not clear how that plays with interceptors though.

kenny22:08:26

Wrote this interceptor. @ikitommi Any interest in a PR to include this in reitit-interceptors?