refactor: share -get-apidocs-openapi between malli, spec & schema

This commit is contained in:
Joel Kaasinen 2023-08-28 09:24:10 +03:00
parent 051452231a
commit 6f111bce2e
3 changed files with 94 additions and 102 deletions

View file

@ -133,96 +133,6 @@
;; malli options
: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
([]
(create nil))
@ -264,7 +174,7 @@
(if (:schema $)
(update $ :schema swagger/transform {:type :schema})
$))]))}))
:openapi (-get-apidocs-openapi this data)
;; :openapi handled in reitit.openapi/-get-apidocs-openapi
(throw
(ex-info
(str "Can't produce Malli apidocs for " specification)

View file

@ -73,6 +73,96 @@
(defn- openapi-path [path opts]
(-> 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
"Stability: alpha
@ -99,7 +189,7 @@
(apply meta-merge (keep (comp :openapi :data) middleware))
(apply meta-merge (keep (comp :openapi :data) interceptors))
(if coercion
(coercion/get-apidocs coercion :openapi data))
(-get-apidocs-openapi coercion data))
(select-keys data [:tags :summary :description])
(strip-top-level-keys openapi))]))
transform-path (fn [[p _ c]]

View file

@ -157,19 +157,16 @@
:version "0.0.1"}
:paths {"/api/spec/plus/{z}" {:get {:parameters [{:in "query"
:name "x"
:description ""
:required true
:schema {:type "integer"
:format "int64"}}
{:in "query"
:name "y"
:description ""
:required true
:schema {:type "integer"
:format "int64"}}
{:in "path"
:name "z"
:description ""
:required true
:schema {:type "integer"
:format "int64"}}]
@ -188,7 +185,6 @@
:post {:parameters [{:in "path"
:name "z"
:required true
:description ""
:schema {:type "integer"
:format "int64"}}]
:requestBody {:content {"application/json" {:schema {:oneOf [{:items {:type "integer"
@ -251,21 +247,18 @@
:required [:error]
:type "object"}}}}}
:summary "plus with body"}}
"/api/schema/plus/{z}" {:get {:parameters [{:description ""
:in "query"
"/api/schema/plus/{z}" {:get {:parameters [{:in "query"
:name "x"
:required true
:schema {:format "int32"
:type "integer"}}
{:description ""
:in "query"
{:in "query"
:name "y"
:required true
:schema {:type "integer"
:format "int32"}}
{:in "path"
:name "z"
:description ""
:required true
:schema {:type "integer"
:format "int32"}}]
@ -282,7 +275,6 @@
:summary "plus"}
:post {:parameters [{:in "path"
:name "z"
:description ""
:required true
:schema {:type "integer"
:format "int32"}}]