Fork me on GitHub
#reitit
<
2019-01-30
>
kanwei05:01:26

ok using data-spec now and it's working

kanwei17:01:18

home stretch - I now combine routers for performance, but now it's mixing all the swagger routes together

kanwei17:01:44

so I have ["/v1/" blah] and ["/v2/" blah] and I want to generate different swagger.json's

ikitommi17:01:53

you can scope the swagger docs.

ikitommi17:01:10

just a sec.

kanwei17:01:31

pretty excited to get this live

ikitommi17:01:35

so, you can add a :id to swagger-apis, one endpoint can belong to multiple apis too. Cool to get a real-life use case for this, just added it because it was easy to do it.

ikitommi17:01:48

[["/v1"
  {:swagger {:id ::v1}}
  v1-routes common-routes
  ["/swagger.json" ...]
  ["/swagger-ui/*" ...]]

 ["/v2"
  {:swagger {:id ::v2}}
  v2-routes common-routes
  ["/swagger.json" ...]
  ["/swagger-ui/*" ...]]]

kanwei18:01:29

is there a way for a child to dissoc/ignore middleware a parent sets?

ikitommi19:01:33

@kanwei use meta-merge meta-data like`:replace` or :displace on the middleware vector to ignore whatever the parents have set. Here are some pointers for it: https://metosin.github.io/reitit/basics/route_data.html#nested-route-data

ikitommi19:01:02

also, the middleware can decide not to mount on certain endpoints, using their :compile lifecycle functions.

ikitommi19:01:08

for example, one can safely add coercion etc. middleware to the root, it will only attach itself to routes that have :coercion and :parameters or :responses defined.

kanwei19:01:51

cool i'll check it out

kanwei19:01:06

also I'm still a bit confused about the vector syntax

kanwei19:01:16

can I refactor this?

ikitommi19:01:47

we lean on`:compile` on projects a lot, one can add data like :roles #{:admin} to route data and have a middleware mount just to those endpoints.

kanwei19:01:10

(def v2-routes
  (reitit/router
    [["/api/v2"
      {:swagger    {:id ::v2}
       :middleware [wrap-api-account
                    wrap-api-validate]}
      private-search-routes]
     ["/api/v2"
      {:swagger {:id ::v2}}
      public-search-routes]
     ["/api/v2/swagger.json"
      {:get {:no-doc  true
             :swagger {:id   ::v2
                       :info {:version     "2.0"
                              :title       "X"
                              :description "X"
                              :contact     {:name  "X"
                                            :email "x"}}}
             :handler (swagger/create-swagger-handler)
             }}]]
    {:data {:coercion   reitit.coercion.spec/coercion
            :middleware [coercion/coerce-response-middleware
                         coercion/coerce-request-middleware
                         ]}}))

kanwei19:01:28

so I'm trying to put some middleware on some routes

kanwei19:01:01

can I rewrite this so that I only have to use :swagger {:id ::v2} once instead of 3x

kanwei19:01:19

the vector syntax still trips me up because I'm not sure how the options maps are applied

ikitommi19:01:58

in the example, you have 3 separate routes. each has it’s own route data.

ikitommi19:01:17

you can nest the path fragments, and the data can be shared. so:

ikitommi19:01:12

[["/api/v2" {:middleware [wrap-x]}]
 ["/api/v2/swagger.json" {:middleware [wrap-x]}]
 ["/api/v2/ping" {:middleware [wrap-x]}]]

ikitommi19:01:18

can be written as:

ikitommi19:01:25

[["/api/v2"
  {:middleware [wrap-x]}
  ["/swagger.json"]
  ["/ping"]]]

ikitommi19:01:58

here the two routes are under the top-level route fragment and the route data gets merged from there (root->leafs)

ikitommi19:01:11

you can think it like hiccup & html.

kanwei19:01:38

what would this do?

kanwei19:01:43

[["/api/v2"
  {:middleware [wrap-x]}
  ["/swagger.json"]
  {:middleware [wrap-y]}
  ["/ping"]]]

