Skip to content

Commit

Permalink
Automatically add refs and handlers to routes
Browse files Browse the repository at this point in the history
  • Loading branch information
weavejester committed Nov 20, 2024
1 parent f95fa73 commit e4cd6a9
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 6 deletions.
57 changes: 51 additions & 6 deletions src/duct/module/web.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,58 @@
(defn- html-response [html]
{:headers {"Content-Type" "text/html; charset=UTF-8"}, :body html})

(defn- walk-route-data [route f]
(if (and (vector? route) (seq route))
(if (vector? (first route))
(mapv #(walk-route-data % f) route)
(let [[path data] route]
[path (cond
(vector? data) (walk-route-data data f)
(record? data) data
(map? data) (f data)
(keyword? data) (f {:name data})
:else data)]))
route))

(defn- route-data-seq [route]
(when (vector? route)
(if (vector? (first route))
(mapcat route-data-seq route)
(let [[_ data] route]
(cond
(vector? data) (route-data-seq data)
(record? data) nil
(map? data) (list data)
(keyword? data) (list {:name data}))))))

(def ^:private handler-keys
#{:handler :get :head :patch :delete :options :post :put :trace})

(defn- add-ref-to-key [m k]
(if (qualified-keyword? (m k)) (update m k ig/ref) m))

(defn- add-refs-to-route-data [route-data]
(if (some route-data handler-keys)
(reduce add-ref-to-key route-data handler-keys)
(assoc route-data :handler (ig/ref (:name route-data)))))

(defn- find-handlers-in-route-data [route-data]
(->> handler-keys (keep route-data) (filter ig/ref?) (map :key)))

(defmethod ig/expand-key :duct.module/web
[_ {:keys [features routes]}]
[_ {:keys [features routes handler-opts]
:or {routes [], handler-opts {}}}]
(let [featureset (set features)
api? (featureset :api)
site? (featureset :site)]
site? (featureset :site)
routes (walk-route-data routes add-refs-to-route-data)]
`{:duct.server.http/jetty
{:port ~(ig/var 'port)
:logger ~(ig/refset :duct/logger)
:handler ~(ig/ref :duct/router)}

:duct.router/reitit
{:routes ~(or routes [])
{:routes ~routes
~@(when api? [:muuntaja {}]) ~@[]
:middleware
[~@(when site? [(ig/ref :duct.middleware.web/webjars)])
Expand All @@ -30,9 +70,9 @@
:repl (ig/ref :duct.middleware.web/stacktrace)
:main (ig/ref :duct.middleware.web/hide-errors))]
:default-handler
{:not-found ~(ig/ref :duct.handler.static/not-found)
{:not-found ~(ig/ref :duct.handler.static/not-found)
:method-not-allowed ~(ig/ref :duct.handler.static/method-not-allowed)
:not-acceptable ~(ig/ref :duct.handler.static/not-acceptable)}}
:not-acceptable ~(ig/ref :duct.handler.static/not-acceptable)}}

:duct.middleware.web/defaults
~(if site?
Expand Down Expand Up @@ -60,9 +100,14 @@
:duct.middleware.web/log-errors {:logger ~(ig/ref :duct/logger)}
:duct.middleware.web/stacktrace {}

~@(when api? [:duct.middleware.web/format {}]) ~@[]
~@(when api? [:duct.middleware.web/format {}]) ~@[]
~@(when site? [:duct.middleware.web/webjars {}]) ~@[]

~@(->> (route-data-seq routes)
(mapcat find-handlers-in-route-data)
(mapcat (fn [key] [key handler-opts])))
~@[]

:duct.middleware.web/hide-errors
{:error-handler ~(ig/ref :duct.handler.static/internal-server-error)}

Expand Down
59 changes: 59 additions & 0 deletions test/duct/module/web_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,62 @@
:duct.middleware.web/log-errors {:logger (ig/ref :duct/logger)}}
(ig/expand {:duct.module/web {:features #{:site}}}
(ig/deprofile [:main])))))

(deftest routes-transform-test
(is (= {:duct.router/reitit
{:routes
[["/one" {:get (ig/ref ::handler1)}]
["/two" {:name ::handler2, :handler (ig/ref ::handler2)}]
["/three/" ["four" {:handler (ig/ref ::handler3)}]]]
:middleware
[(ig/ref :duct.middleware.web/defaults)
(ig/ref :duct.middleware.web/log-requests)
(ig/ref :duct.middleware.web/log-errors)
(ig/ref :duct.middleware.web/hide-errors)]
:default-handler
{:not-found
(ig/ref :duct.handler.static/not-found)
:method-not-allowed
(ig/ref :duct.handler.static/method-not-allowed)
:not-acceptable
(ig/ref :duct.handler.static/not-acceptable)}}
:duct.middleware.web/defaults
{:params {:urlencoded true, :keywordize true}
:responses {:not-modified-responses true
:absolute-redirects true
:content-types true
:default-charset "utf-8"}}
:duct.server.http/jetty
{:port (ig/var 'port)
:handler (ig/ref :duct/router)
:logger (ig/refset :duct/logger)}
::handler1 {:name :foo}
::handler2 {:name :foo}
::handler3 {:name :foo}
:duct.handler.static/bad-request
{:headers {"Content-Type" "text/plain; charset=UTF-8"}
:body "Bad Request"}
:duct.handler.static/not-found
{:headers {"Content-Type" "text/plain; charset=UTF-8"}
:body "Not Found"}
:duct.handler.static/method-not-allowed
{:headers {"Content-Type" "text/plain; charset=UTF-8"}
:body "Method Not Allowed"}
:duct.handler.static/not-acceptable
{:headers {"Content-Type" "text/plain; charset=UTF-8"}
:body "Not Acceptable"}
:duct.handler.static/internal-server-error
{:headers {"Content-Type" "text/plain; charset=UTF-8"}
:body "Internal Server Error"}
:duct.middleware.web/stacktrace {}
:duct.middleware.web/hide-errors
{:error-handler (ig/ref :duct.handler.static/internal-server-error)}
:duct.middleware.web/log-requests {:logger (ig/ref :duct/logger)}
:duct.middleware.web/log-errors {:logger (ig/ref :duct/logger)}}
(ig/expand {:duct.module/web
{:handler-opts {:name :foo}
:routes
[["/one" {:get ::handler1}]
["/two" ::handler2]
["/three/" ["four" {:handler ::handler3}]]]}}
(ig/deprofile [:main])))))

0 comments on commit e4cd6a9

Please sign in to comment.