diff --git a/modules/reitit-malli/src/reitit/coercion/malli.cljc b/modules/reitit-malli/src/reitit/coercion/malli.cljc index c033c5c1..89c65cb5 100644 --- a/modules/reitit-malli/src/reitit/coercion/malli.cljc +++ b/modules/reitit-malli/src/reitit/coercion/malli.cljc @@ -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) diff --git a/modules/reitit-openapi/src/reitit/openapi.cljc b/modules/reitit-openapi/src/reitit/openapi.cljc index 89bfd3df..68dc2caa 100644 --- a/modules/reitit-openapi/src/reitit/openapi.cljc +++ b/modules/reitit-openapi/src/reitit/openapi.cljc @@ -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]] diff --git a/test/cljc/reitit/openapi_test.clj b/test/cljc/reitit/openapi_test.clj index f147c407..3724b7ac 100644 --- a/test/cljc/reitit/openapi_test.clj +++ b/test/cljc/reitit/openapi_test.clj @@ -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"}}]