From 10ccbb72e32bb380465fc069e6686aa1848e63b1 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Tue, 17 Jul 2018 16:47:22 +0300 Subject: [PATCH] wip --- examples/ring-swagger/project.clj | 1 - examples/ring-swagger/src/example/server.clj | 15 ++-- modules/reitit-middleware/project.clj | 10 +++ .../ring/middleware/alpha/exception.cljc | 78 +++++++++++++++++++ .../ring/middleware/alpha/multipart.cljc | 34 ++++++++ .../ring/middleware/alpha/muuntaja.cljc | 17 ++++ modules/reitit/project.clj | 1 + project.clj | 6 +- scripts/lein-modules | 2 +- .../reitit/ring/middleware/muuntaja_test.clj | 18 +++++ 10 files changed, 168 insertions(+), 14 deletions(-) create mode 100644 modules/reitit-middleware/project.clj create mode 100644 modules/reitit-middleware/src/reitit/ring/middleware/alpha/exception.cljc create mode 100644 modules/reitit-middleware/src/reitit/ring/middleware/alpha/multipart.cljc create mode 100644 modules/reitit-middleware/src/reitit/ring/middleware/alpha/muuntaja.cljc create mode 100644 test/clj/reitit/ring/middleware/muuntaja_test.clj diff --git a/examples/ring-swagger/project.clj b/examples/ring-swagger/project.clj index c4fd112d..60f6406d 100644 --- a/examples/ring-swagger/project.clj +++ b/examples/ring-swagger/project.clj @@ -2,6 +2,5 @@ :description "Reitit Ring App with Swagger" :dependencies [[org.clojure/clojure "1.9.0"] [ring "1.6.3"] - [metosin/muuntaja "0.5.0"] [metosin/reitit "0.2.0-SNAPSHOT"]] :repl-options {:init-ns example.server}) diff --git a/examples/ring-swagger/src/example/server.clj b/examples/ring-swagger/src/example/server.clj index adb86200..c14eb283 100644 --- a/examples/ring-swagger/src/example/server.clj +++ b/examples/ring-swagger/src/example/server.clj @@ -5,11 +5,12 @@ [reitit.ring.coercion :as rrc] [reitit.coercion.spec :as spec] [reitit.coercion.schema :as schema] + [reitit.ring.middleware.alpha.muuntaja :as muuntaja] + [schema.core :refer [Int]] [ring.adapter.jetty :as jetty] - [ring.middleware.params] - [muuntaja.middleware])) + [ring.middleware.params])) (def app (ring/ring-handler @@ -58,17 +59,11 @@ :body {:total (+ x y)}})}}]]] {:data {:middleware [ring.middleware.params/wrap-params - muuntaja.middleware/wrap-format + (muuntaja/create-format-middleware) swagger/swagger-feature rrc/coerce-exceptions-middleware rrc/coerce-request-middleware - rrc/coerce-response-middleware] - :swagger {:produces #{"application/json" - "application/edn" - "application/transit+json"} - :consumes #{"application/json" - "application/edn" - "application/transit+json"}}}}) + rrc/coerce-response-middleware]}}) (ring/routes (swagger-ui/create-swagger-ui-handler {:path "/", :url "/api/swagger.json"}) diff --git a/modules/reitit-middleware/project.clj b/modules/reitit-middleware/project.clj new file mode 100644 index 00000000..8212153a --- /dev/null +++ b/modules/reitit-middleware/project.clj @@ -0,0 +1,10 @@ +(defproject metosin/reitit-middleware "0.2.0-SNAPSHOT" + :description "Reitit, common middleware bundled" + :url "https://github.com/metosin/reitit" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + :plugins [[lein-parent "0.3.2"]] + :parent-project {:path "../../project.clj" + :inherit [:deploy-repositories :managed-dependencies]} + :dependencies [[metosin/reitit-ring] + [metosin/muuntaja]]) diff --git a/modules/reitit-middleware/src/reitit/ring/middleware/alpha/exception.cljc b/modules/reitit-middleware/src/reitit/ring/middleware/alpha/exception.cljc new file mode 100644 index 00000000..b82d5276 --- /dev/null +++ b/modules/reitit-middleware/src/reitit/ring/middleware/alpha/exception.cljc @@ -0,0 +1,78 @@ +(ns reitit.ring.middleware.alpha.exception + (:require [reitit.coercion :as coercion] + [reitit.ring :as ring])) + +(defn- super-classes [^Class k] + (loop [sk (.getSuperclass k), ks []] + (if-not (= sk Object) + (recur (.getSuperclass sk) (conj ks sk)) + ks))) + +(defn- call-error-handler [default-handler handlers error request] + (let [{:keys [type] :as data} (ex-data error) + ex-class (class error) + error-handler (or (get handlers type) + (get handlers ex-class) + (some + (partial get handlers) + (super-classes ex-class)) + default-handler)] + (error-handler error data request))) + +(defn default-handler [^Exception e _ _] + {:status 500 + :body {:type "exception" + :class (.getName (.getClass e))}}) + +(defn coercion-handler [status] + (fn [_ data _] + {:status status + :body (coercion/encode-error data)})) + +(defn http-response-handler + "reads response from ex-data :response" + [_ {:keys [response]} _] + response) + +(defn request-parsing-handler [_ {:keys [format]} _] + {:status 400 + :headers {"Content-Type" "text/plain"} + :body (str "Malformed " format " request.")}) + +;; +;; public api +;; + +(def default-handlers + {::default default-handler + ::ring/response http-response-handler + :muuntaja/decode request-parsing-handler + ::coercion/request-coercion (coercion-handler 400) + ::coercion/response-coercion (coercion-handler 500)}) + +(defn create-exceptions-middleware + "Catches all exceptions and looks up a exception handler: + 1) `:type` of ex-data + 2) Class of Exception + 3) Super Classes of Exception + 4) The ::default handler" + ([] + (create-exceptions-middleware default-handlers)) + ([{:keys [handlers] :or {handlers default-handlers}}] + (let [default-handler (get handlers ::default default-handler) + on-exception (fn [e request respond raise] + (try + (respond (call-error-handler default-handler handlers e request)) + (catch Exception e + (raise e))))] + (fn + ([request] + (try + (handler request) + (catch Throwable e + (on-exception e request identity #(throw %))))) + ([request respond raise] + (try + (handler request respond (fn [e] (on-exception e request respond raise))) + (catch Throwable e + (on-exception e request respond raise)))))))) diff --git a/modules/reitit-middleware/src/reitit/ring/middleware/alpha/multipart.cljc b/modules/reitit-middleware/src/reitit/ring/middleware/alpha/multipart.cljc new file mode 100644 index 00000000..169294ea --- /dev/null +++ b/modules/reitit-middleware/src/reitit/ring/middleware/alpha/multipart.cljc @@ -0,0 +1,34 @@ +(ns reitit.ring.middleware.alpha.multipart + (:require [reitit.coercion :as coercion] + [ring.middleware.multipart-params :as multipart-params])) + +(defn multipart-params + "Creates a Middleware to handle the multipart params, based on + ring.middleware.multipart-params, taking same options. Mounts only + if endpoint has `[:parameters :multipart]` defined. Publishes coerced + parameters into `[:parameters :multipart]` under request." + ([] + (multipart-params nil)) + ([options] + (let [parameter-coercion {:multipart (coercion/->ParameterCoercion :multipart-params :string true true)} + coerced-request (fn [request coercers] + (if-let [coerced (if coercers (coercion/coerce-request coercers request))] + (update request :parameters merge coerced) + request))] + {:name ::multipart + :compile (fn [{:keys [parameters coercion]} opts] + (if-let [multipart (:multipart parameters)] + (let [opts (assoc opts ::coercion/parameter-coercion parameter-coercion) + coercers (if multipart (coercion/request-coercers coercion parameters opts))] + (fn [handler] + (fn + ([request] + (-> request + (multipart-params/multipart-params-request options) + (coerced-request coercers) + (handler))) + ([request respond raise] + (-> request + (multipart-params/multipart-params-request options) + (coerced-request coercers) + (handler respond raise))))))))}))) diff --git a/modules/reitit-middleware/src/reitit/ring/middleware/alpha/muuntaja.cljc b/modules/reitit-middleware/src/reitit/ring/middleware/alpha/muuntaja.cljc new file mode 100644 index 00000000..b45ba1ae --- /dev/null +++ b/modules/reitit-middleware/src/reitit/ring/middleware/alpha/muuntaja.cljc @@ -0,0 +1,17 @@ +(ns reitit.ring.middleware.alpha.muuntaja + (:require [muuntaja.core :as m] + [muuntaja.middleware])) + +(defn create-format-middleware + ([] + (create-format-middleware m/default-options)) + ([options] + {:name ::formats + :compile (fn [{:keys [muuntaja]} _] + (let [options (or muuntaja options)] + (if options + (let [m (m/create options)] + {:data {:swagger {:produces (m/encodes m) + :consumes (m/encodes m)}} + :wrap (fn [handler] + (muuntaja.middleware/wrap-format handler m))}))))})) diff --git a/modules/reitit/project.clj b/modules/reitit/project.clj index b0b8bcfa..0dba44f6 100644 --- a/modules/reitit/project.clj +++ b/modules/reitit/project.clj @@ -8,6 +8,7 @@ :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-core] [metosin/reitit-ring] + [metosin/reitit-middleware] [metosin/reitit-spec] [metosin/reitit-schema] [metosin/reitit-swagger] diff --git a/project.clj b/project.clj index c67701a3..6a425083 100644 --- a/project.clj +++ b/project.clj @@ -12,17 +12,18 @@ :managed-dependencies [[metosin/reitit "0.2.0-SNAPSHOT"] [metosin/reitit-core "0.2.0-SNAPSHOT"] [metosin/reitit-ring "0.2.0-SNAPSHOT"] + [metosin/reitit-middleware "0.2.0-SNAPSHOT"] [metosin/reitit-spec "0.2.0-SNAPSHOT"] [metosin/reitit-schema "0.2.0-SNAPSHOT"] [metosin/reitit-swagger "0.2.0-SNAPSHOT"] [metosin/reitit-swagger-ui "0.2.0-SNAPSHOT"] [metosin/reitit-frontend "0.2.0-SNAPSHOT"] - [meta-merge "1.0.0"] [ring/ring-core "1.6.3"] [metosin/spec-tools "0.7.1"] [metosin/schema-tools "0.10.3"] [metosin/ring-swagger-ui "2.2.10"] + [metosin/muuntaja "0.6.0-alpha1"] [metosin/jsonista "0.2.1"]] :plugins [[jonase/eastwood "0.2.6"] @@ -38,6 +39,7 @@ :source-paths ["modules/reitit/src" "modules/reitit-core/src" "modules/reitit-ring/src" + "modules/reitit-middleware/src" "modules/reitit-spec/src" "modules/reitit-schema/src" "modules/reitit-swagger/src" @@ -55,7 +57,7 @@ [ring "1.6.3"] [ikitommi/immutant-web "3.0.0-alpha1"] - [metosin/muuntaja "0.6.0-SNAPSHOT"] + [metosin/muuntaja "0.6.0-alpha1"] [metosin/ring-swagger-ui "2.2.10"] [metosin/jsonista "0.2.1"] diff --git a/scripts/lein-modules b/scripts/lein-modules index fc3870d8..17eefffc 100755 --- a/scripts/lein-modules +++ b/scripts/lein-modules @@ -3,6 +3,6 @@ set -e # Modules -for ext in reitit-core reitit-ring reitit-spec reitit-schema reitit-swagger reitit-swagger-ui reitit-frontend reitit; do +for ext in reitit-core reitit-ring reitit-middleware reitit-spec reitit-schema reitit-swagger reitit-swagger-ui reitit-frontend reitit; do cd modules/$ext; lein "$@"; cd ../..; done diff --git a/test/clj/reitit/ring/middleware/muuntaja_test.clj b/test/clj/reitit/ring/middleware/muuntaja_test.clj new file mode 100644 index 00000000..521da94c --- /dev/null +++ b/test/clj/reitit/ring/middleware/muuntaja_test.clj @@ -0,0 +1,18 @@ +(ns reitit.ring.middleware.muuntaja-test + (:require [clojure.test :refer [deftest testing is]] + [reitit.ring :as ring] + [reitit.ring.middleware.alpha.muuntaja :as muuntaja] + [muuntaja.core :as m])) + +(deftest muuntaja-test + (let [data {:kikka "kukka"} + app (ring/ring-handler + (ring/router + ["/ping" {:get (constantly {:status 200, :body data})}] + {:data {:middleware [(muuntaja/create-format-middleware)]}}))] + (is (= data (->> {:request-method :get, :uri "/ping"} + (app) + :body + (m/decode m/instance "application/json")))))) + +;; TODO: test swagger!