feat: openapi3 multipart support for spec

This commit is contained in:
Joel Kaasinen 2023-03-15 10:22:26 +02:00
parent 8bf4b5c6a6
commit f322597c04
3 changed files with 47 additions and 3 deletions

View file

@ -19,13 +19,17 @@
"Spec for file param created by ring.middleware.multipart-params.temp-file store." "Spec for file param created by ring.middleware.multipart-params.temp-file store."
(st/spec (st/spec
{:spec (s/keys :req-un [::filename ::content-type ::tempfile ::size]) {:spec (s/keys :req-un [::filename ::content-type ::tempfile ::size])
:swagger/type "file"})) :swagger/type "file"
:openapi {:type "string"
:format "binary"}}))
(def bytes-part (def bytes-part
"Spec for file param created by ring.middleware.multipart-params.byte-array store." "Spec for file param created by ring.middleware.multipart-params.byte-array store."
(st/spec (st/spec
{:spec (s/keys :req-un [::filename ::content-type ::bytes]) {:spec (s/keys :req-un [::filename ::content-type ::bytes])
:swagger/type "file"})) :swagger/type "file"
:openapi {:type "string"
:format "binary"}}))
(defn- coerced-request [request coercers] (defn- coerced-request [request coercers]
(if-let [coerced (if coercers (coercion/coerce-request coercers request))] (if-let [coerced (if coercers (coercion/coerce-request coercers request))]

View file

@ -108,7 +108,7 @@
(update $ :schema #(coercion/-compile-model this % nil)) (update $ :schema #(coercion/-compile-model this % nil))
$))]))}))) $))]))})))
:openapi (merge :openapi (merge
(when (seq (dissoc parameters :body :request)) (when (seq (dissoc parameters :body :request :multipart))
(openapi/openapi-spec {::openapi/parameters (openapi/openapi-spec {::openapi/parameters
(into (empty parameters) (into (empty parameters)
(for [[k v] (dissoc parameters :body :request)] (for [[k v] (dissoc parameters :body :request)]
@ -124,6 +124,12 @@
(into {} (into {}
(for [[format model] (:content (:request parameters))] (for [[format model] (:content (:request parameters))]
[format (coercion/-compile-model this model nil)])))})}) [format (coercion/-compile-model this model nil)])))})})
(when (:multipart parameters)
{:requestBody
(openapi/openapi-spec
{::openapi/content
{"multipart/form-data"
(coercion/-compile-model this (:multipart parameters) nil)}})})
(when responses (when responses
{:responses {:responses
(into (into

View file

@ -8,6 +8,7 @@
[reitit.coercion.malli :as malli] [reitit.coercion.malli :as malli]
[reitit.coercion.schema :as schema] [reitit.coercion.schema :as schema]
[reitit.coercion.spec :as spec] [reitit.coercion.spec :as spec]
[reitit.http.interceptors.multipart]
[reitit.openapi :as openapi] [reitit.openapi :as openapi]
[reitit.ring :as ring] [reitit.ring :as ring]
[reitit.ring.spec] [reitit.ring.spec]
@ -429,6 +430,39 @@
(testing "spec is valid" (testing "spec is valid"
(is (nil? (validate spec)))))))) (is (nil? (validate spec))))))))
(deftest multipart-test
(doseq [[coercion file-schema]
[#_[#'malli/coercion (fn [nom] [:map [nom :string]])]
#_[#'schema/coercion (fn [nom] {nom s/Str})]
[#'spec/coercion reitit.http.interceptors.multipart/bytes-part]]]
(testing coercion
(let [app (ring/ring-handler
(ring/router
[["/upload"
{:post {:decription "upload"
:coercion @coercion
:parameters {:multipart {:file file-schema}}
:handler identity}}]
["/openapi.json"
{:get {:handler (openapi/create-openapi-handler)
:openapi {:info {:title "" :version "0.0.1"}}
:no-doc true}}]]
{:data {:middleware [openapi/openapi-feature]}}))
spec (-> {:request-method :get
:uri "/openapi.json"}
app
:body)]
(testing "multipart body"
(is (= {:requestBody {:content {"multipart/form-data" {:schema {:type "object"
:properties {"file" {:type "string"
:format "binary"}}
:required ["file"]}}}}}
(-> spec
(get-in [:paths "/upload" :post])
#_normalize))))
(testing "spec is valid"
(is (nil? (validate spec))))))))
(deftest per-content-type-test (deftest per-content-type-test
(doseq [[coercion ->schema] (doseq [[coercion ->schema]
[[#'malli/coercion (fn [nom] [:map [nom :string]])] [[#'malli/coercion (fn [nom] [:map [nom :string]])]