-
Notifications
You must be signed in to change notification settings - Fork 250
/
middleware.cljc
151 lines (129 loc) · 4.98 KB
/
middleware.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
(ns reitit.middleware
(:require [clojure.pprint :as pprint]
[reitit.core :as r]
[reitit.exception :as exception]
[reitit.impl :as impl]))
(defprotocol IntoMiddleware
(into-middleware [this data opts]))
(defrecord Middleware [name wrap spec])
(defrecord Endpoint [data handler middleware])
(def ^:dynamic *max-compile-depth* 10)
(extend-protocol IntoMiddleware
#?(:clj clojure.lang.Keyword
:cljs cljs.core.Keyword)
(into-middleware [this data {::keys [registry] :as opts}]
(if-let [middleware (if registry (registry this))]
(into-middleware middleware data opts)
(throw
(ex-info
(str
"Middleware " this " not found in registry.\n\n"
(if (seq registry)
(str
"Available middleware in registry:\n"
(with-out-str
(pprint/print-table [:id :description] (for [[k v] registry] {:id k :description v}))))
"see [reitit.middleware/router] on how to add middleware to the registry.\n") "\n")
{:id this
:registry registry}))))
#?(:clj clojure.lang.APersistentVector
:cljs cljs.core.PersistentVector)
(into-middleware [[f & args] data opts]
(if-let [{:keys [wrap] :as mw} (into-middleware f data opts)]
(assoc mw :wrap #(apply wrap % args))))
#?(:clj clojure.lang.Fn
:cljs function)
(into-middleware [this _ _]
(map->Middleware
{:wrap this}))
#?(:clj clojure.lang.PersistentArrayMap
:cljs cljs.core.PersistentArrayMap)
(into-middleware [this data opts]
(into-middleware (map->Middleware this) data opts))
#?(:clj clojure.lang.PersistentHashMap
:cljs cljs.core.PersistentHashMap)
(into-middleware [this data opts]
(into-middleware (map->Middleware this) data opts))
Middleware
(into-middleware [{:keys [compile] :as this} data opts]
(if-not compile
this
(let [compiled (::compiled opts 0)
opts (assoc opts ::compiled (inc ^long compiled))]
(when (>= ^long compiled ^long *max-compile-depth*)
(exception/fail!
(str "Too deep Middleware compilation - " compiled)
{:this this, :data data, :opts opts}))
(if-let [middeware (into-middleware (compile data opts) data opts)]
(map->Middleware
(merge
(dissoc this :compile)
(impl/strip-nils middeware)))))))
nil
(into-middleware [_ _ _]))
(defn- ensure-handler! [path data scope]
(when-not (:handler data)
(exception/fail!
(str "path \"" path "\" doesn't have a :handler defined"
(if scope (str " for " scope)))
(merge {:path path, :data data}
(if scope {:scope scope})))))
(defn- expand-and-transform
[middleware data {::keys [transform] :or {transform identity} :as opts}]
(let [transform (if (vector? transform) (apply comp (reverse transform)) transform)]
(->> middleware
(keep #(into-middleware % data opts))
(into [])
(transform)
(keep #(into-middleware % data opts))
(into []))))
(defn- compile-handler [middleware handler]
((apply comp identity (keep :wrap middleware)) handler))
;;
;; public api
;;
(defn chain
"Creates a Ring middleware chain out of sequence of IntoMiddleware
and Handler. Optional takes route data and (Router) opts."
([middleware handler]
(chain middleware handler nil))
([middleware handler data]
(chain middleware handler data nil))
([middleware handler data opts]
(compile-handler (expand-and-transform middleware data opts) handler)))
(defn compile-result
([route opts]
(compile-result route opts nil))
([[path {:keys [middleware handler] :as data}] opts scope]
(ensure-handler! path data scope)
(let [middleware (expand-and-transform middleware data opts)]
(map->Endpoint
{:handler (compile-handler middleware handler)
:middleware middleware
:data data}))))
(defn router
"Creates a [[reitit.core/Router]] from raw route data and optionally an options map with
support for Middleware. See documentation on [[reitit.core/router]] for available options.
In addition, the following options are available:
| key | description
| -------------------------------|-------------
| `:reitit.middleware/transform` | Function or vector of functions of type `[Middleware] => [Middleware]` to transform the expanded Middleware (default: identity)
| `:reitit.middleware/registry` | Map of `keyword => IntoMiddleware` to replace keyword references into Middleware
Example:
(router
[\"/api\" {:middleware [wrap-format wrap-oauth2]}
[\"/users\" {:middleware [wrap-delete]
:handler get-user}]])"
([data]
(router data nil))
([data opts]
(let [opts (impl/meta-merge {:compile compile-result} opts opts)]
(r/router data opts))))
(defn middleware-handler [router]
(with-meta
(fn [path]
(some->> path
(r/match-by-path router)
:result
:handler))
{::router router}))