mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 08:51:12 +00:00
refactor: share -get-apidocs-openapi between malli, spec & schema
This commit is contained in:
parent
051452231a
commit
6f111bce2e
3 changed files with 94 additions and 102 deletions
|
|
@ -133,96 +133,6 @@
|
||||||
;; malli options
|
;; malli options
|
||||||
:options nil})
|
:options nil})
|
||||||
|
|
||||||
(defn -get-apidocs-openapi
|
|
||||||
[coercion {:keys [request parameters responses content-types] :or {content-types ["application/json"]}}]
|
|
||||||
(let [{:keys [body multipart]} parameters
|
|
||||||
parameters (dissoc parameters :request :body :multipart)
|
|
||||||
->content (fn [data schema]
|
|
||||||
(merge
|
|
||||||
{:schema schema}
|
|
||||||
(select-keys data [:description :examples])
|
|
||||||
(:openapi data)))
|
|
||||||
->schema-object #(coercion/-get-model-apidocs coercion :openapi %1 %2)]
|
|
||||||
(merge
|
|
||||||
(when (seq parameters)
|
|
||||||
{:parameters
|
|
||||||
(->> (for [[in schema] parameters
|
|
||||||
:let [{:keys [properties required]} (->schema-object schema {:in in :type :parameter})
|
|
||||||
required? (partial contains? (set required))]
|
|
||||||
[k schema] properties]
|
|
||||||
(merge {:in (name in)
|
|
||||||
:name k
|
|
||||||
:required (required? k)
|
|
||||||
:schema schema}
|
|
||||||
(select-keys schema [:description])))
|
|
||||||
(into []))})
|
|
||||||
(when body
|
|
||||||
;; body uses a single schema to describe every :requestBody
|
|
||||||
;; the schema-object transformer should be able to transform into distinct content-types
|
|
||||||
{:requestBody {:content (into {}
|
|
||||||
(map (fn [content-type]
|
|
||||||
(let [schema (->schema-object body {:in :requestBody
|
|
||||||
:type :schema
|
|
||||||
:content-type content-type})]
|
|
||||||
[content-type {:schema schema}])))
|
|
||||||
content-types)}})
|
|
||||||
|
|
||||||
(when request
|
|
||||||
;; request allow to different :requestBody per content-type
|
|
||||||
{:requestBody
|
|
||||||
{:content (merge
|
|
||||||
(select-keys request [:description])
|
|
||||||
(when-let [{:keys [schema] :as data} (coercion/get-default request)]
|
|
||||||
(into {}
|
|
||||||
(map (fn [content-type]
|
|
||||||
(let [schema (->schema-object schema {:in :requestBody
|
|
||||||
:type :schema
|
|
||||||
:content-type content-type})]
|
|
||||||
[content-type (->content data schema)])))
|
|
||||||
content-types))
|
|
||||||
(into {}
|
|
||||||
(map (fn [[content-type {:keys [schema] :as data}]]
|
|
||||||
(let [schema (->schema-object schema {:in :requestBody
|
|
||||||
:type :schema
|
|
||||||
:content-type content-type})]
|
|
||||||
[content-type (->content data schema)])))
|
|
||||||
(:content request)))}})
|
|
||||||
(when multipart
|
|
||||||
{:requestBody
|
|
||||||
{:content
|
|
||||||
{"multipart/form-data"
|
|
||||||
{:schema
|
|
||||||
(->schema-object multipart {:in :requestBody
|
|
||||||
:type :schema
|
|
||||||
:content-type "multipart/form-data"})}}}})
|
|
||||||
(when responses
|
|
||||||
{:responses
|
|
||||||
(into {}
|
|
||||||
(map (fn [[status {:keys [content], :as response}]]
|
|
||||||
(let [default (coercion/get-default-schema response)
|
|
||||||
content (-> (merge
|
|
||||||
(when default
|
|
||||||
(into {}
|
|
||||||
(map (fn [content-type]
|
|
||||||
(let [schema (->schema-object default {:in :responses
|
|
||||||
:type :schema
|
|
||||||
:content-type content-type})]
|
|
||||||
[content-type (->content nil schema)])))
|
|
||||||
content-types))
|
|
||||||
(when content
|
|
||||||
(into {}
|
|
||||||
(map (fn [[content-type {:keys [schema] :as data}]]
|
|
||||||
(let [schema (->schema-object schema {:in :responses
|
|
||||||
:type :schema
|
|
||||||
:content-type content-type})]
|
|
||||||
[content-type (->content data schema)])))
|
|
||||||
content)))
|
|
||||||
(dissoc :default))]
|
|
||||||
[status (merge (select-keys response [:description])
|
|
||||||
(when content
|
|
||||||
{:content content}))]))
|
|
||||||
responses))}))))
|
|
||||||
|
|
||||||
(defn create
|
(defn create
|
||||||
([]
|
([]
|
||||||
(create nil))
|
(create nil))
|
||||||
|
|
@ -264,7 +174,7 @@
|
||||||
(if (:schema $)
|
(if (:schema $)
|
||||||
(update $ :schema swagger/transform {:type :schema})
|
(update $ :schema swagger/transform {:type :schema})
|
||||||
$))]))}))
|
$))]))}))
|
||||||
:openapi (-get-apidocs-openapi this data)
|
;; :openapi handled in reitit.openapi/-get-apidocs-openapi
|
||||||
(throw
|
(throw
|
||||||
(ex-info
|
(ex-info
|
||||||
(str "Can't produce Malli apidocs for " specification)
|
(str "Can't produce Malli apidocs for " specification)
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,96 @@
|
||||||
(defn- openapi-path [path opts]
|
(defn- openapi-path [path opts]
|
||||||
(-> path (trie/normalize opts) (str/replace #"\{\*" "{")))
|
(-> path (trie/normalize opts) (str/replace #"\{\*" "{")))
|
||||||
|
|
||||||
|
(defn -get-apidocs-openapi
|
||||||
|
[coercion {:keys [request parameters responses content-types] :or {content-types ["application/json"]}}]
|
||||||
|
(let [{:keys [body multipart]} parameters
|
||||||
|
parameters (dissoc parameters :request :body :multipart)
|
||||||
|
->content (fn [data schema]
|
||||||
|
(merge
|
||||||
|
{:schema schema}
|
||||||
|
(select-keys data [:description :examples])
|
||||||
|
(:openapi data)))
|
||||||
|
->schema-object #(coercion/-get-model-apidocs coercion :openapi %1 %2)]
|
||||||
|
(merge
|
||||||
|
(when (seq parameters)
|
||||||
|
{:parameters
|
||||||
|
(->> (for [[in schema] parameters
|
||||||
|
:let [{:keys [properties required]} (->schema-object schema {:in in :type :parameter})
|
||||||
|
required? (partial contains? (set required))]
|
||||||
|
[k schema] properties]
|
||||||
|
(merge {:in (name in)
|
||||||
|
:name k
|
||||||
|
:required (required? k)
|
||||||
|
:schema schema}
|
||||||
|
(select-keys schema [:description])))
|
||||||
|
(into []))})
|
||||||
|
(when body
|
||||||
|
;; body uses a single schema to describe every :requestBody
|
||||||
|
;; the schema-object transformer should be able to transform into distinct content-types
|
||||||
|
{:requestBody {:content (into {}
|
||||||
|
(map (fn [content-type]
|
||||||
|
(let [schema (->schema-object body {:in :requestBody
|
||||||
|
:type :schema
|
||||||
|
:content-type content-type})]
|
||||||
|
[content-type {:schema schema}])))
|
||||||
|
content-types)}})
|
||||||
|
|
||||||
|
(when request
|
||||||
|
;; request allow to different :requestBody per content-type
|
||||||
|
{:requestBody
|
||||||
|
{:content (merge
|
||||||
|
(select-keys request [:description])
|
||||||
|
(when-let [{:keys [schema] :as data} (coercion/get-default request)]
|
||||||
|
(into {}
|
||||||
|
(map (fn [content-type]
|
||||||
|
(let [schema (->schema-object schema {:in :requestBody
|
||||||
|
:type :schema
|
||||||
|
:content-type content-type})]
|
||||||
|
[content-type (->content data schema)])))
|
||||||
|
content-types))
|
||||||
|
(into {}
|
||||||
|
(map (fn [[content-type {:keys [schema] :as data}]]
|
||||||
|
(let [schema (->schema-object schema {:in :requestBody
|
||||||
|
:type :schema
|
||||||
|
:content-type content-type})]
|
||||||
|
[content-type (->content data schema)])))
|
||||||
|
(:content request)))}})
|
||||||
|
(when multipart
|
||||||
|
{:requestBody
|
||||||
|
{:content
|
||||||
|
{"multipart/form-data"
|
||||||
|
{:schema
|
||||||
|
(->schema-object multipart {:in :requestBody
|
||||||
|
:type :schema
|
||||||
|
:content-type "multipart/form-data"})}}}})
|
||||||
|
(when responses
|
||||||
|
{:responses
|
||||||
|
(into {}
|
||||||
|
(map (fn [[status {:keys [content], :as response}]]
|
||||||
|
(let [default (coercion/get-default-schema response)
|
||||||
|
content (-> (merge
|
||||||
|
(when default
|
||||||
|
(into {}
|
||||||
|
(map (fn [content-type]
|
||||||
|
(let [schema (->schema-object default {:in :responses
|
||||||
|
:type :schema
|
||||||
|
:content-type content-type})]
|
||||||
|
[content-type (->content nil schema)])))
|
||||||
|
content-types))
|
||||||
|
(when content
|
||||||
|
(into {}
|
||||||
|
(map (fn [[content-type {:keys [schema] :as data}]]
|
||||||
|
(let [schema (->schema-object schema {:in :responses
|
||||||
|
:type :schema
|
||||||
|
:content-type content-type})]
|
||||||
|
[content-type (->content data schema)])))
|
||||||
|
content)))
|
||||||
|
(dissoc :default))]
|
||||||
|
[status (merge (select-keys response [:description])
|
||||||
|
(when content
|
||||||
|
{:content content}))]))
|
||||||
|
responses))}))))
|
||||||
|
|
||||||
(defn create-openapi-handler
|
(defn create-openapi-handler
|
||||||
"Stability: alpha
|
"Stability: alpha
|
||||||
|
|
||||||
|
|
@ -99,7 +189,7 @@
|
||||||
(apply meta-merge (keep (comp :openapi :data) middleware))
|
(apply meta-merge (keep (comp :openapi :data) middleware))
|
||||||
(apply meta-merge (keep (comp :openapi :data) interceptors))
|
(apply meta-merge (keep (comp :openapi :data) interceptors))
|
||||||
(if coercion
|
(if coercion
|
||||||
(coercion/get-apidocs coercion :openapi data))
|
(-get-apidocs-openapi coercion data))
|
||||||
(select-keys data [:tags :summary :description])
|
(select-keys data [:tags :summary :description])
|
||||||
(strip-top-level-keys openapi))]))
|
(strip-top-level-keys openapi))]))
|
||||||
transform-path (fn [[p _ c]]
|
transform-path (fn [[p _ c]]
|
||||||
|
|
|
||||||
|
|
@ -157,19 +157,16 @@
|
||||||
:version "0.0.1"}
|
:version "0.0.1"}
|
||||||
:paths {"/api/spec/plus/{z}" {:get {:parameters [{:in "query"
|
:paths {"/api/spec/plus/{z}" {:get {:parameters [{:in "query"
|
||||||
:name "x"
|
:name "x"
|
||||||
:description ""
|
|
||||||
:required true
|
:required true
|
||||||
:schema {:type "integer"
|
:schema {:type "integer"
|
||||||
:format "int64"}}
|
:format "int64"}}
|
||||||
{:in "query"
|
{:in "query"
|
||||||
:name "y"
|
:name "y"
|
||||||
:description ""
|
|
||||||
:required true
|
:required true
|
||||||
:schema {:type "integer"
|
:schema {:type "integer"
|
||||||
:format "int64"}}
|
:format "int64"}}
|
||||||
{:in "path"
|
{:in "path"
|
||||||
:name "z"
|
:name "z"
|
||||||
:description ""
|
|
||||||
:required true
|
:required true
|
||||||
:schema {:type "integer"
|
:schema {:type "integer"
|
||||||
:format "int64"}}]
|
:format "int64"}}]
|
||||||
|
|
@ -188,7 +185,6 @@
|
||||||
:post {:parameters [{:in "path"
|
:post {:parameters [{:in "path"
|
||||||
:name "z"
|
:name "z"
|
||||||
:required true
|
:required true
|
||||||
:description ""
|
|
||||||
:schema {:type "integer"
|
:schema {:type "integer"
|
||||||
:format "int64"}}]
|
:format "int64"}}]
|
||||||
:requestBody {:content {"application/json" {:schema {:oneOf [{:items {:type "integer"
|
:requestBody {:content {"application/json" {:schema {:oneOf [{:items {:type "integer"
|
||||||
|
|
@ -251,21 +247,18 @@
|
||||||
:required [:error]
|
:required [:error]
|
||||||
:type "object"}}}}}
|
:type "object"}}}}}
|
||||||
:summary "plus with body"}}
|
:summary "plus with body"}}
|
||||||
"/api/schema/plus/{z}" {:get {:parameters [{:description ""
|
"/api/schema/plus/{z}" {:get {:parameters [{:in "query"
|
||||||
:in "query"
|
|
||||||
:name "x"
|
:name "x"
|
||||||
:required true
|
:required true
|
||||||
:schema {:format "int32"
|
:schema {:format "int32"
|
||||||
:type "integer"}}
|
:type "integer"}}
|
||||||
{:description ""
|
{:in "query"
|
||||||
:in "query"
|
|
||||||
:name "y"
|
:name "y"
|
||||||
:required true
|
:required true
|
||||||
:schema {:type "integer"
|
:schema {:type "integer"
|
||||||
:format "int32"}}
|
:format "int32"}}
|
||||||
{:in "path"
|
{:in "path"
|
||||||
:name "z"
|
:name "z"
|
||||||
:description ""
|
|
||||||
:required true
|
:required true
|
||||||
:schema {:type "integer"
|
:schema {:type "integer"
|
||||||
:format "int32"}}]
|
:format "int32"}}]
|
||||||
|
|
@ -282,7 +275,6 @@
|
||||||
:summary "plus"}
|
:summary "plus"}
|
||||||
:post {:parameters [{:in "path"
|
:post {:parameters [{:in "path"
|
||||||
:name "z"
|
:name "z"
|
||||||
:description ""
|
|
||||||
:required true
|
:required true
|
||||||
:schema {:type "integer"
|
:schema {:type "integer"
|
||||||
:format "int32"}}]
|
:format "int32"}}]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue