diff --git a/core/src/martian/openapi.cljc b/core/src/martian/openapi.cljc index 57f7151..eeff720 100644 --- a/core/src/martian/openapi.cljc +++ b/core/src/martian/openapi.cljc @@ -53,7 +53,23 @@ components (conj seen-set reference))) (wrap schema - (condp = (:type schema) + (condp = (if-let [typ (:type schema)] + typ + ;; If a schema has no :type key, and the only key it contains is a :properties key, + ;; then the :type can reasonably be inferred as "object". + ;; + ;; See https://github.com/OAI/OpenAPI-Specification/issues/1657 + ;; + ;; Excerpt: + ;; A particularly common form of this is a schema that omits type, but specifies properties. + ;; Strictly speaking, this does not mean that the value must be an object. + ;; It means that if the value is an object, and it includes any of those properties, + ;; the property values must conform to the corresponding property subschemas. + ;; + ;; In reality, this construct almost always means that the user intends type: object, + ;; and I think it would be reasonable for a code generator to assume this, + ;; maybe with a validation: strict|lax config option to control that behavior. + (when (= #{:properties} (set (keys schema))) "object")) "array" [(openapi->schema (:items schema) components seen-set)] "object" (let [required? (set (:required schema))] (into {} diff --git a/core/test/martian/openapi_test.cljc b/core/test/martian/openapi_test.cljc index 0d959ce..62bf512 100644 --- a/core/test/martian/openapi_test.cljc +++ b/core/test/martian/openapi_test.cljc @@ -160,3 +160,30 @@ {:status (s/eq 404) :body {(s/optional-key :code) s/Int (s/optional-key :details) s/Str}}] (:response-schemas handler))))) + +(deftest schemas-without-type-test + (let [openapi-json + {:paths {(keyword "/models") + {:get {:operationId "list-models" + :summary "Lists models" + :responses {:404 {:$ref "#/components/responses/NotFound"}}}}} + :components {:responses {:NotFound + {:description "The requested resource was not found." + :content + {:application/json + {:schema {:$ref "#/components/schemas/Error"}}}}} + :schemas {:Error + {:properties + {:code + {:description "An enumerated error for machine use.", + :type "integer", + :readOnly true}, + :details + {:description "A human-readable description of the error.", + :type "string", + :readOnly true}}}}}} + [handler] (openapi->handlers openapi-json {:encodes ["application/json"] + :decodes ["application/json"]})] + (is (= [{:status (s/eq 404) + :body {(s/optional-key :code) s/Int (s/optional-key :details) s/Str}}] + (:response-schemas handler)))))