kanwei19:01:21

does a map only work in the first position after the path?

ikitommi19:01:45

like attributes for html elements

ikitommi19:01:49

(ns user
  (:require [reitit.core :as r]))

(-> (r/router
      ["/api"
       {:middleware [:api]}
       ["/v2"
        {:middleware [:v2]}
        ["/ping"]
        ["/pong"]]])
    (r/routes))
;[["/api/v2/ping" {:middleware [:api :v2]}]
; ["/api/v2/pong" {:middleware [:api :v2]}]]

ikitommi19:01:19

internally, the routes are flattented and the data gets merged using the meta-merge rules

ikitommi19:01:07

(-> (r/router
      ["/api"
       {:middleware [:api]}
       ["/v2"
        {:middleware [:v2]}
        ["/ping"]
        ["/pong"
         {:middleware ^:replace [:pong]}]]])
    (r/routes))
;[["/api/v2/ping" {:middleware [:api :v2]}]
; ["/api/v2/pong" {:middleware [:pong]}]]

kanwei19:01:08

ok that makes more sense

ikitommi19:01:35

we are about to do a swagger-like tool, but for reitit, with visualization of the merged route structure to help development

👍 5
ikitommi19:01:14

or more like re-frame-10x

kanwei19:01:10

so one of my routers has a {:conflicts nil} defined

kanwei19:01:35

in my top function I merge routes with merge-routers like the example says

kanwei19:01:46

but I had to override the merge with {:conflicts nil}

kanwei19:01:48

(defn merge-routers [& routers]
  (reitit-core/router
    (apply merge (map reitit-core/routes routers))
    (assoc (apply merge (map reitit-core/options routers))
      :conflicts nil)))

kanwei19:01:50

is that expected?

kanwei19:01:22

(reitit/ring-handler
                (merge-routers controller/main-website-routes ; <---- has conflicts
                               register-routes <---- no conflicts
                               ...))

kanwei19:01:41

I would have assumed that you could just do a normal merge of a quarantine router (controller/main-website-routes) and a regular router without having to do another {:conflicts nil}

kanwei19:01:55

hope that made sense

ikitommi19:01:21

merge works from left to right, the options are just maps, and the last one wins.

ikitommi19:01:44

the defult value is reitit.core/throw-on-conflicts!.

ikitommi19:01:17

btw, the route fragment can be empty, too. this allows route-data to be attached without adding anything to the path. like :<> in reagent. This is a big win over compojure & others.

ikitommi19:01:23

(-> (r/router
      ["" {:no-doc true}
       ["/ping"]
       ["/pong"]])
    (r/routes))
;[["/ping" {:no-doc true}]
; ["/pong" {:no-doc true}]]

kanwei20:01:21

[["/api/v2"
      {:swagger {:id ::v2}}
      [private-search-routes {:middleware [wrap-api-account
                                           wrap-api-validate]}]
      [public-search-routes]]]

kanwei20:01:46

the private-search-routes isn't getting middleware wrapped

kanwei20:01:56

any way to make it work like this, or do I have to put it in another ["/api/v2"] sibling?

ikitommi21:01:25

the private-search-routes is most likely a vector, the data can only be after a path string. the empty fragment helps here:

ikitommi21:01:28

[["/api/v2"
  {:swagger {:id ::v2}}
  ["" {:middleware [wrap-api-account wrap-api-validate]}
   private-search-routes]
  public-search-routes]]

ikitommi21:01:43

or if that’s a common pattern, you can refactor that out into a function:

ikitommi21:01:00

(defn wrap-api [routes]
  ["" {:middleware [wrap-api-account wrap-api-validate]}
   routes])

[["/api/v2"
  {:swagger {:id ::v2}}
  (wrap-api private-search-routes)
  public-search-routes]]

kanwei21:01:54

yeah that makes sense

kanwei21:01:06

i have to really just think about it as a huge tree

👍 5