feat: openapi #/components/schemas

collect definitions when traversing the models, and put them in the
right place for openapi

depends on :malli.json-schema/definitions-path support
This commit is contained in:
Joel Kaasinen 2024-04-19 10:57:51 +03:00
parent c2372473d0
commit 288b701d4e
2 changed files with 22 additions and 14 deletions

View file

@ -77,7 +77,7 @@
(-> path (trie/normalize opts) (str/replace #"\{\*" "{"))) (-> path (trie/normalize opts) (str/replace #"\{\*" "{")))
(defn -get-apidocs-openapi (defn -get-apidocs-openapi
[coercion {:keys [request muuntaja parameters responses openapi/request-content-types openapi/response-content-types]}] [coercion {:keys [request muuntaja parameters responses openapi/request-content-types openapi/response-content-types]} definitions]
(let [{:keys [body multipart]} parameters (let [{:keys [body multipart]} parameters
parameters (dissoc parameters :request :body :multipart) parameters (dissoc parameters :request :body :multipart)
->content (fn [data schema] ->content (fn [data schema]
@ -85,7 +85,13 @@
{:schema schema} {:schema schema}
(select-keys data [:description :examples]) (select-keys data [:description :examples])
(:openapi data))) (:openapi data)))
->schema-object #(coercion/-get-model-apidocs coercion :openapi %1 %2) ->schema-object (fn [model opts]
(let [result (coercion/-get-model-apidocs
coercion :openapi model
(assoc opts :malli.json-schema/definitions-path "#/components/schemas/"))]
(when-let [d (:definitions result)]
(vswap! definitions merge d))
(dissoc result :definitions)))
request-content-types (or request-content-types request-content-types (or request-content-types
(when muuntaja (m/decodes muuntaja)) (when muuntaja (m/decodes muuntaja))
["application/json"]) ["application/json"])
@ -189,6 +195,7 @@
:x-id ids})) :x-id ids}))
accept-route (fn [route] accept-route (fn [route]
(-> route second :openapi :id (or ::default) (trie/into-set) (set/intersection ids) seq)) (-> route second :openapi :id (or ::default) (trie/into-set) (set/intersection ids) seq))
definitions (volatile! {})
transform-endpoint (fn [[method {{:keys [coercion no-doc openapi] :as data} :data transform-endpoint (fn [[method {{:keys [coercion no-doc openapi] :as data} :data
middleware :middleware middleware :middleware
interceptors :interceptors}]] interceptors :interceptors}]]
@ -198,7 +205,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
(-get-apidocs-openapi coercion data)) (-get-apidocs-openapi coercion data definitions))
(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]]
@ -207,7 +214,8 @@
map-in-order #(->> % (apply concat) (apply array-map)) map-in-order #(->> % (apply concat) (apply array-map))
paths (->> router (r/compiled-routes) (filter accept-route) (map transform-path) map-in-order)] paths (->> router (r/compiled-routes) (filter accept-route) (map transform-path) map-in-order)]
{:status 200 {:status 200
:body (meta-merge openapi {:paths paths})})) :body (cond-> (meta-merge openapi {:paths paths})
(seq @definitions) (assoc-in [:components :schemas] @definitions))}))
([req res raise] ([req res raise]
(try (try
(res (create-openapi req)) (res (create-openapi req))

View file

@ -844,16 +844,16 @@
:requestBody :requestBody
{:content {:content
{"application/json" {"application/json"
{:schema {:$ref "#/definitions/friend" {:schema {:$ref "#/components/schemas/friend"}}}}}}}
:definitions {"friend" {:properties {:age {:type "integer"} :components {:schemas {"friend" {:properties {:age {:type "integer"}
:pet {:$ref "#/definitions/pet"}} :pet {:$ref "#/components/schemas/pet"}}
:required [:age :pet] :required [:age :pet]
:type "object"} :type "object"}
"pet" {:properties {:friends {:items {:$ref "#/definitions/friend"} "pet" {:properties {:friends {:items {:$ref "#/components/schemas/friend"}
:type "array"} :type "array"}
:name {:type "string"}} :name {:type "string"}}
:required [:name :friends] :required [:name :friends]
:type "object"}}}}}}}}}} :type "object"}}}}
spec)) spec))
(testing "spec is valid" (testing "spec is valid"
(is (nil? (validate spec)))))) (is (nil? (validate spec))))))