diff --git a/modules/reitit-middleware/src/reitit/ring/middleware/multipart.clj b/modules/reitit-middleware/src/reitit/ring/middleware/multipart.clj index 64365bee..e6152e02 100644 --- a/modules/reitit-middleware/src/reitit/ring/middleware/multipart.clj +++ b/modules/reitit-middleware/src/reitit/ring/middleware/multipart.clj @@ -1,15 +1,63 @@ (ns ^:no-doc reitit.ring.middleware.multipart + (:refer-clojure :exclude [compile]) (:require [reitit.coercion :as coercion] - [ring.middleware.multipart-params :as multipart-params])) + [ring.middleware.multipart-params :as multipart-params] + [clojure.spec.alpha :as s] + [spec-tools.core :as st]) + (:import (java.io File))) -(def parameter-coercion - {:multipart (coercion/->ParameterCoercion :multipart-params :string true true)}) +(s/def ::filename string?) +(s/def ::content-type string?) +(s/def ::tempfile (partial instance? File)) +(s/def ::bytes bytes?) +(s/def ::size int?) -(defn coerced-request [request coercers] +(def temp-file-part + "Spec for file param created by ring.middleware.multipart-params.temp-file store." + (st/spec + {:spec (s/keys :req-un [::filename ::content-type ::tempfile ::size]) + :swagger/type "file"})) + +(def bytes-part + "Spec for file param created by ring.middleware.multipart-params.byte-array store." + (st/spec + {:spec (s/keys :req-un [::filename ::content-type ::bytes]) + :swagger/type "file"})) + +(defn- coerced-request [request coercers] (if-let [coerced (if coercers (coercion/coerce-request coercers request))] (update request :parameters merge coerced) request)) +(defn- compile [options] + (fn [{:keys [parameters coercion]} opts] + (if-let [multipart (:multipart parameters)] + (let [parameter-coercion {:multipart (coercion/->ParameterCoercion + :multipart-params :string true true)} + opts (assoc opts ::coercion/parameter-coercion parameter-coercion) + coercers (if multipart (coercion/request-coercers coercion parameters opts))] + {:data {:swagger {:consumes ^:replace #{"multipart/form-data"}}} + :wrap (fn [handler] + (fn + ([request] + (try + (-> request + (multipart-params/multipart-params-request options) + (coerced-request coercers) + (handler)) + (catch Exception e + (.printStackTrace e) + (throw e)))) + ([request respond raise] + (-> request + (multipart-params/multipart-params-request options) + (coerced-request coercers) + (handler respond raise)))))})))) + +;; +;; public api +;; + (defn create-multipart-middleware "Creates a Middleware to handle the multipart params, based on ring.middleware.multipart-params, taking same options. Mounts only @@ -19,20 +67,11 @@ (create-multipart-middleware nil)) ([options] {: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))] - {:data {:swagger {:consumes #{"multipart/form-data"}}} - :wrap (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)))))})))})) + :compile (compile options)})) + +(def multipart-middleware + "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." + (create-multipart-middleware)) diff --git a/modules/reitit-schema/src/reitit/ring/schema.cljc b/modules/reitit-schema/src/reitit/ring/schema.cljc new file mode 100644 index 00000000..094b23b4 --- /dev/null +++ b/modules/reitit-schema/src/reitit/ring/schema.cljc @@ -0,0 +1,30 @@ +(ns reitit.ring.schema + (:require [schema.core :as s] + [schema-tools.swagger.core :as swagger]) + #?(:clj (:import (java.io File)))) + +(defrecord Upload [m] + s/Schema + (spec [_] + (s/spec m)) + (explain [_] + (cons 'file m)) + + swagger/SwaggerSchema + (-transform [_ _] + {:type "file"})) + +#?(:clj + (def TempFilePart + "Schema for file param created by ring.middleware.multipart-params.temp-file store." + (->Upload {:filename s/Str + :content-type s/Str + :size s/Int + :tempfile File}))) + +#?(:clj + (def BytesPart + "Schema for file param created by ring.middleware.multipart-params.byte-array store." + (->Upload {:filename s/Str + :content-type s/Str + :bytes s/Any})))