diff --git a/src/duct/reitit.clj b/src/duct/reitit.clj index 6d5111b..9a23680 100644 --- a/src/duct/reitit.clj +++ b/src/duct/reitit.clj @@ -1,9 +1,9 @@ (ns duct.reitit (:require [duct.core :as core :refer [merge-configs]] - [duct.core.merge :as m] [duct.reitit.handler] [duct.reitit.util :as util :refer [get-namespaces resolve-registry with-registry spy]] - [integrant.core :refer [init-key] :as ig])) + [integrant.core :refer [init-key] :as ig] + [duct.logger :as logger])) (def ^:private base-config {:duct.core/handler-ns 'handler @@ -29,30 +29,36 @@ :requests? true :pretty? false}}}) -(defn- merge-to-options [configs] +(defn- merge-to-options [config] (reduce-kv (fn [acc k v] (if (= "duct.reitit" (namespace k)) (assoc-in acc [::options (keyword (name k))] v) (assoc acc k v))) - {} configs)) + {} config)) + +(defmethod init-key ::log [_ {{:keys [enable logger pretty? exceptions? coercions?]} :logging}] + (when (and enable (or exceptions? coercions?)) + (if (and logger (not pretty?)) + (fn [level message] + (logger/log logger level message)) + println))) (defmethod init-key :duct.module/reitit [_ _] (fn [{:duct.reitit/keys [registry routes] - :duct.core/keys [environment] :as config}] + :duct.core/keys [environment] :as user-config}] (let [env-config (or (configs environment) {}) - config (merge-to-options (merge-configs base-config env-config config)) + config (merge-to-options (merge-configs base-config env-config user-config)) namespaces (get-namespaces config) registry (resolve-registry namespaces registry) - regrefs (reduce-kv (fn [m k v] (assoc m k (ig/ref (first v)))) {} registry) merge (partial with-registry config registry)] (merge - {::logging (ig/ref ::options) - ::middleware {:options (ig/ref ::options) :logging (ig/ref ::logging)} - ::registry regrefs - :duct.handler/root {:options (ig/ref ::options) :router (ig/ref :duct.router/reitit)} - :duct.router/reitit {:routes routes - :middleware (ig/ref ::middleware) - :registry (ig/ref ::registry) - :options (ig/ref ::options) - :namespaces namespaces}})))) + {::registry (reduce-kv (fn [m k v] (assoc m k (ig/ref (first v)))) {} registry) + ::log (ig/ref ::options) + :duct.handler/root {:options (ig/ref ::options) + :router (ig/ref :duct.router/reitit)} + :duct.router/reitit {:routes routes + :log (ig/ref ::log) + :registry (ig/ref ::registry) + :options (ig/ref ::options) + :namespaces namespaces}})))) diff --git a/src/duct/reitit/middleware.clj b/src/duct/reitit/middleware.clj index a41bf0c..1cb37e2 100644 --- a/src/duct/reitit/middleware.clj +++ b/src/duct/reitit/middleware.clj @@ -1,7 +1,6 @@ (ns duct.reitit.middleware "Construct Ring-Reitit Global Middleware" - (:require [integrant.core :refer [init-key]] - [duct.reitit.util :refer [compact defm spy]] + (:require [duct.reitit.util :refer [compact defm spy]] [reitit.ring.middleware.muuntaja :refer [format-middleware]] [reitit.ring.middleware.parameters :refer [parameters-middleware]] [duct.reitit.middleware.exception :as exception] @@ -17,23 +16,19 @@ (defn- get-format-middleware [muuntaja] (when muuntaja format-middleware)) -(defn- create-middleware [extras] - (fn [& defaults] - (->> (conj extras defaults) - (apply concat) - (vec) (compact)))) +(defn- merge-middlewares [{:keys [middleware] :as options}] + (let [coercion-middlewares (coercion/get-middleware options)] + (fn [& defaults] + (-> (concat defaults coercion-middlewares middleware) + (vec) + (compact))))) -(defmethod init-key :duct.reitit/middleware - [_ {:keys [logging] {:keys [muuntaja middleware coercion exception]} :options}] - (let [{:keys [coerce-response coerce-request coerce-exceptions]} (coercion/get-middleware coercion logging) - format-middleware (get-format-middleware muuntaja) - exception-middleware (exception/get-middleware logging coercion exception) - create-middleware (create-middleware middleware)] - (create-middleware parameters-middleware +(defn create-router-middleware [{:keys [muuntaja] :as options}] + (let [format-middleware (get-format-middleware muuntaja) + exception-middleware (exception/get-middleware options) + merge-middlewares (merge-middlewares options)] + (merge-middlewares parameters-middleware environment-middleware format-middleware - exception-middleware - coerce-exceptions - coerce-request - coerce-response))) + exception-middleware))) diff --git a/src/duct/reitit/middleware/coercion.clj b/src/duct/reitit/middleware/coercion.clj index cb34b13..733dbdf 100644 --- a/src/duct/reitit/middleware/coercion.clj +++ b/src/duct/reitit/middleware/coercion.clj @@ -9,20 +9,18 @@ (fn [exception request] (handler exception request)))) - ;; should be nil when 1. logging is enabled and 2. customized message) -(defn- get-coercion-exception [should-use] - (when-not should-use rcc/coerce-exceptions-middleware)) +(defn get-exception-handler + [{:keys [with-formatted-message?] :as _coercion}] + (when with-formatted-message? + {:reitit.coercion/request-coercion (get-coercion-exception-handler 400) + :reitit.coercion/response-coercion (get-coercion-exception-handler 500)})) (defn get-middleware - [{:keys [enable with-formatted-message?]} - {:keys [coercions? exceptions?]}] + [{{:keys [enable with-formatted-message?]} :coercion + {:keys [coercions? exceptions?]} :logging}] (when enable (let [with-exception (or coercions? exceptions? with-formatted-message?)] - {:coerce-exceptions (get-coercion-exception with-exception) - :coerce-request rcc/coerce-request-middleware - :coerce-response rcc/coerce-response-middleware}))) + [(when-not with-exception rcc/coerce-exceptions-middleware) + rcc/coerce-request-middleware + rcc/coerce-response-middleware]))) -(defn get-exception-handler [{:keys [with-formatted-message?] :as _config} enabled?] - (when (or enabled? with-formatted-message?) - {:reitit.coercion/request-coercion (get-coercion-exception-handler 400) - :reitit.coercion/response-coercion (get-coercion-exception-handler 500)})) diff --git a/src/duct/reitit/middleware/exception.clj b/src/duct/reitit/middleware/exception.clj index 781d234..2700362 100644 --- a/src/duct/reitit/middleware/exception.clj +++ b/src/duct/reitit/middleware/exception.clj @@ -1,18 +1,24 @@ (ns duct.reitit.middleware.exception (:require [reitit.ring.middleware.exception :as exception :refer [create-exception-middleware default-handlers]] - [duct.reitit.middleware.coercion :as coercion])) + [duct.reitit.middleware.coercion :as coercion] + [duct.reitit.middleware.format :refer [ex-format]] + [duct.reitit.util :refer [spy]])) -(defn- create-middleware [& handlers] - (->> (cons default-handlers handlers) - (apply merge) - (create-exception-middleware))) +(defn ^:private get-exception-wrapper [log config] + (let [config (merge config {:with-req-info? true})] + (fn [handler exception request] + (log :error (ex-format exception request config)) + (handler exception request)))) (defn get-middleware "Create custom exception middleware." - [{:keys [enable ex-logger coercions? exceptions?]} coercion exception] - (let [should-wrap (or (and enable coercions?) (and enable ex-logger exceptions?)) - coercion-handlers (coercion/get-exception-handler coercion coercions?) - exception-wrapper (when should-wrap {::exception/wrap ex-logger})] - (create-middleware exception-wrapper - coercion-handlers - exception))) + [{:keys [coercion exception log logging]}] + (let [{:keys [enable coercions? exceptions?]} logging + should-wrap (or (and enable coercions?) (and enable exceptions?)) + coercion-handlers (when coercions? (coercion/get-exception-handler coercion)) + exception-wrapper (when should-wrap {::exception/wrap (get-exception-wrapper log logging)}) + create-middleware #(create-exception-middleware (apply merge default-handlers %))] + (create-middleware + [exception-wrapper + coercion-handlers + exception]))) diff --git a/src/duct/reitit/middleware/logging.clj b/src/duct/reitit/middleware/logging.clj index b66c36a..4766cf5 100644 --- a/src/duct/reitit/middleware/logging.clj +++ b/src/duct/reitit/middleware/logging.clj @@ -3,7 +3,7 @@ [duct.reitit.middleware.format :refer [ex-format]] [duct.logger :as logger])) -(defmethod init-key :duct.reitit/logging +(defmethod init-key :ffduct.reitit/logging [_ {{:keys [enable logger pretty? exceptions? coercions?] :as config} :logging}] (when (and enable (or exceptions? coercions?)) (let [enabled {:coercions? coercions? :exceptions? exceptions?} diff --git a/src/duct/reitit/router.clj b/src/duct/reitit/router.clj index 5695ee6..719906c 100644 --- a/src/duct/reitit/router.clj +++ b/src/duct/reitit/router.clj @@ -1,14 +1,14 @@ (ns duct.reitit.router (:require [clojure.walk :refer [postwalk]] [duct.logger :as logger] - [duct.reitit.util :as util :refer [compact member? resolve-key]] + [duct.reitit.util :as util :refer [compact member? resolve-key spy]] [integrant.core :as ig :refer [init-key]] [muuntaja.core :refer [instance] :rename {instance muuntaja-instance}] - #_[reitit.coercion :refer [compile-request-coercers]] [reitit.coercion.malli :as malli] [reitit.coercion.schema :as schema] [reitit.coercion.spec :as spec] - [reitit.ring :as ring])) + [reitit.ring :as ring] + [duct.reitit.middleware :refer [create-router-middleware]])) (def ^:private coercion-index {:malli malli/coercion :spec spec/coercion :schema schema/coercion}) @@ -17,20 +17,20 @@ (let [resolve (partial resolve-key namespaces) member? (partial member? (keys registry)) valid? (fn [x] (and (keyword? x) (member? x)))] - (fn [x] - (cond (symbol? x) (or (resolve x) x) - (valid? x) (get registry x) - :else x)))) + (fn [x] (cond (symbol? x) (or (resolve x) x) + (valid? x) (get registry x) + :else x)))) ; :compile coercion/compile-request-coercers? -(defn process-options [{:keys [muuntaja environment coercion]}] +(defn process-options [{:keys [muuntaja environment coercion] :as options}] {:data (compact {:environment environment :muuntaja (cond (boolean? muuntaja) muuntaja-instance (nil? muuntaja) nil :else muuntaja) - :coercion (some-> coercion :coercer keyword coercion-index)})}) + :coercion (some-> coercion :coercer keyword coercion-index) + :middleware (create-router-middleware options)})}) -(defmethod init-key :duct.router/reitit [_ {:keys [logger registry middleware routes namespaces options]}] - (when logger (logger/log logger :report ::init)) - (ring/router - (postwalk (get-resolver registry namespaces) routes) - (assoc-in (process-options options) [:data :middleware] middleware))) +(defmethod init-key :duct.router/reitit + [_ {:keys [registry routes namespaces options log]}] + ; (log :report ::initializing) + (ring/router (postwalk (get-resolver registry namespaces) routes) + (process-options (assoc options :log log)))) diff --git a/test/duct/reitit_test.clj b/test/duct/reitit_test.clj index 532c99f..9e44f17 100644 --- a/test/duct/reitit_test.clj +++ b/test/duct/reitit_test.clj @@ -71,28 +71,28 @@ (let [config (core/prep-config base-config) in-options (new-config-handling base-config)] - ;; Reitit Module keys used for futher processing - (is (->> (keys config) - (filterv #(= "duct.reitit" (namespace %))) - (mapv #(keyword (name %))) - (= [:options :registry :middleware :logging]))) + ;; Reitit Module keys used for futher processing + (is (= [:options :registry :log] + (->> (keys config) + (filterv #(= "duct.reitit" (namespace %))) + (mapv #(keyword (name %)))))) (are [key value] (= value (key config)) - ;; Defaulhandler middleware namespace + ;; Defaulhandler middleware namespace :duct.core/handler-ns 'handler - ;; Default middleware namespace + ;; Default middleware namespace :duct.core/middleware-ns 'middleware - ;; Configuration Pass it reitit router to initialize it + ;; Configuration Pass it reitit router to initialize it :duct.router/reitit {:routes nil, - :middleware (ig/ref :duct.reitit/middleware) + :log (ig/ref :duct.reitit/log) :registry (ig/ref :duct.reitit/registry) :options (ig/ref :duct.reitit/options) :namespaces ["foo.handler" "foo.middleware"]} - ;; Configuration Pass it ring handler to initialize it + ;; Configuration Pass it ring handler to initialize it :duct.handler/root {:router (ig/ref :duct.router/reitit) :options (ig/ref :duct.reitit/options)}) - ;; Configuration Values + ;; Configuration Values (are [path value] (= (in-options path) value) [:logging :exceptions?] true ;; default types supported by default [:logging :pretty?] false ;; No pretty logging by default. @@ -106,9 +106,9 @@ (let [in-options (-> (assoc base-config :duct.profile/dev {}) (new-config-handling [:duct.profile/dev]))] (are [path value] (= (in-options path) value) - [:logging :exceptions?] true ;; default types supported by default - [:logging :coercions?] true ;; default types supported by default - [:logging :requests?] true ;; default types supported by default + [:logging :exceptions?] true ;; exceptions enabled by default + [:logging :coercions?] true ;; coercions enabled by default + [:logging :requests?] true ;; requests enabled by default [:logging :pretty?] true ;; pretty logging by default. [:logging :logger] nil ;; No logger by default. :muuntaja true ;; Muuntaja formatting is enabled by default @@ -129,7 +129,7 @@ :muuntaja true ;; Muuntaja formatting is enabled by default :environment {} ;; Empty Environment :middleware [] ;; Empty Middleware - :cross-origin nil))) ;; No Cross-origin + :cross-origin nil))) ;; No Cross-origin (deftest test-foo-module (let [extra {::coercion {:enable true :coercer 'spec}} @@ -138,13 +138,13 @@ routes (routes router) handler (config :duct.handler/root)] - (testing "Registry Merge" + (testing "Registry-Merge:" (are [x] (not= nil (x config)) :foo.handler/ping :foo.handler/index :foo.handler.plus/with-body)) - (testing "Resulting Router" + (testing "Resulting-Router:" (is (= :reitit.core/router (type router))) (is (= 5 (count routes))) (is (vector? (-> (get routes "/") :environment :db))) @@ -164,7 +164,7 @@ :get "/plus" {:query-params {:y 3 :x 6}} [:total] 9 :get "/author" {} [:author] "tami5")) - (testing "Custom Error Handling Repsonse" + (testing "Custom-Error-Repsonse:" (let [divide-by-zero-response (to-edn (handler (request :get "/divide" {:body-params {:y 0 :x 0}}))) no-params-response (to-edn (handler (request :get "/divide" {})))] @@ -173,7 +173,7 @@ (is (= {:y 0 :x 0} (:data divide-by-zero-response))) (is (= "No parameters received" (:cause no-params-response))))))) -(deftest module-behavior +(deftest test-logging-behavior (testing "Logging:" (let [does-include* (fn [ptr] (fn [str] (str/includes? str ptr))) spec-pretty? (does-include* "-- Spec failed --------------------") @@ -186,7 +186,7 @@ (->> request handler with-out-str checkfn)))] (testing "Exception-Logging:" - (let [base {::logging {:enable true :pretty? false :logger nil :exception? true}}] + (let [base {::logging {:enable true :pretty? false :exception? true}}] (are [checkfn cfg] (test-behavior :get "/divide" {:body-params {:y 0 :x 0}} base cfg checkfn) ;; Enabled ex-compact? {}