mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 17:01:11 +00:00
Merge pull request #628 from metosin/openapi-parameters
Openapi parameters
This commit is contained in:
commit
b0c810a981
13 changed files with 429 additions and 284 deletions
|
|
@ -160,14 +160,14 @@ You can also specify request and response body schemas per content-type. The syn
|
||||||
(ring/router
|
(ring/router
|
||||||
["/api"
|
["/api"
|
||||||
["/example" {:post {:coercion reitit.coercion.schema/coercion
|
["/example" {:post {:coercion reitit.coercion.schema/coercion
|
||||||
:parameters {:request {:content {"application/json" {:y s/Int}
|
:request {:content {"application/json" {:schema {:y s/Int}}
|
||||||
"application/edn" {:z s/Int}}
|
"application/edn" {:schema {:z s/Int}}}
|
||||||
;; default if no content-type matches:
|
;; default if no content-type matches:
|
||||||
:body {:yy s/Int}}}
|
:body {:yy s/Int}}
|
||||||
:responses {200 {:content {"application/json" {:w s/Int}
|
:responses {200 {:content {"application/json" {:schema {:w s/Int}}
|
||||||
"application/edn" {:x s/Int}}
|
"application/edn" {:schema {:x s/Int}}}
|
||||||
;; default if no content-type matches:
|
;; default if no content-type matches:
|
||||||
:body {:ww s/Int}}
|
:body {:ww s/Int}}}
|
||||||
:handler ...}}]]
|
:handler ...}}]]
|
||||||
{:data {:middleware [rrc/coerce-exceptions-middleware
|
{:data {:middleware [rrc/coerce-exceptions-middleware
|
||||||
rrc/coerce-request-middleware
|
rrc/coerce-request-middleware
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@
|
||||||
(def ^:no-doc default-parameter-coercion
|
(def ^:no-doc default-parameter-coercion
|
||||||
{:query (->ParameterCoercion :query-params :string true true)
|
{:query (->ParameterCoercion :query-params :string true true)
|
||||||
:body (->ParameterCoercion :body-params :body false false)
|
:body (->ParameterCoercion :body-params :body false false)
|
||||||
:request (->ParameterCoercion :body-params :request false false)
|
|
||||||
:form (->ParameterCoercion :form-params :string true true)
|
:form (->ParameterCoercion :form-params :string true true)
|
||||||
:header (->ParameterCoercion :headers :string true true)
|
:header (->ParameterCoercion :headers :string true true)
|
||||||
:path (->ParameterCoercion :path-params :string true true)
|
:path (->ParameterCoercion :path-params :string true true)
|
||||||
|
|
@ -83,26 +82,45 @@
|
||||||
value)
|
value)
|
||||||
|
|
||||||
;; TODO: support faster key walking, walk/keywordize-keys is quite slow...
|
;; TODO: support faster key walking, walk/keywordize-keys is quite slow...
|
||||||
(defn request-coercer [coercion type model {::keys [extract-request-format parameter-coercion serialize-failed-result]
|
(defn request-coercer [coercion type model {::keys [extract-request-format parameter-coercion serialize-failed-result skip]
|
||||||
:or {extract-request-format extract-request-format-default
|
:or {extract-request-format extract-request-format-default
|
||||||
parameter-coercion default-parameter-coercion}}]
|
parameter-coercion default-parameter-coercion
|
||||||
|
skip #{}}}]
|
||||||
(if coercion
|
(if coercion
|
||||||
(if-let [{:keys [keywordize? open? in style]} (parameter-coercion type)]
|
(when-let [{:keys [keywordize? open? in style]} (parameter-coercion type)]
|
||||||
|
(when-not (skip style)
|
||||||
(let [transform (comp (if keywordize? walk/keywordize-keys identity) in)
|
(let [transform (comp (if keywordize? walk/keywordize-keys identity) in)
|
||||||
->open (if open? #(-open-model coercion %) identity)
|
->open (if open? #(-open-model coercion %) identity)
|
||||||
format-schema-pairs (if (= :request style)
|
coercer (-request-coercer coercion style (->open model))]
|
||||||
(conj (:content model) [:default (:body model)])
|
(when coercer
|
||||||
[[:default model]])
|
|
||||||
format->coercer (some->> (for [[format schema] format-schema-pairs
|
|
||||||
:when schema
|
|
||||||
:let [type (case style :request :body style)]]
|
|
||||||
[format (-request-coercer coercion type (->open schema))])
|
|
||||||
(filter second)
|
|
||||||
(seq)
|
|
||||||
(into {}))]
|
|
||||||
(when format->coercer
|
|
||||||
(fn [request]
|
(fn [request]
|
||||||
(let [value (transform request)
|
(let [value (transform request)
|
||||||
|
format (extract-request-format request)
|
||||||
|
result (coercer value format)]
|
||||||
|
(if (error? result)
|
||||||
|
(request-coercion-failed! result coercion value in request serialize-failed-result)
|
||||||
|
result)))))))))
|
||||||
|
|
||||||
|
(defn get-default-schema [request-or-response]
|
||||||
|
(or (-> request-or-response :content :default :schema)
|
||||||
|
(:body request-or-response)))
|
||||||
|
|
||||||
|
(defn get-default [request-or-response]
|
||||||
|
(or (-> request-or-response :content :default)
|
||||||
|
(some->> request-or-response :body (assoc {} :schema))))
|
||||||
|
|
||||||
|
(defn content-request-coercer [coercion {:keys [content body]} {::keys [extract-request-format serialize-failed-result]
|
||||||
|
:or {extract-request-format extract-request-format-default}}]
|
||||||
|
(when coercion
|
||||||
|
(let [in :body-params
|
||||||
|
format->coercer (some->> (concat (when body
|
||||||
|
[[:default (-request-coercer coercion :body body)]])
|
||||||
|
(for [[format {:keys [schema]}] content, :when schema]
|
||||||
|
[format (-request-coercer coercion :body schema)]))
|
||||||
|
(filter second) (seq) (into (array-map)))]
|
||||||
|
(when format->coercer
|
||||||
|
(fn [request]
|
||||||
|
(let [value (in request)
|
||||||
format (extract-request-format request)
|
format (extract-request-format request)
|
||||||
coercer (or (format->coercer format)
|
coercer (or (format->coercer format)
|
||||||
(format->coercer :default)
|
(format->coercer :default)
|
||||||
|
|
@ -110,7 +128,7 @@
|
||||||
result (coercer value format)]
|
result (coercer value format)]
|
||||||
(if (error? result)
|
(if (error? result)
|
||||||
(request-coercion-failed! result coercion value in request serialize-failed-result)
|
(request-coercion-failed! result coercion value in request serialize-failed-result)
|
||||||
result))))))))
|
result)))))))
|
||||||
|
|
||||||
(defn extract-response-format-default [request _]
|
(defn extract-response-format-default [request _]
|
||||||
(-> request :muuntaja/response :format))
|
(-> request :muuntaja/response :format))
|
||||||
|
|
@ -118,18 +136,18 @@
|
||||||
(defn response-coercer [coercion {:keys [content body]} {:keys [extract-response-format serialize-failed-result]
|
(defn response-coercer [coercion {:keys [content body]} {:keys [extract-response-format serialize-failed-result]
|
||||||
:or {extract-response-format extract-response-format-default}}]
|
:or {extract-response-format extract-response-format-default}}]
|
||||||
(if coercion
|
(if coercion
|
||||||
(let [per-format-coercers (some->> (for [[format schema] content
|
(let [format->coercer (some->> (concat (when body
|
||||||
:when schema]
|
[[:default (-response-coercer coercion body)]])
|
||||||
[format (-response-coercer coercion schema)])
|
(for [[format {:keys [schema]}] content, :when schema]
|
||||||
(filter second)
|
[format (-response-coercer coercion schema)]))
|
||||||
(seq)
|
(filter second) (seq) (into (array-map)))]
|
||||||
(into {}))
|
(when format->coercer
|
||||||
default (when body (-response-coercer coercion body))]
|
|
||||||
(when (or per-format-coercers default)
|
|
||||||
(fn [request response]
|
(fn [request response]
|
||||||
(let [format (extract-response-format request response)
|
(let [format (extract-response-format request response)
|
||||||
value (:body response)
|
value (:body response)
|
||||||
coercer (get per-format-coercers format (or default -identity-coercer))
|
coercer (or (format->coercer format)
|
||||||
|
(format->coercer :default)
|
||||||
|
-identity-coercer)
|
||||||
result (coercer value format)]
|
result (coercer value format)]
|
||||||
(if (error? result)
|
(if (error? result)
|
||||||
(response-coercion-failed! result coercion value request response serialize-failed-result)
|
(response-coercion-failed! result coercion value request response serialize-failed-result)
|
||||||
|
|
@ -153,10 +171,15 @@
|
||||||
(impl/fast-assoc response :body (coercer request response))
|
(impl/fast-assoc response :body (coercer request response))
|
||||||
response)))
|
response)))
|
||||||
|
|
||||||
(defn request-coercers [coercion parameters opts]
|
(defn request-coercers
|
||||||
|
([coercion parameters opts]
|
||||||
(some->> (for [[k v] parameters, :when v]
|
(some->> (for [[k v] parameters, :when v]
|
||||||
[k (request-coercer coercion k v opts)])
|
[k (request-coercer coercion k v opts)])
|
||||||
(filter second) (seq) (into {})))
|
(filter second) (seq) (into {})))
|
||||||
|
([coercion parameters route-request opts]
|
||||||
|
(let [crc (when route-request (some->> (content-request-coercer coercion route-request opts) (array-map :request)))
|
||||||
|
rcs (request-coercers coercion parameters (cond-> opts route-request (assoc ::skip #{:body})))]
|
||||||
|
(if (and crc rcs) (into crc (vec rcs)) (or crc rcs)))))
|
||||||
|
|
||||||
(defn response-coercers [coercion responses opts]
|
(defn response-coercers [coercion responses opts]
|
||||||
(some->> (for [[status model] responses]
|
(some->> (for [[status model] responses]
|
||||||
|
|
@ -170,8 +193,8 @@
|
||||||
;; api-docs
|
;; api-docs
|
||||||
;;
|
;;
|
||||||
|
|
||||||
(defn -warn-unsupported-coercions [{:keys [parameters responses] :as _data}]
|
(defn -warn-unsupported-coercions [{:keys [request responses] :as _data}]
|
||||||
(when (:request parameters)
|
(when request
|
||||||
(println "WARNING [reitit.coercion]: swagger apidocs don't support :request coercion"))
|
(println "WARNING [reitit.coercion]: swagger apidocs don't support :request coercion"))
|
||||||
(when (some :content (vals responses))
|
(when (some :content (vals responses))
|
||||||
(println "WARNING [reitit.coercion]: swagger apidocs don't support :responses :content coercion")))
|
(println "WARNING [reitit.coercion]: swagger apidocs don't support :responses :content coercion")))
|
||||||
|
|
@ -197,7 +220,6 @@
|
||||||
(into {}))))
|
(into {}))))
|
||||||
(-get-apidocs coercion specification))))))
|
(-get-apidocs coercion specification))))))
|
||||||
|
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; integration
|
;; integration
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,11 @@
|
||||||
|
|
||||||
(s/def :reitit.core.coercion/model any?)
|
(s/def :reitit.core.coercion/model any?)
|
||||||
|
|
||||||
|
(s/def :reitit.core.coercion/schema any?)
|
||||||
|
(s/def :reitit.core.coercion/map-model (s/keys :opt-un [:reitit.core.coercion/schema]))
|
||||||
|
|
||||||
(s/def :reitit.core.coercion/content
|
(s/def :reitit.core.coercion/content
|
||||||
(s/map-of string? :reitit.core.coercion/model))
|
(s/map-of (s/or :string string?, :default #{:default}) :reitit.core.coercion/map-model))
|
||||||
|
|
||||||
(s/def :reitit.core.coercion/query :reitit.core.coercion/model)
|
(s/def :reitit.core.coercion/query :reitit.core.coercion/model)
|
||||||
(s/def :reitit.core.coercion/body :reitit.core.coercion/model)
|
(s/def :reitit.core.coercion/body :reitit.core.coercion/model)
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,15 @@
|
||||||
[]
|
[]
|
||||||
{:name ::coerce-request
|
{:name ::coerce-request
|
||||||
:spec ::rs/parameters
|
:spec ::rs/parameters
|
||||||
:compile (fn [{:keys [coercion parameters]} opts]
|
:compile (fn [{:keys [coercion parameters request]} opts]
|
||||||
(cond
|
(cond
|
||||||
;; no coercion, skip
|
;; no coercion, skip
|
||||||
(not coercion) nil
|
(not coercion) nil
|
||||||
;; just coercion, don't mount
|
;; just coercion, don't mount
|
||||||
(not parameters) {}
|
(not (or parameters request)) {}
|
||||||
;; mount
|
;; mount
|
||||||
:else
|
:else
|
||||||
(if-let [coercers (coercion/request-coercers coercion parameters opts)]
|
(if-let [coercers (coercion/request-coercers coercion parameters request opts)]
|
||||||
{:enter (fn [ctx]
|
{:enter (fn [ctx]
|
||||||
(let [request (:request ctx)
|
(let [request (:request ctx)
|
||||||
coerced (coercion/coerce-request coercers request)
|
coerced (coercion/coerce-request coercers request)
|
||||||
|
|
|
||||||
|
|
@ -133,13 +133,22 @@
|
||||||
;; malli options
|
;; malli options
|
||||||
:options nil})
|
:options nil})
|
||||||
|
|
||||||
|
;; TODO: this is now seems like a generic transforming function that could be used in all of malli, spec, schema
|
||||||
|
;; ... just tranform the schemas in place
|
||||||
|
;; also, this has internally massive amount of duplicate code, could be simplified
|
||||||
|
;; ... tests too
|
||||||
(defn -get-apidocs-openapi
|
(defn -get-apidocs-openapi
|
||||||
[coercion {:keys [parameters responses content-types] :or {content-types ["application/json"]}} options]
|
[_ {:keys [request parameters responses content-types] :or {content-types ["application/json"]}} options]
|
||||||
(let [{:keys [body request multipart]} parameters
|
(let [{:keys [body multipart]} parameters
|
||||||
parameters (dissoc parameters :request :body :multipart)
|
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 schema current-opts)))]
|
(json-schema/transform schema current-opts)))
|
||||||
|
->content (fn [data schema]
|
||||||
|
(merge
|
||||||
|
{:schema schema}
|
||||||
|
(select-keys data [:description :examples])
|
||||||
|
(:openapi data)))]
|
||||||
(merge
|
(merge
|
||||||
(when (seq parameters)
|
(when (seq parameters)
|
||||||
{:parameters
|
{:parameters
|
||||||
|
|
@ -168,20 +177,21 @@
|
||||||
;; request allow to different :requestBody per content-type
|
;; request allow to different :requestBody per content-type
|
||||||
{:requestBody
|
{:requestBody
|
||||||
{:content (merge
|
{:content (merge
|
||||||
(when (:body request)
|
(select-keys request [:description])
|
||||||
|
(when-let [{:keys [schema] :as data} (coercion/get-default request)]
|
||||||
(into {}
|
(into {}
|
||||||
(map (fn [content-type]
|
(map (fn [content-type]
|
||||||
(let [schema (->schema-object (:body request) {:in :requestBody
|
(let [schema (->schema-object schema {:in :requestBody
|
||||||
:type :schema
|
:type :schema
|
||||||
:content-type content-type})]
|
:content-type content-type})]
|
||||||
[content-type {:schema schema}])))
|
[content-type (->content data schema)])))
|
||||||
content-types))
|
content-types))
|
||||||
(into {}
|
(into {}
|
||||||
(map (fn [[content-type requestBody]]
|
(map (fn [[content-type {:keys [schema] :as data}]]
|
||||||
(let [schema (->schema-object requestBody {:in :requestBody
|
(let [schema (->schema-object schema {:in :requestBody
|
||||||
:type :schema
|
:type :schema
|
||||||
:content-type content-type})]
|
:content-type content-type})]
|
||||||
[content-type {:schema schema}])))
|
[content-type (->content data schema)])))
|
||||||
(:content request)))}})
|
(:content request)))}})
|
||||||
(when multipart
|
(when multipart
|
||||||
{:requestBody
|
{:requestBody
|
||||||
|
|
@ -194,29 +204,30 @@
|
||||||
(when responses
|
(when responses
|
||||||
{:responses
|
{:responses
|
||||||
(into {}
|
(into {}
|
||||||
(map (fn [[status {:keys [body content]
|
(map (fn [[status {:keys [content], :as response}]]
|
||||||
:as response}]]
|
(let [default (coercion/get-default-schema response)
|
||||||
(let [content (merge
|
content (-> (merge
|
||||||
(when body
|
(when default
|
||||||
(into {}
|
(into {}
|
||||||
(map (fn [content-type]
|
(map (fn [content-type]
|
||||||
(let [schema (->schema-object body {:in :responses
|
(let [schema (->schema-object default {:in :responses
|
||||||
:type :schema
|
:type :schema
|
||||||
:content-type content-type})]
|
:content-type content-type})]
|
||||||
[content-type {:schema schema}])))
|
[content-type (->content nil schema)])))
|
||||||
content-types))
|
content-types))
|
||||||
(when content
|
(when content
|
||||||
(into {}
|
(into {}
|
||||||
(map (fn [[content-type schema]]
|
(map (fn [[content-type {:keys [schema] :as data}]]
|
||||||
(let [schema (->schema-object schema {:in :responses
|
(let [schema (->schema-object schema {:in :responses
|
||||||
:type :schema
|
:type :schema
|
||||||
:content-type content-type})]
|
:content-type content-type})]
|
||||||
[content-type {:schema schema}])))
|
[content-type (->content data schema)])))
|
||||||
content)))]
|
content)))
|
||||||
|
(dissoc :default))]
|
||||||
[status (merge (select-keys response [:description])
|
[status (merge (select-keys response [:description])
|
||||||
(when content
|
(when content
|
||||||
{:content content}))])))
|
{:content content}))]))
|
||||||
responses)}))))
|
responses))}))))
|
||||||
|
|
||||||
(defn create
|
(defn create
|
||||||
([]
|
([]
|
||||||
|
|
|
||||||
|
|
@ -32,19 +32,25 @@
|
||||||
(defn -update-paths [f]
|
(defn -update-paths [f]
|
||||||
(let [not-request? #(not= :request %)
|
(let [not-request? #(not= :request %)
|
||||||
http-method? #(contains? http-methods %)]
|
http-method? #(contains? http-methods %)]
|
||||||
[;; default parameters and responses
|
[;; default parameters
|
||||||
[[:parameters not-request?] f]
|
[[:parameters not-request?] f]
|
||||||
[[http-method? :parameters not-request?] f]
|
[[http-method? :parameters not-request?] f]
|
||||||
|
|
||||||
|
;; default responses
|
||||||
[[:responses any? :body] f]
|
[[:responses any? :body] f]
|
||||||
[[http-method? :responses any? :body] f]
|
[[http-method? :responses any? :body] f]
|
||||||
|
|
||||||
;; openapi3 parameters and responses
|
;; openapi3 request
|
||||||
[[:parameters :request :content any?] f]
|
[[:request :content any? :schema] f]
|
||||||
[[http-method? :parameters :request :content any?] f]
|
[[http-method? :request :content any? :schema] f]
|
||||||
[[:parameters :request :body] f]
|
|
||||||
[[http-method? :parameters :request :body] f]
|
;; openapi3 LEGACY body
|
||||||
[[:responses any? :content any?] f]
|
[[:request :body] f]
|
||||||
[[http-method? :responses any? :content any?] f]]))
|
[[http-method? :request :body] f]
|
||||||
|
|
||||||
|
;; openapi3 responses
|
||||||
|
[[:responses any? :content any? :schema] f]
|
||||||
|
[[http-method? :responses any? :content any? :schema] f]]))
|
||||||
|
|
||||||
(defn -compile-coercion [{:keys [coercion] :as data}]
|
(defn -compile-coercion [{:keys [coercion] :as data}]
|
||||||
(cond-> data coercion (impl/path-update (-update-paths #(coercion/-compile-model coercion % nil)))))
|
(cond-> data coercion (impl/path-update (-update-paths #(coercion/-compile-model coercion % nil)))))
|
||||||
|
|
|
||||||
|
|
@ -24,15 +24,15 @@
|
||||||
and :parameters from route data, otherwise does not mount."
|
and :parameters from route data, otherwise does not mount."
|
||||||
{:name ::coerce-request
|
{:name ::coerce-request
|
||||||
:spec ::rs/parameters
|
:spec ::rs/parameters
|
||||||
:compile (fn [{:keys [coercion parameters]} opts]
|
:compile (fn [{:keys [coercion parameters request]} opts]
|
||||||
(cond
|
(cond
|
||||||
;; no coercion, skip
|
;; no coercion, skip
|
||||||
(not coercion) nil
|
(not coercion) nil
|
||||||
;; just coercion, don't mount
|
;; just coercion, don't mount
|
||||||
(not parameters) {}
|
(not (or parameters request)) {}
|
||||||
;; mount
|
;; mount
|
||||||
:else
|
:else
|
||||||
(if-let [coercers (coercion/request-coercers coercion parameters opts)]
|
(if-let [coercers (coercion/request-coercers coercion parameters request opts)]
|
||||||
(fn [handler]
|
(fn [handler]
|
||||||
(fn
|
(fn
|
||||||
([request]
|
([request]
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
(reify coercion/Coercion
|
(reify coercion/Coercion
|
||||||
(-get-name [_] :schema)
|
(-get-name [_] :schema)
|
||||||
(-get-options [_] opts)
|
(-get-options [_] opts)
|
||||||
(-get-apidocs [this specification {:keys [parameters responses content-types]
|
(-get-apidocs [_ specification {:keys [request parameters responses content-types]
|
||||||
:or {content-types ["application/json"]}}]
|
:or {content-types ["application/json"]}}]
|
||||||
;; TODO: this looks identical to spec, refactor when schema is done.
|
;; TODO: this looks identical to spec, refactor when schema is done.
|
||||||
(case specification
|
(case specification
|
||||||
|
|
@ -67,12 +67,14 @@
|
||||||
(when (:body parameters)
|
(when (:body parameters)
|
||||||
{:requestBody (openapi/openapi-spec
|
{:requestBody (openapi/openapi-spec
|
||||||
{::openapi/content (zipmap content-types (repeat (:body parameters)))})})
|
{::openapi/content (zipmap content-types (repeat (:body parameters)))})})
|
||||||
(when (:request parameters)
|
(when request
|
||||||
{:requestBody (openapi/openapi-spec
|
{:requestBody (openapi/openapi-spec
|
||||||
{::openapi/content (merge
|
{::openapi/content (merge
|
||||||
(when-let [default (get-in parameters [:request :body])]
|
(when-let [default (coercion/get-default-schema request)]
|
||||||
(zipmap content-types (repeat default)))
|
(zipmap content-types (repeat default)))
|
||||||
(:content (:request parameters)))})})
|
(->> (for [[content-type {:keys [schema]}] (:content request)]
|
||||||
|
[content-type schema])
|
||||||
|
(into {})))})})
|
||||||
(when (:multipart parameters)
|
(when (:multipart parameters)
|
||||||
{:requestBody
|
{:requestBody
|
||||||
(openapi/openapi-spec
|
(openapi/openapi-spec
|
||||||
|
|
@ -81,16 +83,19 @@
|
||||||
{:responses
|
{:responses
|
||||||
(into
|
(into
|
||||||
(empty responses)
|
(empty responses)
|
||||||
(for [[k {:keys [body content] :as response}] responses]
|
(for [[k {:keys [content] :as response}] responses
|
||||||
|
:let [default (coercion/get-default-schema response)]]
|
||||||
[k (merge
|
[k (merge
|
||||||
(select-keys response [:description])
|
(select-keys response [:description])
|
||||||
(when (or body content)
|
(when (or content default)
|
||||||
(openapi/openapi-spec
|
(openapi/openapi-spec
|
||||||
{::openapi/content (merge
|
{::openapi/content (-> (merge
|
||||||
(when body
|
(when default
|
||||||
(zipmap content-types (repeat body)))
|
(zipmap content-types (repeat default)))
|
||||||
(when response
|
(->> (for [[content-type {:keys [schema]}] content]
|
||||||
(:content response)))})))]))}))
|
[content-type schema])
|
||||||
|
(into {})))
|
||||||
|
(dissoc :default))})))]))}))
|
||||||
|
|
||||||
(throw
|
(throw
|
||||||
(ex-info
|
(ex-info
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@
|
||||||
(reify coercion/Coercion
|
(reify coercion/Coercion
|
||||||
(-get-name [_] :spec)
|
(-get-name [_] :spec)
|
||||||
(-get-options [_] opts)
|
(-get-options [_] opts)
|
||||||
(-get-apidocs [this specification {:keys [parameters responses content-types]
|
(-get-apidocs [_ specification {:keys [request parameters responses content-types]
|
||||||
:or {content-types ["application/json"]}}]
|
:or {content-types ["application/json"]}}]
|
||||||
(case specification
|
(case specification
|
||||||
:swagger (swagger/swagger-spec
|
:swagger (swagger/swagger-spec
|
||||||
|
|
@ -108,12 +108,14 @@
|
||||||
(when (:body parameters)
|
(when (:body parameters)
|
||||||
{:requestBody (openapi/openapi-spec
|
{:requestBody (openapi/openapi-spec
|
||||||
{::openapi/content (zipmap content-types (repeat (:body parameters)))})})
|
{::openapi/content (zipmap content-types (repeat (:body parameters)))})})
|
||||||
(when (:request parameters)
|
(when request
|
||||||
{:requestBody (openapi/openapi-spec
|
{:requestBody (openapi/openapi-spec
|
||||||
{::openapi/content (merge
|
{::openapi/content (merge
|
||||||
(when-let [default (get-in parameters [:request :body])]
|
(when-let [default (coercion/get-default-schema request)]
|
||||||
(zipmap content-types (repeat default)))
|
(zipmap content-types (repeat default)))
|
||||||
(:content (:request parameters)))})})
|
(->> (for [[content-type {:keys [schema]}] (:content request)]
|
||||||
|
[content-type schema])
|
||||||
|
(into {})))})})
|
||||||
(when (:multipart parameters)
|
(when (:multipart parameters)
|
||||||
{:requestBody
|
{:requestBody
|
||||||
(openapi/openapi-spec
|
(openapi/openapi-spec
|
||||||
|
|
@ -122,16 +124,20 @@
|
||||||
{:responses
|
{:responses
|
||||||
(into
|
(into
|
||||||
(empty responses)
|
(empty responses)
|
||||||
(for [[k {:keys [body content] :as response}] responses]
|
(for [[k {:keys [content] :as response}] responses
|
||||||
|
:let [default (coercion/get-default-schema response)
|
||||||
|
content-types (remove #{:default} content-types)]]
|
||||||
[k (merge
|
[k (merge
|
||||||
(select-keys response [:description])
|
(select-keys response [:description])
|
||||||
(when (or body content)
|
(when (or content default)
|
||||||
(openapi/openapi-spec
|
(openapi/openapi-spec
|
||||||
{::openapi/content (merge
|
{::openapi/content (-> (merge
|
||||||
(when body
|
(when default
|
||||||
(zipmap content-types (repeat (:body response))))
|
(zipmap content-types (repeat default)))
|
||||||
(when response
|
(->> (for [[content-type {:keys [schema]}] content]
|
||||||
(:content response)))})))]))}))
|
[content-type schema])
|
||||||
|
(into {})))
|
||||||
|
(dissoc :default))})))]))}))
|
||||||
(throw
|
(throw
|
||||||
(ex-info
|
(ex-info
|
||||||
(str "Can't produce Spec apidocs for " specification)
|
(str "Can't produce Spec apidocs for " specification)
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@
|
||||||
[ikitommi/immutant-web "3.0.0-alpha1"]
|
[ikitommi/immutant-web "3.0.0-alpha1"]
|
||||||
[metosin/ring-http-response "0.9.3"]
|
[metosin/ring-http-response "0.9.3"]
|
||||||
[metosin/ring-swagger-ui "4.18.1"]
|
[metosin/ring-swagger-ui "4.18.1"]
|
||||||
|
[org.clojure/tools.analyzer "1.1.1"]
|
||||||
|
|
||||||
[criterium "0.4.6"]
|
[criterium "0.4.6"]
|
||||||
[org.clojure/test.check "1.1.1"]
|
[org.clojure/test.check "1.1.1"]
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,10 @@
|
||||||
:description "kosh"}}}
|
:description "kosh"}}}
|
||||||
:responses {200 {:description "success"
|
:responses {200 {:description "success"
|
||||||
:body {:total int?}}
|
:body {:total int?}}
|
||||||
500 {:description "fail"}}
|
500 {:description "fail"}
|
||||||
|
504 {:description "default"
|
||||||
|
:content {:default {:schema {:error string?}}}
|
||||||
|
:body {:masked string?}}}
|
||||||
:handler (fn [{{{:keys [z]} :path
|
:handler (fn [{{{:keys [z]} :path
|
||||||
xs :body} :parameters}]
|
xs :body} :parameters}]
|
||||||
{:status 200, :body {:total (+ (reduce + xs) z)}})}}]]
|
{:status 200, :body {:total (+ (reduce + xs) z)}})}}]]
|
||||||
|
|
@ -91,7 +94,10 @@
|
||||||
:content {"application/json" {:schema {:type "string"}}}}}}
|
:content {"application/json" {:schema {:type "string"}}}}}}
|
||||||
:responses {200 {:description "success"
|
:responses {200 {:description "success"
|
||||||
:body [:map [:total int?]]}
|
:body [:map [:total int?]]}
|
||||||
500 {:description "fail"}}
|
500 {:description "fail"}
|
||||||
|
504 {:description "default"
|
||||||
|
:content {:default {:schema {:error string?}}}
|
||||||
|
:body {:masked string?}}}
|
||||||
:handler (fn [{{{:keys [z]} :path
|
:handler (fn [{{{:keys [z]} :path
|
||||||
xs :body} :parameters}]
|
xs :body} :parameters}]
|
||||||
{:status 200, :body {:total (+ (reduce + xs) z)}})}}]]
|
{:status 200, :body {:total (+ (reduce + xs) z)}})}}]]
|
||||||
|
|
@ -117,7 +123,10 @@
|
||||||
:description "kosh"}}}
|
:description "kosh"}}}
|
||||||
:responses {200 {:description "success"
|
:responses {200 {:description "success"
|
||||||
:body {:total s/Int}}
|
:body {:total s/Int}}
|
||||||
500 {:description "fail"}}
|
500 {:description "fail"}
|
||||||
|
504 {:description "default"
|
||||||
|
:content {:default {:schema {:error s/Str}}}
|
||||||
|
:body {:masked s/Str}}}
|
||||||
:handler (fn [{{{:keys [z]} :path
|
:handler (fn [{{{:keys [z]} :path
|
||||||
xs :body} :parameters}]
|
xs :body} :parameters}]
|
||||||
{:status 200, :body {:total (+ (reduce + xs) z)}})}}]]]
|
{:status 200, :body {:total (+ (reduce + xs) z)}})}}]]]
|
||||||
|
|
@ -193,7 +202,11 @@
|
||||||
:type "object"}}}}
|
:type "object"}}}}
|
||||||
400 {:content {"application/json" {:schema {:type "string"}}}
|
400 {:content {"application/json" {:schema {:type "string"}}}
|
||||||
:description "kosh"}
|
:description "kosh"}
|
||||||
500 {:description "fail"}}
|
500 {:description "fail"}
|
||||||
|
504 {:description "default"
|
||||||
|
:content {"application/json" {:schema {:properties {"error" {:type "string"}}
|
||||||
|
:required ["error"]
|
||||||
|
:type "object"}}}}}
|
||||||
:summary "plus with body"}}
|
:summary "plus with body"}}
|
||||||
"/api/malli/plus/{z}" {:get {:parameters [{:in "query"
|
"/api/malli/plus/{z}" {:get {:parameters [{:in "query"
|
||||||
:name :x
|
:name :x
|
||||||
|
|
@ -231,7 +244,12 @@
|
||||||
:type "object"}}}}
|
:type "object"}}}}
|
||||||
400 {:description "kosh"
|
400 {:description "kosh"
|
||||||
:content {"application/json" {:schema {:type "string"}}}}
|
:content {"application/json" {:schema {:type "string"}}}}
|
||||||
500 {:description "fail"}}
|
500 {:description "fail"}
|
||||||
|
504 {:description "default"
|
||||||
|
:content {"application/json" {:schema {:additionalProperties false
|
||||||
|
:properties {:error {:type "string"}}
|
||||||
|
:required [:error]
|
||||||
|
:type "object"}}}}}
|
||||||
:summary "plus with body"}}
|
:summary "plus with body"}}
|
||||||
"/api/schema/plus/{z}" {:get {:parameters [{:description ""
|
"/api/schema/plus/{z}" {:get {:parameters [{:description ""
|
||||||
:in "query"
|
:in "query"
|
||||||
|
|
@ -280,10 +298,15 @@
|
||||||
:type "object"}}}}
|
:type "object"}}}}
|
||||||
400 {:description "kosh"
|
400 {:description "kosh"
|
||||||
:content {"application/json" {:schema {:type "string"}}}}
|
:content {"application/json" {:schema {:type "string"}}}}
|
||||||
500 {:description "fail"}}
|
500 {:description "fail"}
|
||||||
|
504 {:description "default"
|
||||||
|
:content {"application/json" {:schema {:additionalProperties false
|
||||||
|
:properties {"error" {:type "string"}}
|
||||||
|
:required ["error"]
|
||||||
|
:type "object"}}}}}
|
||||||
:summary "plus with body"}}}}]
|
:summary "plus with body"}}}}]
|
||||||
(is (= expected spec))
|
(is (= expected spec))
|
||||||
(is (nil? (validate spec))))))
|
(is (= nil (validate spec))))))
|
||||||
|
|
||||||
(defn spec-paths [app uri]
|
(defn spec-paths [app uri]
|
||||||
(-> {:request-method :get, :uri uri} app :body :paths keys))
|
(-> {:request-method :get, :uri uri} app :body :paths keys))
|
||||||
|
|
@ -457,8 +480,8 @@
|
||||||
[["/examples"
|
[["/examples"
|
||||||
{:post {:decription "examples"
|
{:post {:decription "examples"
|
||||||
:coercion @coercion
|
:coercion @coercion
|
||||||
:parameters {:query (->schema :q)
|
:request {:body (->schema :b)}
|
||||||
:request {:body (->schema :b)}}
|
:parameters {:query (->schema :q)}
|
||||||
:responses {200 {:description "success"
|
:responses {200 {:description "success"
|
||||||
:body (->schema :ok)}}
|
:body (->schema :ok)}}
|
||||||
:openapi {:requestBody
|
:openapi {:requestBody
|
||||||
|
|
@ -517,8 +540,7 @@
|
||||||
(is (nil? (validate spec))))))))
|
(is (nil? (validate spec))))))))
|
||||||
|
|
||||||
(deftest multipart-test
|
(deftest multipart-test
|
||||||
(doseq [[coercion file-schema string-schema]
|
(doseq [[coercion file-schema string-schema] [[#'malli/coercion
|
||||||
[[#'malli/coercion
|
|
||||||
reitit.ring.malli/bytes-part
|
reitit.ring.malli/bytes-part
|
||||||
:string]
|
:string]
|
||||||
[#'schema/coercion
|
[#'schema/coercion
|
||||||
|
|
@ -565,21 +587,20 @@
|
||||||
(is (nil? (validate spec))))))))
|
(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]])]
|
[schema/coercion (fn [nom] {nom s/Str})]
|
||||||
[#'schema/coercion (fn [nom] {nom s/Str})]
|
[spec/coercion (fn [nom] {nom string?})]]]
|
||||||
[#'spec/coercion (fn [nom] {nom string?})]]]
|
|
||||||
(testing (str coercion)
|
(testing (str coercion)
|
||||||
(let [app (ring/ring-handler
|
(let [app (ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
[["/parameters"
|
[["/parameters"
|
||||||
{:post {:description "parameters"
|
{:post {:description "parameters"
|
||||||
:coercion @coercion
|
:coercion coercion
|
||||||
:parameters {:request {:content {"application/json" (->schema :b)
|
:request {:content {"application/json" {:schema (->schema :b)}
|
||||||
"application/edn" (->schema :c)}}}
|
"application/edn" {:schema (->schema :c)}}}
|
||||||
:responses {200 {:description "success"
|
:responses {200 {:description "success"
|
||||||
:content {"application/json" (->schema :ok)
|
:content {"application/json" {:schema (->schema :ok)}
|
||||||
"application/edn" (->schema :edn)}}}
|
"application/edn" {:schema (->schema :edn)}}}}
|
||||||
:handler (fn [req]
|
:handler (fn [req]
|
||||||
{:status 200
|
{:status 200
|
||||||
:body (-> req :parameters :request)})}}]
|
:body (-> req :parameters :request)})}}]
|
||||||
|
|
@ -594,37 +615,38 @@
|
||||||
spec (-> {:request-method :get
|
spec (-> {:request-method :get
|
||||||
:uri "/openapi.json"}
|
:uri "/openapi.json"}
|
||||||
app
|
app
|
||||||
:body)]
|
:body)
|
||||||
|
spec-coercion (= coercion spec/coercion)]
|
||||||
(testing "body parameter"
|
(testing "body parameter"
|
||||||
(is (match? (merge {:type "object"
|
(is (= (merge {:type "object"
|
||||||
:properties {:b {:type "string"}}
|
:properties {:b {:type "string"}}
|
||||||
:required ["b"]}
|
:required ["b"]}
|
||||||
(when-not (#{#'spec/coercion} coercion)
|
(when-not spec-coercion
|
||||||
{:additionalProperties false}))
|
{:additionalProperties false}))
|
||||||
(-> spec
|
(-> spec
|
||||||
(get-in [:paths "/parameters" :post :requestBody :content "application/json" :schema])
|
(get-in [:paths "/parameters" :post :requestBody :content "application/json" :schema])
|
||||||
normalize)))
|
normalize)))
|
||||||
(is (match? (merge {:type "object"
|
(is (= (merge {:type "object"
|
||||||
:properties {:c {:type "string"}}
|
:properties {:c {:type "string"}}
|
||||||
:required ["c"]}
|
:required ["c"]}
|
||||||
(when-not (#{#'spec/coercion} coercion)
|
(when-not spec-coercion
|
||||||
{:additionalProperties false}))
|
{:additionalProperties false}))
|
||||||
(-> spec
|
(-> spec
|
||||||
(get-in [:paths "/parameters" :post :requestBody :content "application/edn" :schema])
|
(get-in [:paths "/parameters" :post :requestBody :content "application/edn" :schema])
|
||||||
normalize))))
|
normalize))))
|
||||||
(testing "body response"
|
(testing "body response"
|
||||||
(is (match? (merge {:type "object"
|
(is (= (merge {:type "object"
|
||||||
:properties {:ok {:type "string"}}
|
:properties {:ok {:type "string"}}
|
||||||
:required ["ok"]}
|
:required ["ok"]}
|
||||||
(when-not (#{#'spec/coercion} coercion)
|
(when-not spec-coercion
|
||||||
{:additionalProperties false}))
|
{:additionalProperties false}))
|
||||||
(-> spec
|
(-> spec
|
||||||
(get-in [:paths "/parameters" :post :responses 200 :content "application/json" :schema])
|
(get-in [:paths "/parameters" :post :responses 200 :content "application/json" :schema])
|
||||||
normalize)))
|
normalize)))
|
||||||
(is (match? (merge {:type "object"
|
(is (= (merge {:type "object"
|
||||||
:properties {:edn {:type "string"}}
|
:properties {:edn {:type "string"}}
|
||||||
:required ["edn"]}
|
:required ["edn"]}
|
||||||
(when-not (#{#'spec/coercion} coercion)
|
(when-not spec-coercion
|
||||||
{:additionalProperties false}))
|
{:additionalProperties false}))
|
||||||
(-> spec
|
(-> spec
|
||||||
(get-in [:paths "/parameters" :post :responses 200 :content "application/edn" :schema])
|
(get-in [:paths "/parameters" :post :responses 200 :content "application/edn" :schema])
|
||||||
|
|
@ -653,23 +675,22 @@
|
||||||
(is (nil? (validate spec))))))))
|
(is (nil? (validate spec))))))))
|
||||||
|
|
||||||
(deftest default-content-type-test
|
(deftest default-content-type-test
|
||||||
(doseq [[coercion ->schema]
|
(doseq [[coercion ->schema] [[malli/coercion (fn [nom] [:map [nom :string]])]
|
||||||
[[#'malli/coercion (fn [nom] [:map [nom :string]])]
|
[schema/coercion (fn [nom] {nom s/Str})]
|
||||||
[#'schema/coercion (fn [nom] {nom s/Str})]
|
[spec/coercion (fn [nom] {nom string?})]]]
|
||||||
[#'spec/coercion (fn [nom] {nom string?})]]]
|
(testing (str coercion)
|
||||||
(testing coercion
|
|
||||||
(doseq [content-type ["application/json" "application/edn"]]
|
(doseq [content-type ["application/json" "application/edn"]]
|
||||||
(testing (str "default content type " content-type)
|
(testing (str "default content type " content-type)
|
||||||
(let [app (ring/ring-handler
|
(let [app (ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
[["/parameters"
|
[["/parameters"
|
||||||
{:post {:description "parameters"
|
{:post {:description "parameters"
|
||||||
:coercion @coercion
|
:coercion coercion
|
||||||
:content-types [content-type] ;; TODO should this be under :openapi ?
|
:content-types [content-type] ;; TODO should this be under :openapi ?
|
||||||
:parameters {:request {:content {"application/transit" (->schema :transit)}
|
:request {:content {"application/transit" {:schema (->schema :transit)}}
|
||||||
:body (->schema :default)}}
|
:body (->schema :default)}
|
||||||
:responses {200 {:description "success"
|
:responses {200 {:description "success"
|
||||||
:content {"application/transit" (->schema :transit)}
|
:content {"application/transit" {:schema (->schema :transit)}}
|
||||||
:body (->schema :default)}}
|
:body (->schema :default)}}
|
||||||
:handler (fn [req]
|
:handler (fn [req]
|
||||||
{:status 200
|
{:status 200
|
||||||
|
|
@ -707,8 +728,7 @@
|
||||||
[["/parameters"
|
[["/parameters"
|
||||||
{:post {:description "parameters"
|
{:post {:description "parameters"
|
||||||
:coercion malli/coercion
|
:coercion malli/coercion
|
||||||
:parameters {:request
|
:request {:body
|
||||||
{:body
|
|
||||||
[:schema
|
[:schema
|
||||||
{:registry {"friend" [:map
|
{:registry {"friend" [:map
|
||||||
[:age int?]
|
[:age int?]
|
||||||
|
|
@ -716,7 +736,7 @@
|
||||||
"pet" [:map
|
"pet" [:map
|
||||||
[:name :string]
|
[:name :string]
|
||||||
[:friends [:vector [:ref "friend"]]]]}}
|
[:friends [:vector [:ref "friend"]]]]}}
|
||||||
"friend"]}}
|
"friend"]}
|
||||||
:handler (fn [req]
|
:handler (fn [req]
|
||||||
{:status 200
|
{:status 200
|
||||||
:body (-> req :parameters :request)})}}]
|
:body (-> req :parameters :request)})}}]
|
||||||
|
|
@ -754,3 +774,53 @@
|
||||||
spec))
|
spec))
|
||||||
(testing "spec is valid"
|
(testing "spec is valid"
|
||||||
(is (nil? (validate spec))))))
|
(is (nil? (validate spec))))))
|
||||||
|
|
||||||
|
(deftest openapi-malli-tests
|
||||||
|
(let [app (ring/ring-handler
|
||||||
|
(ring/router
|
||||||
|
[["/openapi.json"
|
||||||
|
{:get {:no-doc true
|
||||||
|
:handler (openapi/create-openapi-handler)}}]
|
||||||
|
|
||||||
|
["/malli" {:coercion malli/coercion}
|
||||||
|
["/plus" {:post {:summary "plus with body"
|
||||||
|
:request {:description "body description"
|
||||||
|
:content {"application/json" {:schema {:x int?, :y int?}
|
||||||
|
:examples {"1+1" {:x 1, :y 1}
|
||||||
|
"1+2" {:x 1, :y 2}}
|
||||||
|
:openapi {:example {:x 2, :y 2}}}}}
|
||||||
|
:responses {200 {:description "success"
|
||||||
|
:content {"application/json" {:schema {:total int?}
|
||||||
|
:examples {"2" {:total 2}
|
||||||
|
"3" {:total 3}}
|
||||||
|
:openapi {:example {:total 4}}}}}}
|
||||||
|
:handler (fn [request]
|
||||||
|
(let [{:keys [x y]} (-> request :parameters :body)]
|
||||||
|
{:status 200, :body {:total (+ x y)}}))}}]]]
|
||||||
|
|
||||||
|
{:validate reitit.ring.spec/validate
|
||||||
|
:data {:middleware [openapi/openapi-feature
|
||||||
|
rrc/coerce-exceptions-middleware
|
||||||
|
rrc/coerce-request-middleware
|
||||||
|
rrc/coerce-response-middleware]}}))]
|
||||||
|
(is (= {"/malli/plus" {:post {:requestBody {:content {:description "body description",
|
||||||
|
"application/json" {:schema {:type "object",
|
||||||
|
:properties {:x {:type "integer"},
|
||||||
|
:y {:type "integer"}},
|
||||||
|
:required [:x :y],
|
||||||
|
:additionalProperties false},
|
||||||
|
:examples {"1+1" {:x 1, :y 1}, "1+2" {:x 1, :y 2}},
|
||||||
|
:example {:x 2, :y 2}}}},
|
||||||
|
:responses {200 {:description "success",
|
||||||
|
:content {"application/json" {:schema {:type "object",
|
||||||
|
:properties {:total {:type "integer"}},
|
||||||
|
:required [:total],
|
||||||
|
:additionalProperties false},
|
||||||
|
:examples {"2" {:total 2}, "3" {:total 3}},
|
||||||
|
:example {:total 4}}}}},
|
||||||
|
:summary "plus with body"}}})
|
||||||
|
(-> {:request-method :get
|
||||||
|
:uri "/openapi.json"}
|
||||||
|
(app)
|
||||||
|
:body
|
||||||
|
:paths))))
|
||||||
|
|
|
||||||
|
|
@ -606,22 +606,43 @@
|
||||||
{:request any? :response (clojure.spec.alpha/spec #{:end})}
|
{:request any? :response (clojure.spec.alpha/spec #{:end})}
|
||||||
{:request any? :response (clojure.spec.alpha/spec #{:default})}]]]
|
{:request any? :response (clojure.spec.alpha/spec #{:default})}]]]
|
||||||
(testing (str coercion)
|
(testing (str coercion)
|
||||||
(let [app (ring/ring-handler
|
(doseq [{:keys [name app]}
|
||||||
|
[{:name "using top-level :body"
|
||||||
|
:app (ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
["/foo" {:post {:parameters {:request {:content {"application/json" json-request
|
["/foo" {:post {:request {:content {"application/json" {:schema json-request}
|
||||||
"application/edn" edn-request}
|
"application/edn" {:schema edn-request}}
|
||||||
:body default-request}}
|
:body default-request}
|
||||||
:responses {200 {:content {"application/json" json-response
|
:responses {200 {:content {"application/json" {:schema json-response}
|
||||||
"application/edn" edn-response}
|
"application/edn" {:schema edn-response}}
|
||||||
:body default-response}}
|
:body default-response}}
|
||||||
:handler (fn [req]
|
:handler (fn [req]
|
||||||
{:status 200
|
{:status 200
|
||||||
:body (-> req :parameters :request)})}}]
|
:body (-> req :parameters :request)})}}]
|
||||||
{#_#_:validate reitit.ring.spec/validate
|
{:validate reitit.ring.spec/validate
|
||||||
:data {:middleware [rrc/coerce-request-middleware
|
:data {:middleware [rrc/coerce-request-middleware
|
||||||
rrc/coerce-response-middleware]
|
rrc/coerce-response-middleware]
|
||||||
:coercion coercion}}))
|
:coercion coercion}}))}
|
||||||
call (fn [request]
|
{:name "using :default content"
|
||||||
|
:app (ring/ring-handler
|
||||||
|
(ring/router
|
||||||
|
["/foo" {:post {:request {:content {"application/json" {:schema json-request}
|
||||||
|
"application/edn" {:schema edn-request}
|
||||||
|
:default {:schema default-request}}
|
||||||
|
:body json-request} ;; not applied as :default exists
|
||||||
|
:responses {200 {:content {"application/json" {:schema json-response}
|
||||||
|
"application/edn" {:schema edn-response}
|
||||||
|
:default {:schema default-response}}
|
||||||
|
:body json-response}} ;; not applied as :default exists
|
||||||
|
:handler (fn [req]
|
||||||
|
{:status 200
|
||||||
|
:body (-> req :parameters :request)})}}]
|
||||||
|
{:validate reitit.ring.spec/validate
|
||||||
|
:data {:middleware [rrc/coerce-request-middleware
|
||||||
|
rrc/coerce-response-middleware]
|
||||||
|
:coercion coercion}}))}]]
|
||||||
|
(testing name
|
||||||
|
(let [call (fn [request]
|
||||||
(try
|
(try
|
||||||
(app request)
|
(app request)
|
||||||
(catch ExceptionInfo e
|
(catch ExceptionInfo e
|
||||||
|
|
@ -652,7 +673,7 @@
|
||||||
(is (= {:type :reitit.coercion/response-coercion :in [:response :body]}
|
(is (= {:type :reitit.coercion/response-coercion :in [:response :body]}
|
||||||
(call (request "application/json" "application/edn" {:request :json :response :json}))))
|
(call (request "application/json" "application/edn" {:request :json :response :json}))))
|
||||||
(is (= {:type :reitit.coercion/response-coercion :in [:response :body]}
|
(is (= {:type :reitit.coercion/response-coercion :in [:response :body]}
|
||||||
(call (request "application/json" "application/transit" {:request :json :response :json})))))))))
|
(call (request "application/json" "application/transit" {:request :json :response :json})))))))))))
|
||||||
|
|
||||||
|
|
||||||
#?(:clj
|
#?(:clj
|
||||||
|
|
|
||||||
|
|
@ -401,7 +401,7 @@
|
||||||
(ring/router
|
(ring/router
|
||||||
[["/parameters"
|
[["/parameters"
|
||||||
{:post {:coercion spec/coercion
|
{:post {:coercion spec/coercion
|
||||||
:parameters {:request {:content {"application/json" {:x string?}}}}
|
:request {:content {"application/json" {:x string?}}}
|
||||||
:handler identity}}]
|
:handler identity}}]
|
||||||
["/swagger.json"
|
["/swagger.json"
|
||||||
{:get {:no-doc true
|
{:get {:no-doc true
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue