mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 08:51:12 +00:00
wrap :content schemas in :schema
This commit is contained in:
parent
499f84be21
commit
d17c97780e
8 changed files with 114 additions and 104 deletions
|
|
@ -91,9 +91,9 @@
|
||||||
(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)
|
format-schema-pairs (if (= :request style)
|
||||||
(conj (:content model) [:default (:body model)])
|
(conj (:content model) [:default {:schema (:body model)}])
|
||||||
[[:default model]])
|
[[:default {:schema model}]])
|
||||||
format->coercer (some->> (for [[format schema] format-schema-pairs
|
format->coercer (some->> (for [[format {:keys [schema]}] format-schema-pairs
|
||||||
:when schema
|
:when schema
|
||||||
:let [type (case style :request :body style)]]
|
:let [type (case style :request :body style)]]
|
||||||
[format (-request-coercer coercion type (->open schema))])
|
[format (-request-coercer coercion type (->open schema))])
|
||||||
|
|
@ -118,7 +118,7 @@
|
||||||
(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 [per-format-coercers (some->> (for [[format {:keys [schema]}] content
|
||||||
:when schema]
|
:when schema]
|
||||||
[format (-response-coercer coercion schema)])
|
[format (-response-coercer coercion schema)])
|
||||||
(filter second)
|
(filter second)
|
||||||
|
|
|
||||||
|
|
@ -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 string? :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)
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@
|
||||||
:options nil})
|
:options nil})
|
||||||
|
|
||||||
(defn -get-apidocs-openapi
|
(defn -get-apidocs-openapi
|
||||||
[coercion {:keys [parameters responses content-types] :or {content-types ["application/json"]}} options]
|
[_ {:keys [parameters responses content-types] :or {content-types ["application/json"]}} options]
|
||||||
(let [{:keys [body request multipart]} parameters
|
(let [{:keys [body request multipart]} parameters
|
||||||
parameters (dissoc parameters :request :body :multipart)
|
parameters (dissoc parameters :request :body :multipart)
|
||||||
->schema-object (fn [schema opts]
|
->schema-object (fn [schema opts]
|
||||||
|
|
@ -177,10 +177,10 @@
|
||||||
[content-type {:schema schema}])))
|
[content-type {:schema schema}])))
|
||||||
content-types))
|
content-types))
|
||||||
(into {}
|
(into {}
|
||||||
(map (fn [[content-type requestBody]]
|
(map (fn [[content-type {:keys [schema]}]]
|
||||||
(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 {:schema schema}])))
|
||||||
(:content request)))}})
|
(:content request)))}})
|
||||||
(when multipart
|
(when multipart
|
||||||
|
|
@ -207,7 +207,7 @@
|
||||||
content-types))
|
content-types))
|
||||||
(when content
|
(when content
|
||||||
(into {}
|
(into {}
|
||||||
(map (fn [[content-type schema]]
|
(map (fn [[content-type {:keys [schema]}]]
|
||||||
(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})]
|
||||||
|
|
|
||||||
|
|
@ -39,12 +39,12 @@
|
||||||
[[http-method? :responses any? :body] f]
|
[[http-method? :responses any? :body] f]
|
||||||
|
|
||||||
;; openapi3 parameters and responses
|
;; openapi3 parameters and responses
|
||||||
[[:parameters :request :content any?] f]
|
[[:parameters :request :content any? :schema] f]
|
||||||
[[http-method? :parameters :request :content any?] f]
|
[[http-method? :parameters :request :content any? :schema] f]
|
||||||
[[:parameters :request :body] f]
|
[[:parameters :request :body] f]
|
||||||
[[http-method? :parameters :request :body] f]
|
[[http-method? :parameters :request :body] f]
|
||||||
[[:responses any? :content any?] f]
|
[[:responses any? :content any? :schema] f]
|
||||||
[[http-method? :responses any? :content any?] 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)))))
|
||||||
|
|
|
||||||
|
|
@ -47,9 +47,9 @@
|
||||||
(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 [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
|
||||||
:swagger (swagger/swagger-spec
|
:swagger (swagger/swagger-spec
|
||||||
(merge
|
(merge
|
||||||
|
|
@ -62,35 +62,38 @@
|
||||||
(for [[k response] responses]
|
(for [[k response] responses]
|
||||||
[k (set/rename-keys response {:body :schema})]))})))
|
[k (set/rename-keys response {:body :schema})]))})))
|
||||||
:openapi (merge
|
:openapi (merge
|
||||||
(when (seq (dissoc parameters :body :request :multipart))
|
(when (seq (dissoc parameters :body :request :multipart))
|
||||||
(openapi/openapi-spec {::openapi/parameters (dissoc parameters :body :request)}))
|
(openapi/openapi-spec {::openapi/parameters (dissoc parameters :body :request)}))
|
||||||
(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 parameters)
|
||||||
{: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 (get-in parameters [:request :body])]
|
||||||
(zipmap content-types (repeat default)))
|
(zipmap content-types (repeat default)))
|
||||||
(:content (:request parameters)))})})
|
(->> (for [[content-type {:keys [schema]}] (:content (:request parameters))]
|
||||||
(when (:multipart parameters)
|
[content-type schema])
|
||||||
{:requestBody
|
(into {})))})})
|
||||||
(openapi/openapi-spec
|
(when (:multipart parameters)
|
||||||
{::openapi/content {"multipart/form-data" (:multipart parameters)}})})
|
{:requestBody
|
||||||
(when responses
|
(openapi/openapi-spec
|
||||||
{:responses
|
{::openapi/content {"multipart/form-data" (:multipart parameters)}})})
|
||||||
(into
|
(when responses
|
||||||
(empty responses)
|
{:responses
|
||||||
(for [[k {:keys [body content] :as response}] responses]
|
(into
|
||||||
[k (merge
|
(empty responses)
|
||||||
(select-keys response [:description])
|
(for [[k {:keys [body content] :as response}] responses]
|
||||||
(when (or body content)
|
[k (merge
|
||||||
(openapi/openapi-spec
|
(select-keys response [:description])
|
||||||
{::openapi/content (merge
|
(when (or body content)
|
||||||
(when body
|
(openapi/openapi-spec
|
||||||
(zipmap content-types (repeat body)))
|
{::openapi/content (merge
|
||||||
(when response
|
(when body
|
||||||
(:content response)))})))]))}))
|
(zipmap content-types (repeat body)))
|
||||||
|
(->> (for [[content-type {:keys [schema]}] (:content response)]
|
||||||
|
[content-type schema])
|
||||||
|
(into {})))})))]))}))
|
||||||
|
|
||||||
(throw
|
(throw
|
||||||
(ex-info
|
(ex-info
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,9 @@
|
||||||
{::openapi/content (merge
|
{::openapi/content (merge
|
||||||
(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)))})})
|
(->> (for [[content-type {:keys [schema]}] (:content (:request parameters))]
|
||||||
|
[content-type schema])
|
||||||
|
(into {})))})})
|
||||||
(when (:multipart parameters)
|
(when (:multipart parameters)
|
||||||
{:requestBody
|
{:requestBody
|
||||||
(openapi/openapi-spec
|
(openapi/openapi-spec
|
||||||
|
|
@ -130,8 +132,9 @@
|
||||||
{::openapi/content (merge
|
{::openapi/content (merge
|
||||||
(when body
|
(when body
|
||||||
(zipmap content-types (repeat (:body response))))
|
(zipmap content-types (repeat (:body response))))
|
||||||
(when response
|
(->> (for [[content-type {:keys [schema]}] (:content response)]
|
||||||
(:content response)))})))]))}))
|
[content-type schema])
|
||||||
|
(into {})))})))]))}))
|
||||||
(throw
|
(throw
|
||||||
(ex-info
|
(ex-info
|
||||||
(str "Can't produce Spec apidocs for " specification)
|
(str "Can't produce Spec apidocs for " specification)
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@
|
||||||
{:get {:summary "plus"
|
{:get {:summary "plus"
|
||||||
:tags [:plus :schema]
|
:tags [:plus :schema]
|
||||||
:parameters {:query {:x s/Int, :y s/Int}
|
:parameters {:query {:x s/Int, :y s/Int}
|
||||||
:path {:z s/Int}}
|
:path {:z s/Int}}
|
||||||
:openapi {:responses {400 {:content {"application/json" {:schema {:type "string"}}}
|
:openapi {:responses {400 {:content {"application/json" {:schema {:type "string"}}}
|
||||||
:description "kosh"}}}
|
:description "kosh"}}}
|
||||||
:responses {200 {:description "success"
|
:responses {200 {:description "success"
|
||||||
|
|
@ -566,20 +566,20 @@
|
||||||
|
|
||||||
(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)
|
:parameters {: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,41 +594,42 @@
|
||||||
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])
|
||||||
normalize))))
|
normalize))))
|
||||||
(testing "validation"
|
(testing "validation"
|
||||||
(let [query {:request-method :post
|
(let [query {:request-method :post
|
||||||
:uri "/parameters"
|
:uri "/parameters"
|
||||||
|
|
@ -654,22 +655,22 @@
|
||||||
|
|
||||||
(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 coercion
|
(testing (str 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)}
|
:parameters {: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
|
||||||
|
|
|
||||||
|
|
@ -608,16 +608,16 @@
|
||||||
(testing (str coercion)
|
(testing (str coercion)
|
||||||
(let [app (ring/ring-handler
|
(let [app (ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
["/foo" {:post {:parameters {:request {:content {"application/json" json-request
|
["/foo" {:post {:parameters {: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}}))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue