Merge pull request #676 from metosin/update-validator

update openapi-schema-validator, fix openapi requestBody description
This commit is contained in:
Joel Kaasinen 2024-04-22 08:48:59 +03:00 committed by GitHub
commit 15790f3028
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 74 additions and 51 deletions

View file

@ -125,23 +125,24 @@
(when request (when request
;; request allow to different :requestBody per content-type ;; request allow to different :requestBody per content-type
{:requestBody {:requestBody
{:content (merge (merge
(select-keys request [:description]) (select-keys request [:description])
(when-let [{:keys [schema] :as data} (coercion/get-default request)] {:content (merge
(into {} (when-let [{:keys [schema] :as data} (coercion/get-default request)]
(map (fn [content-type] (into {}
(let [schema (->schema-object schema {:in :requestBody (map (fn [content-type]
:type :schema (let [schema (->schema-object schema {:in :requestBody
:content-type content-type})] :type :schema
[content-type (->content data schema)]))) :content-type content-type})]
request-content-types)) [content-type (->content data schema)])))
(into {} request-content-types))
(map (fn [[content-type {:keys [schema] :as data}]] (into {}
(let [schema (->schema-object schema {:in :requestBody (map (fn [[content-type {:keys [schema] :as data}]]
:type :schema (let [schema (->schema-object schema {:in :requestBody
:content-type content-type})] :type :schema
[content-type (->content data schema)]))) :content-type content-type})]
(dissoc (:content request) :default)))}}) [content-type (->content data schema)])))
(dissoc (:content request) :default)))})})
(when multipart (when multipart
{:requestBody {:requestBody
{:content {:content

23
package-lock.json generated
View file

@ -6,7 +6,7 @@
"": { "": {
"name": "reitit", "name": "reitit",
"devDependencies": { "devDependencies": {
"@seriousme/openapi-schema-validator": "^2.1.0", "@seriousme/openapi-schema-validator": "^2.2.1",
"karma": "^4.1.0", "karma": "^4.1.0",
"karma-chrome-launcher": "^2.2.0", "karma-chrome-launcher": "^2.2.0",
"karma-cli": "^2.0.0", "karma-cli": "^2.0.0",
@ -15,20 +15,31 @@
} }
}, },
"node_modules/@seriousme/openapi-schema-validator": { "node_modules/@seriousme/openapi-schema-validator": {
"version": "2.1.0", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/@seriousme/openapi-schema-validator/-/openapi-schema-validator-2.1.0.tgz", "resolved": "https://registry.npmjs.org/@seriousme/openapi-schema-validator/-/openapi-schema-validator-2.2.1.tgz",
"integrity": "sha512-J461zq7Qj4N/SQlUiyXFelGqtKJenW9DnTjX5fraLk9Lmybq7B6goBieAlMf3D2W+grrVz/hSDodB0faoD9y2Q==", "integrity": "sha512-I+6l2vZ4qx+RyUo8GNnIbeqbv5ao1enSdNFPJ7x3slIVLU8aSBf228qpo+6iNWbRMK4ktF91jsv5KN7PpaJQtg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"ajv": "^8.11.0", "ajv": "^8.12.0",
"ajv-draft-04": "^1.0.0", "ajv-draft-04": "^1.0.0",
"ajv-formats": "^2.1.1", "ajv-formats": "^2.1.1",
"js-yaml": "^4.1.0" "js-yaml": "^4.1.0",
"minimist": "^1.2.8"
}, },
"bin": { "bin": {
"bundle-api": "bin/bundle-api-cli.js",
"validate-api": "bin/validate-api-cli.js" "validate-api": "bin/validate-api-cli.js"
} }
}, },
"node_modules/@seriousme/openapi-schema-validator/node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/accepts": { "node_modules/accepts": {
"version": "1.3.7", "version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",

View file

@ -2,7 +2,7 @@
"name": "reitit", "name": "reitit",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"@seriousme/openapi-schema-validator": "^2.1.0", "@seriousme/openapi-schema-validator": "^2.2.1",
"karma": "^4.1.0", "karma": "^4.1.0",
"karma-chrome-launcher": "^2.2.0", "karma-chrome-launcher": "^2.2.0",
"karma-cli": "^2.0.0", "karma-cli": "^2.0.0",

View file

@ -869,19 +869,20 @@
(ring/router (ring/router
[["/openapi.json" [["/openapi.json"
{:get {:no-doc true {:get {:no-doc true
:openapi {:info {:title "" :version "0.0.1"}}
:handler (openapi/create-openapi-handler)}}] :handler (openapi/create-openapi-handler)}}]
["/malli" {:coercion malli/coercion} ["/malli" {:coercion malli/coercion}
["/plus" {:post {:summary "plus with body" ["/plus" {:post {:summary "plus with body"
:request {:description "body description" :request {:description "body description"
:content {"application/json" {:schema {:x int?, :y int?} :content {"application/json" {:schema {:x int?, :y int?}
:examples {"1+1" {:x 1, :y 1} :examples {"1+1" {:value {:x 1, :y 1}}
"1+2" {:x 1, :y 2}} "1+2" {:value {:x 1, :y 2}}}
:openapi {:example {:x 2, :y 2}}}}} :openapi {:example {:x 2, :y 2}}}}}
:responses {200 {:description "success" :responses {200 {:description "success"
:content {"application/json" {:schema {:total int?} :content {"application/json" {:schema {:total int?}
:examples {"2" {:total 2} :examples {"2" {:value {:total 2}}
"3" {:total 3}} "3" {:value {:total 3}}}
:openapi {:example {:total 4}}}}}} :openapi {:example {:total 4}}}}}}
:handler (fn [request] :handler (fn [request]
(let [{:keys [x y]} (-> request :parameters :body)] (let [{:keys [x y]} (-> request :parameters :body)]
@ -891,49 +892,51 @@
:data {:middleware [openapi/openapi-feature :data {:middleware [openapi/openapi-feature
rrc/coerce-exceptions-middleware rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware rrc/coerce-request-middleware
rrc/coerce-response-middleware]}}))] rrc/coerce-response-middleware]}}))
(is (= {"/malli/plus" {:post {:requestBody {:content {:description "body description", spec (:body (app {:request-method :get :uri "/openapi.json"}))]
"application/json" {:schema {:type "object", (is (= {"/malli/plus" {:post {:requestBody {:description "body description",
:content {"application/json" {:schema {:type "object",
:properties {:x {:type "integer"}, :properties {:x {:type "integer"},
:y {:type "integer"}}, :y {:type "integer"}},
:required [:x :y], :required [:x :y],
:additionalProperties false}, :additionalProperties false},
:examples {"1+1" {:x 1, :y 1}, "1+2" {:x 1, :y 2}}, :examples {"1+1" {:value {:x 1, :y 1}}
"1+2" {:value {:x 1, :y 2}}},
:example {:x 2, :y 2}}}}, :example {:x 2, :y 2}}}},
:responses {200 {:description "success", :responses {200 {:description "success",
:content {"application/json" {:schema {:type "object", :content {"application/json" {:schema {:type "object",
:properties {:total {:type "integer"}}, :properties {:total {:type "integer"}},
:required [:total], :required [:total],
:additionalProperties false}, :additionalProperties false},
:examples {"2" {:total 2}, "3" {:total 3}}, :examples {"2" {:value {:total 2}},
"3" {:value {:total 3}}},
:example {:total 4}}}}}, :example {:total 4}}}}},
:summary "plus with body"}}} :summary "plus with body"}}}
(-> {:request-method :get (:paths spec)))
:uri "/openapi.json"} (is (nil? (validate spec))))
(app)
:body
:paths))))
(testing "ref schemas" (testing "ref schemas"
(let [registry (merge (mc/base-schemas) (let [registry (merge (mc/base-schemas)
(mc/type-schemas) (mc/type-schemas)
{::plus [:map [:x :int] [:y ::y]] {"plus" [:map [:x :int] [:y "y"]]
::y :int}) "y" :int})
app (ring/ring-handler app (ring/ring-handler
(ring/router (ring/router
[["/openapi.json" [["/openapi.json"
{:get {:no-doc true {:get {:no-doc true
:openapi {:info {:title "" :version "0.0.1"}}
:handler (openapi/create-openapi-handler)}}] :handler (openapi/create-openapi-handler)}}]
["/post" ["/post"
{:post {:coercion malli/coercion {:post {:coercion malli/coercion
:parameters {:body (mc/schema ::plus {:registry registry})} :parameters {:body (mc/schema "plus" {:registry registry})}
:handler identity}}] :handler identity}}]
["/get" ["/get"
{:get {:coercion malli/coercion {:get {:coercion malli/coercion
:parameters {:query (mc/schema ::plus {:registry registry})} :parameters {:query (mc/schema "plus" {:registry registry})}
:handler identity}}]])) :handler identity}}]]))
spec (:body (app {:request-method :get :uri "/openapi.json"}))] spec (:body (app {:request-method :get :uri "/openapi.json"}))]
(is (= {:openapi "3.1.0" (is (= {:openapi "3.1.0"
:x-id #{:reitit.openapi/default} :x-id #{:reitit.openapi/default}
:info {:title "" :version "0.0.1"}
:paths {"/get" {:get {:parameters [{:in "query" :paths {"/get" {:get {:parameters [{:in "query"
:name :x :name :x
:required true :required true
@ -941,25 +944,27 @@
{:in "query" {:in "query"
:name :y :name :y
:required true :required true
:schema {:$ref "#/components/schemas/reitit.openapi-test~1y"}}]}} :schema {:$ref "#/components/schemas/y"}}]}}
"/post" {:post "/post" {:post
{:requestBody {:requestBody
{:content {:content
{"application/json" {"application/json"
{:schema {:schema
{:$ref "#/components/schemas/reitit.openapi-test~1plus"}}}}}}} {:$ref "#/components/schemas/plus"}}}}}}}
:components {:schemas :components {:schemas
{"reitit.openapi-test/y" {:type "integer"} {"y" {:type "integer"}
"reitit.openapi-test/plus" {:type "object" "plus" {:type "object"
:properties {:x {:type "integer"} :properties {:x {:type "integer"}
:y {:$ref "#/components/schemas/reitit.openapi-test~1y"}} :y {:$ref "#/components/schemas/y"}}
:required [:x :y]}}}} :required [:x :y]}}}}
spec)))) spec))
(is (nil? (validate spec)))))
(testing "var schemas" (testing "var schemas"
(let [app (ring/ring-handler (let [app (ring/ring-handler
(ring/router (ring/router
[["/openapi.json" [["/openapi.json"
{:get {:no-doc true {:get {:no-doc true
:openapi {:info {:title "" :version "0.0.1"}}
:handler (openapi/create-openapi-handler)}}] :handler (openapi/create-openapi-handler)}}]
["/post" ["/post"
{:post {:coercion malli/coercion {:post {:coercion malli/coercion
@ -972,6 +977,7 @@
spec (:body (app {:request-method :get :uri "/openapi.json"}))] spec (:body (app {:request-method :get :uri "/openapi.json"}))]
(is (= {:openapi "3.1.0" (is (= {:openapi "3.1.0"
:x-id #{:reitit.openapi/default} :x-id #{:reitit.openapi/default}
:info {:title "" :version "0.0.1"}
:paths :paths
{"/post" {"/post"
{:post {:post
@ -999,4 +1005,9 @@
:y {:$ref "#/components/schemas/reitit.openapi-test~1Y"}} :y {:$ref "#/components/schemas/reitit.openapi-test~1Y"}}
:required [:x :y]} :required [:x :y]}
"reitit.openapi-test/Y" {:type "integer"}}}} "reitit.openapi-test/Y" {:type "integer"}}}}
spec))))) spec))
;; TODO: the OAS 3.1 json schema disallows "/" in :components :schemas keys,
;; even though the text of the spec allows it. See:
;; https://github.com/seriousme/openapi-schema-validator/blob/772375bf4895f0e641d103c27140cdd1d2afc34e/schemas/v3.1/schema.json#L282
#_
(is (nil? (validate spec))))))