mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 08:51:12 +00:00
Merge pull request #593 from metosin/openapi-multipart
OpenAPI3 multipart support
This commit is contained in:
commit
bae6e6b8dd
14 changed files with 175 additions and 25 deletions
|
|
@ -4,14 +4,15 @@ Basic coercion is explained in detail [in the Coercion Guide](../coercion/coerci
|
||||||
|
|
||||||
The following request parameters are currently supported:
|
The following request parameters are currently supported:
|
||||||
|
|
||||||
| type | request source |
|
| type | request source |
|
||||||
|------------|--------------------------------------------------|
|
|--------------|--------------------------------------------------|
|
||||||
| `:query` | `:query-params` |
|
| `:query` | `:query-params` |
|
||||||
| `:body` | `:body-params` |
|
| `:body` | `:body-params` |
|
||||||
| `:request` | `:body-params`, allows per-content-type coercion |
|
| `:request` | `:body-params`, allows per-content-type coercion |
|
||||||
| `:form` | `:form-params` |
|
| `:form` | `:form-params` |
|
||||||
| `:header` | `:header-params` |
|
| `:header` | `:header-params` |
|
||||||
| `:path` | `:path-params` |
|
| `:path` | `:path-params` |
|
||||||
|
| `:multipart` | `:multipart-params`, see [Default Middleware](default_middleware.md) |
|
||||||
|
|
||||||
To enable coercion, the following things need to be done:
|
To enable coercion, the following things need to be done:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@
|
||||||
Reitit can generate [OpenAPI 3.1.0](https://spec.openapis.org/oas/v3.1.0)
|
Reitit can generate [OpenAPI 3.1.0](https://spec.openapis.org/oas/v3.1.0)
|
||||||
documentation. The feature works similarly to [Swagger documentation](swagger.md).
|
documentation. The feature works similarly to [Swagger documentation](swagger.md).
|
||||||
|
|
||||||
The [http-swagger example](../../examples/http-swagger) also has OpenAPI documentation.
|
The [http-swagger](../../examples/http-swagger) and
|
||||||
|
[ring-malli-swagger](../../examples/ring-malli-swagger) examples also
|
||||||
|
have OpenAPI documentation.
|
||||||
|
|
||||||
## OpenAPI data
|
## OpenAPI data
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,8 @@
|
||||||
["/download"
|
["/download"
|
||||||
{:get {:summary "downloads a file"
|
{:get {:summary "downloads a file"
|
||||||
:swagger {:produces ["image/png"]}
|
:swagger {:produces ["image/png"]}
|
||||||
|
:responses {200 {:description "an image"
|
||||||
|
:content {"image/png" any?}}}
|
||||||
:handler (fn [_]
|
:handler (fn [_]
|
||||||
{:status 200
|
{:status 200
|
||||||
:headers {"Content-Type" "image/png"}
|
:headers {"Content-Type" "image/png"}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
(start)
|
(start)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- Swagger spec served at <http://localhost:3000/swagger.json>
|
||||||
|
- Openapi spec served at <http://localhost:3000/openapi.json>
|
||||||
|
- Swagger UI served at <http://localhost:3000/>
|
||||||
|
|
||||||
To test the endpoints using [httpie](https://httpie.org/):
|
To test the endpoints using [httpie](https://httpie.org/):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -20,4 +24,4 @@ http GET :3000/swagger.json
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright © 2017-2019 Metosin Oy
|
Copyright © 2017-2023 Metosin Oy
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||||
[metosin/jsonista "0.2.6"]
|
[metosin/jsonista "0.2.6"]
|
||||||
[ring/ring-jetty-adapter "1.7.1"]
|
[ring/ring-jetty-adapter "1.7.1"]
|
||||||
[metosin/reitit "0.6.0"]]
|
[metosin/reitit "0.6.0"]
|
||||||
|
[metosin/ring-swagger-ui "5.0.0-alpha.0"]]
|
||||||
:repl-options {:init-ns example.server}
|
:repl-options {:init-ns example.server}
|
||||||
:profiles {:dev {:dependencies [[ring/ring-mock "0.3.2"]]}})
|
:profiles {:dev {:dependencies [[ring/ring-mock "0.3.2"]]}})
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
(ns example.server
|
(ns example.server
|
||||||
(:require [reitit.ring :as ring]
|
(:require [reitit.ring :as ring]
|
||||||
[reitit.coercion.malli]
|
[reitit.coercion.malli]
|
||||||
|
[reitit.openapi :as openapi]
|
||||||
[reitit.ring.malli]
|
[reitit.ring.malli]
|
||||||
[reitit.swagger :as swagger]
|
[reitit.swagger :as swagger]
|
||||||
[reitit.swagger-ui :as swagger-ui]
|
[reitit.swagger-ui :as swagger-ui]
|
||||||
|
|
@ -24,13 +25,20 @@
|
||||||
[["/swagger.json"
|
[["/swagger.json"
|
||||||
{:get {:no-doc true
|
{:get {:no-doc true
|
||||||
:swagger {:info {:title "my-api"
|
:swagger {:info {:title "my-api"
|
||||||
:description "with [malli](https://github.com/metosin/malli) and reitit-ring"}
|
:description "swagger docs with [malli](https://github.com/metosin/malli) and reitit-ring"
|
||||||
|
:version "0.0.1"}
|
||||||
:tags [{:name "files", :description "file api"}
|
:tags [{:name "files", :description "file api"}
|
||||||
{:name "math", :description "math api"}]}
|
{:name "math", :description "math api"}]}
|
||||||
:handler (swagger/create-swagger-handler)}}]
|
:handler (swagger/create-swagger-handler)}}]
|
||||||
|
["/openapi.json"
|
||||||
|
{:get {:no-doc true
|
||||||
|
:openapi {:info {:title "my-api"
|
||||||
|
:description "openapi3 docs with [malli](https://github.com/metosin/malli) and reitit-ring"
|
||||||
|
:version "0.0.1"}}
|
||||||
|
:handler (openapi/create-openapi-handler)}}]
|
||||||
|
|
||||||
["/files"
|
["/files"
|
||||||
{:swagger {:tags ["files"]}}
|
{:tags ["files"]}
|
||||||
|
|
||||||
["/upload"
|
["/upload"
|
||||||
{:post {:summary "upload a file"
|
{:post {:summary "upload a file"
|
||||||
|
|
@ -44,6 +52,8 @@
|
||||||
["/download"
|
["/download"
|
||||||
{:get {:summary "downloads a file"
|
{:get {:summary "downloads a file"
|
||||||
:swagger {:produces ["image/png"]}
|
:swagger {:produces ["image/png"]}
|
||||||
|
:responses {200 {:description "an image"
|
||||||
|
:content {"image/png" any?}}}
|
||||||
:handler (fn [_]
|
:handler (fn [_]
|
||||||
{:status 200
|
{:status 200
|
||||||
:headers {"Content-Type" "image/png"}
|
:headers {"Content-Type" "image/png"}
|
||||||
|
|
@ -52,7 +62,7 @@
|
||||||
(io/input-stream))})}}]]
|
(io/input-stream))})}}]]
|
||||||
|
|
||||||
["/math"
|
["/math"
|
||||||
{:swagger {:tags ["math"]}}
|
{:tags ["math"]}
|
||||||
|
|
||||||
["/plus"
|
["/plus"
|
||||||
{:get {:summary "plus with malli query parameters"
|
{:get {:summary "plus with malli query parameters"
|
||||||
|
|
@ -96,8 +106,9 @@
|
||||||
;; malli options
|
;; malli options
|
||||||
:options nil})
|
:options nil})
|
||||||
:muuntaja m/instance
|
:muuntaja m/instance
|
||||||
:middleware [;; swagger feature
|
:middleware [;; swagger & openapi
|
||||||
swagger/swagger-feature
|
swagger/swagger-feature
|
||||||
|
openapi/openapi-feature
|
||||||
;; query-params & form-params
|
;; query-params & form-params
|
||||||
parameters/parameters-middleware
|
parameters/parameters-middleware
|
||||||
;; content-negotiation
|
;; content-negotiation
|
||||||
|
|
@ -118,6 +129,9 @@
|
||||||
(swagger-ui/create-swagger-ui-handler
|
(swagger-ui/create-swagger-ui-handler
|
||||||
{:path "/"
|
{:path "/"
|
||||||
:config {:validatorUrl nil
|
:config {:validatorUrl nil
|
||||||
|
:urls [{:name "swagger", :url "swagger.json"}
|
||||||
|
{:name "openapi", :url "openapi.json"}]
|
||||||
|
:urls.primaryName "openapi"
|
||||||
:operationsSorter "alpha"}})
|
:operationsSorter "alpha"}})
|
||||||
(ring/create-default-handler))))
|
(ring/create-default-handler))))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))]
|
||||||
|
|
|
||||||
|
|
@ -135,8 +135,8 @@
|
||||||
|
|
||||||
(defn -get-apidocs-openapi
|
(defn -get-apidocs-openapi
|
||||||
[coercion {:keys [parameters responses content-types] :or {content-types ["application/json"]}} options]
|
[coercion {:keys [parameters responses content-types] :or {content-types ["application/json"]}} options]
|
||||||
(let [{:keys [body request]} parameters
|
(let [{:keys [body request multipart]} parameters
|
||||||
parameters (dissoc parameters :request :body)
|
parameters (dissoc parameters :request :body :multipart)
|
||||||
->schema-object (fn [schema opts]
|
->schema-object (fn [schema opts]
|
||||||
(let [current-opts (merge options opts)]
|
(let [current-opts (merge options opts)]
|
||||||
(json-schema/transform (coercion/-compile-model coercion schema current-opts)
|
(json-schema/transform (coercion/-compile-model coercion schema current-opts)
|
||||||
|
|
@ -184,6 +184,14 @@
|
||||||
:content-type content-type})]
|
:content-type content-type})]
|
||||||
[content-type {:schema schema}])))
|
[content-type {:schema schema}])))
|
||||||
(:content request)))}})
|
(:content request)))}})
|
||||||
|
(when multipart
|
||||||
|
{:requestBody
|
||||||
|
{:content
|
||||||
|
{"multipart/form-data"
|
||||||
|
{:schema
|
||||||
|
(->schema-object multipart {:in :requestBody
|
||||||
|
:type :schema
|
||||||
|
:content-type "multipart/form-data"})}}}})
|
||||||
(when responses
|
(when responses
|
||||||
{:responses
|
{:responses
|
||||||
(into {}
|
(into {}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(def temp-file-part
|
(def temp-file-part
|
||||||
"Schema for file param created by ring.middleware.multipart-params.temp-file store."
|
"Schema for file param created by ring.middleware.multipart-params.temp-file store."
|
||||||
[:map {:json-schema {:type "file"}}
|
[:map {:swagger {:type "file"}
|
||||||
|
:json-schema {:type "string"
|
||||||
|
:format "binary"}}
|
||||||
[:filename string?]
|
[:filename string?]
|
||||||
[:content-type string?]
|
[:content-type string?]
|
||||||
[:size int?]
|
[:size int?]
|
||||||
|
|
@ -13,7 +15,9 @@
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(def bytes-part
|
(def bytes-part
|
||||||
"Schema for file param created by ring.middleware.multipart-params.byte-array store."
|
"Schema for file param created by ring.middleware.multipart-params.byte-array store."
|
||||||
[:map {:json-schema {:type "file"}}
|
[:map {:swagger {:type "file"}
|
||||||
|
:json-schema {:type "string"
|
||||||
|
:format "binary"}}
|
||||||
[:filename string?]
|
[:filename string?]
|
||||||
[:content-type string?]
|
[:content-type string?]
|
||||||
[:bytes bytes?]]))
|
[:bytes bytes?]]))
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,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
|
(into
|
||||||
(empty parameters)
|
(empty parameters)
|
||||||
|
|
@ -85,6 +85,10 @@
|
||||||
(when-let [default (get-in parameters [:request :body])]
|
(when-let [default (get-in parameters [:request :body])]
|
||||||
(zipmap content-types (repeat default)))
|
(zipmap content-types (repeat default)))
|
||||||
(:content (:request parameters)))})})
|
(:content (:request parameters)))})})
|
||||||
|
(when (:multipart parameters)
|
||||||
|
{:requestBody
|
||||||
|
(openapi/openapi-spec
|
||||||
|
{::openapi/content {"multipart/form-data" (:multipart parameters)}})})
|
||||||
(when responses
|
(when responses
|
||||||
{:responses
|
{:responses
|
||||||
(into
|
(into
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
[metosin/reitit-pedestal "0.6.0"]
|
[metosin/reitit-pedestal "0.6.0"]
|
||||||
[metosin/ring-swagger-ui "4.15.5"]
|
[metosin/ring-swagger-ui "4.15.5"]
|
||||||
[metosin/spec-tools "0.10.5"]
|
[metosin/spec-tools "0.10.5"]
|
||||||
[metosin/schema-tools "0.12.3"]
|
[metosin/schema-tools "0.13.0"]
|
||||||
[metosin/muuntaja "0.6.8"]
|
[metosin/muuntaja "0.6.8"]
|
||||||
[metosin/jsonista "0.3.7"]
|
[metosin/jsonista "0.3.7"]
|
||||||
[metosin/sieppari "0.0.0-alpha13"]
|
[metosin/sieppari "0.0.0-alpha13"]
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
[org.clojure/clojurescript "1.10.773"]
|
[org.clojure/clojurescript "1.10.773"]
|
||||||
|
|
||||||
;; modules dependencies
|
;; modules dependencies
|
||||||
[metosin/schema-tools "0.12.3"]
|
[metosin/schema-tools "0.13.0"]
|
||||||
[metosin/spec-tools "0.10.5"]
|
[metosin/spec-tools "0.10.5"]
|
||||||
[metosin/muuntaja "0.6.8"]
|
[metosin/muuntaja "0.6.8"]
|
||||||
[metosin/sieppari "0.0.0-alpha13"]
|
[metosin/sieppari "0.0.0-alpha13"]
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,15 @@
|
||||||
[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.malli]
|
||||||
[reitit.ring.spec]
|
[reitit.ring.spec]
|
||||||
[reitit.ring.coercion :as rrc]
|
[reitit.ring.coercion :as rrc]
|
||||||
[reitit.swagger-ui :as swagger-ui]
|
[reitit.swagger-ui :as swagger-ui]
|
||||||
[schema.core :as s]
|
[schema.core :as s]
|
||||||
|
[schema-tools.core]
|
||||||
[spec-tools.data-spec :as ds]))
|
[spec-tools.data-spec :as ds]))
|
||||||
|
|
||||||
(defn validate
|
(defn validate
|
||||||
|
|
@ -429,6 +432,54 @@
|
||||||
(testing "spec is valid"
|
(testing "spec is valid"
|
||||||
(is (nil? (validate spec))))))))
|
(is (nil? (validate spec))))))))
|
||||||
|
|
||||||
|
(deftest multipart-test
|
||||||
|
(doseq [[coercion file-schema string-schema]
|
||||||
|
[[#'malli/coercion
|
||||||
|
reitit.ring.malli/bytes-part
|
||||||
|
:string]
|
||||||
|
[#'schema/coercion
|
||||||
|
(schema-tools.core/schema {:filename s/Str
|
||||||
|
:content-type s/Str
|
||||||
|
:bytes s/Num}
|
||||||
|
{:openapi {:type "string"
|
||||||
|
:format "binary"}})
|
||||||
|
s/Str]
|
||||||
|
[#'spec/coercion
|
||||||
|
reitit.http.interceptors.multipart/bytes-part
|
||||||
|
string?]]]
|
||||||
|
(testing coercion
|
||||||
|
(let [app (ring/ring-handler
|
||||||
|
(ring/router
|
||||||
|
[["/upload"
|
||||||
|
{:post {:decription "upload"
|
||||||
|
:coercion @coercion
|
||||||
|
:parameters {:multipart {:file file-schema
|
||||||
|
:more string-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 (nil? (get-in spec [:paths "/upload" :post :parameters])))
|
||||||
|
(is (= (merge {:type "object"
|
||||||
|
:properties {:file {:type "string"
|
||||||
|
:format "binary"}
|
||||||
|
:more {:type "string"}}
|
||||||
|
:required ["file" "more"]}
|
||||||
|
(when-not (= #'spec/coercion coercion)
|
||||||
|
{:additionalProperties false}))
|
||||||
|
(-> spec
|
||||||
|
(get-in [:paths "/upload" :post :requestBody :content "multipart/form-data" :schema])
|
||||||
|
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]])]
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,27 @@
|
||||||
(ns reitit.swagger-test
|
(ns reitit.swagger-test
|
||||||
(:require [clojure.test :refer [deftest is testing]]
|
(:require [clojure.test :refer [deftest is testing]]
|
||||||
|
[jsonista.core :as j]
|
||||||
[muuntaja.core :as m]
|
[muuntaja.core :as m]
|
||||||
[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.ring :as ring]
|
[reitit.ring :as ring]
|
||||||
|
[reitit.ring.malli]
|
||||||
[reitit.ring.coercion :as rrc]
|
[reitit.ring.coercion :as rrc]
|
||||||
[reitit.swagger :as swagger]
|
[reitit.swagger :as swagger]
|
||||||
[reitit.swagger-ui :as swagger-ui]
|
[reitit.swagger-ui :as swagger-ui]
|
||||||
[schema.core :as s]
|
[schema.core :as s]
|
||||||
[spec-tools.data-spec :as ds]))
|
[spec-tools.data-spec :as ds]))
|
||||||
|
|
||||||
|
(defn- normalize
|
||||||
|
"Normalize format of swagger spec by converting it to json and back.
|
||||||
|
Handles differences like :q vs \"q\" in swagger generation."
|
||||||
|
[data]
|
||||||
|
(-> data
|
||||||
|
j/write-value-as-string
|
||||||
|
(j/read-value j/keyword-keys-object-mapper)))
|
||||||
|
|
||||||
(def app
|
(def app
|
||||||
(ring/ring-handler
|
(ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
|
|
@ -410,3 +421,41 @@
|
||||||
:handler (swagger/create-swagger-handler)}}]]))
|
:handler (swagger/create-swagger-handler)}}]]))
|
||||||
output (with-out-str (app {:request-method :get, :uri "/swagger.json"}))]
|
output (with-out-str (app {:request-method :get, :uri "/swagger.json"}))]
|
||||||
(is (.contains output "WARN")))))
|
(is (.contains output "WARN")))))
|
||||||
|
|
||||||
|
(deftest multipart-test
|
||||||
|
(doseq [[coercion file-schema string-schema]
|
||||||
|
[[#'malli/coercion
|
||||||
|
reitit.ring.malli/bytes-part
|
||||||
|
:string]
|
||||||
|
[#'spec/coercion
|
||||||
|
reitit.http.interceptors.multipart/bytes-part
|
||||||
|
string?]]]
|
||||||
|
(testing coercion
|
||||||
|
(let [app (ring/ring-handler
|
||||||
|
(ring/router
|
||||||
|
[["/upload"
|
||||||
|
{:post {:decription "upload"
|
||||||
|
:coercion @coercion
|
||||||
|
:parameters {:multipart {:file file-schema
|
||||||
|
:more string-schema}}
|
||||||
|
:handler identity}}]
|
||||||
|
["/swagger.json"
|
||||||
|
{:get {:no-doc true
|
||||||
|
:handler (swagger/create-swagger-handler)}}]]
|
||||||
|
{:data {:middleware [swagger/swagger-feature]}}))
|
||||||
|
spec (-> {:request-method :get
|
||||||
|
:uri "/swagger.json"}
|
||||||
|
app
|
||||||
|
:body)]
|
||||||
|
(is (= [{:description ""
|
||||||
|
:in "formData"
|
||||||
|
:name "file"
|
||||||
|
:required true
|
||||||
|
:type "file"}
|
||||||
|
{:description ""
|
||||||
|
:in "formData"
|
||||||
|
:name "more"
|
||||||
|
:required true
|
||||||
|
:type "string"}]
|
||||||
|
(normalize
|
||||||
|
(get-in spec [:paths "/upload" :post :parameters]))))))))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue