read openapi metadata into openapi description

This commit is contained in:
Tommi Reiman 2023-08-23 16:43:34 +03:00
parent 226ca889b6
commit adef7ad06e
3 changed files with 75 additions and 11 deletions

View file

@ -105,6 +105,10 @@
(or (-> request-or-response :content :default :schema) (or (-> request-or-response :content :default :schema)
(:body request-or-response))) (: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] (defn content-request-coercer [coercion {:keys [content body]} {::keys [extract-request-format serialize-failed-result]
:or {extract-request-format extract-request-format-default}}] :or {extract-request-format extract-request-format-default}}]
(when coercion (when coercion

View file

@ -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
[_ {:keys [request 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 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-let [default (coercion/get-default-schema 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 default {: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 {:keys [schema]}]] (map (fn [[content-type {:keys [schema] :as data}]]
(let [schema (->schema-object schema {: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
@ -203,15 +213,15 @@
(let [schema (->schema-object default {: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 {:keys [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))] (dissoc :default))]
[status (merge (select-keys response [:description]) [status (merge (select-keys response [:description])

View file

@ -774,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))))