Skip to content

Commit

Permalink
openapi 3.0 support pass schema type as key of options
Browse files Browse the repository at this point in the history
  • Loading branch information
rajcspsg committed Apr 3, 2024
1 parent 4870e57 commit 94f1d01
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 85 deletions.
6 changes: 3 additions & 3 deletions src/ring/openapi/openapi3.clj
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
(for [[k v] (-> model common/value-of stc/schema-value rsc/strict-schema)
:when (s/specific-key? k)
:let [rk (s/explicit-schema-key k)
json-schema (rsjs/->swagger v options :openapi)]
json-schema (rsjs/->swagger v (assoc options :schema-type :openapi))]
:when json-schema]
{:in (name in)
:name (rsjs/key-name rk)
Expand All @@ -60,7 +60,7 @@
(into {} (for [[content-type schema-input] contents]
[content-type
(let [schema (rsc/peek-schema schema-input)
schema-json (rsjs/->swagger schema-input options :openapi)]
schema-json (rsjs/->swagger schema-input (assoc options :schema-type :openapi))]
{:name (or (common/title schema) "")
:schema schema-json})]))))

Expand All @@ -78,7 +78,7 @@
(cond-> headers (update-in [:headers] (fn [headers]
(if headers
(->> (for [[k v] headers]
[k (rsjs/->swagger v options :openapi)])
[k (rsjs/->swagger v (assoc options :schema-type :openapi))])
(into {}))))))
(update-in [:description] #(or %
(:description (rsjs/json-schema-meta v))
Expand Down
117 changes: 58 additions & 59 deletions src/ring/swagger/json_schema.clj
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@
(str (if n (str n "/")) (name x)))
x))

(defmulti convert-class (fn [c _ _] c))
(defmulti convert-class (fn [c _] c))

(defprotocol JsonSchema
(convert [this options schema-type]))
(convert [this options]))

(defn not-supported! [e]
(throw (IllegalArgumentException.
Expand Down Expand Up @@ -92,28 +92,28 @@
m))

;; Classes
(defmethod convert-class java.lang.Integer [_ _ _] {:type "integer" :format "int32"})
(defmethod convert-class java.lang.Long [_ _ _] {:type "integer" :format "int64"})
(defmethod convert-class java.lang.Double [_ _ _] {:type "number" :format "double"})
(defmethod convert-class java.lang.Number [_ _ _] {:type "number" :format "double"})
(defmethod convert-class java.lang.String [_ _ _] {:type "string"})
(defmethod convert-class java.lang.Boolean [_ _ _] {:type "boolean"})
(defmethod convert-class clojure.lang.Keyword [_ _ _] {:type "string"})
(defmethod convert-class clojure.lang.Symbol [_ _ _] {:type "string"})
(defmethod convert-class java.util.UUID [_ _ _] {:type "string" :format "uuid"})
(defmethod convert-class java.util.Date [_ _ _] {:type "string" :format "date-time"})
(defmethod convert-class org.joda.time.DateTime [_ _ _] {:type "string" :format "date-time"})
(defmethod convert-class org.joda.time.LocalDate [_ _ _] {:type "string" :format "date"})
(defmethod convert-class org.joda.time.LocalTime [_ _ _] {:type "string" :format "time"})
(defmethod convert-class java.util.regex.Pattern [_ _ _] {:type "string" :format "regex"})
(defmethod convert-class java.io.File [_ _ _] {:type "file"})
(defmethod convert-class java.lang.Integer [_ _] {:type "integer" :format "int32"})
(defmethod convert-class java.lang.Long [_ _] {:type "integer" :format "int64"})
(defmethod convert-class java.lang.Double [_ _] {:type "number" :format "double"})
(defmethod convert-class java.lang.Number [_ _] {:type "number" :format "double"})
(defmethod convert-class java.lang.String [_ _] {:type "string"})
(defmethod convert-class java.lang.Boolean [_ _] {:type "boolean"})
(defmethod convert-class clojure.lang.Keyword [_ _] {:type "string"})
(defmethod convert-class clojure.lang.Symbol [_ _] {:type "string"})
(defmethod convert-class java.util.UUID [_ _] {:type "string" :format "uuid"})
(defmethod convert-class java.util.Date [_ _] {:type "string" :format "date-time"})
(defmethod convert-class org.joda.time.DateTime [_ _] {:type "string" :format "date-time"})
(defmethod convert-class org.joda.time.LocalDate [_ _] {:type "string" :format "date"})
(defmethod convert-class org.joda.time.LocalTime [_ _] {:type "string" :format "time"})
(defmethod convert-class java.util.regex.Pattern [_ _] {:type "string" :format "regex"})
(defmethod convert-class java.io.File [_ _] {:type "file"})

(extension/java-time
(defmethod convert-class java.time.Instant [_ _ _] {:type "string" :format "date-time"})
(defmethod convert-class java.time.LocalDate [_ _ _] {:type "string" :format "date"})
(defmethod convert-class java.time.LocalTime [_ _ _] {:type "string" :format "time"}))
(defmethod convert-class java.time.Instant [_ _] {:type "string" :format "date-time"})
(defmethod convert-class java.time.LocalDate [_ _] {:type "string" :format "date"})
(defmethod convert-class java.time.LocalTime [_ _] {:type "string" :format "time"}))

(defmethod convert-class :default [e _ _]
(defmethod convert-class :default [e _]
(if-not *ignore-missing-mappings*
(not-supported! e)))

Expand All @@ -127,127 +127,126 @@

(defn ->swagger
([x]
(->swagger x {} :swagger))
([x options schema-type]
(->swagger x {}))
([x options]
(-> x
(convert options schema-type)
(convert options)
(merge-meta x options))))

(defn try->swagger [v k key-meta schema-type]
(try (->swagger v {:key-meta key-meta} schema-type)
(try (->swagger v {:key-meta key-meta :schema-type schema-type})
(catch Exception e
(throw
(IllegalArgumentException.
(str "error converting to swagger schema [" k " "
(try (s/explain v) (catch Exception _ v)) "]") e)))))


(defn- coll-schema [e options schema-type]
(defn- coll-schema [e options]
(-> {:type "array"
:items (->swagger (first e) (assoc options ::no-meta true) schema-type)}
:items (->swagger (first e) (assoc options ::no-meta true))}
(assoc-collection-format options)))

(extend-protocol JsonSchema

Object
(convert [e _ _]
(convert [e _]
(not-supported! e))

Class
(convert [e options schema-type]
(convert [e options]
(if-let [schema (common/record-schema e)]
(schema-object schema schema-type)
(convert-class e options schema-type)))
(schema-object schema (:schema-type options))
(convert-class e options)))

nil
(convert [_ _ _]
(convert [_ _]
nil)

FieldSchema
(convert [e _ schema-type]
(->swagger (:schema e) {} schema-type))
(convert [e _]
(->swagger (:schema e) {}))

schema.core.Predicate
(convert [e _ schema-type]
(some-> e :pred-name predicate-name-to-class (->swagger {} schema-type)))
(convert [e _]
(some-> e :pred-name predicate-name-to-class (->swagger {})))

schema.core.EnumSchema
(convert [e options schema-type]
(merge (->swagger (class (first (:vs e))) options schema-type) {:enum (seq (:vs e))}))
(convert [e options]
(merge (->swagger (class (first (:vs e))) options) {:enum (seq (:vs e))}))

schema.core.Maybe
(convert [e {:keys [in]} _]
(convert [e {:keys [in]}]
(let [schema (->swagger (:schema e))]
(condp contains? in
#{:query :formData} (assoc schema :allowEmptyValue true)
#{nil :body} (assoc schema :x-nullable true)
schema)))

schema.core.Both
(convert [e _ _]
(convert [e _]
(->swagger (first (:schemas e))))

schema.core.Either
(convert [e _ _]
(convert [e _]
(->swagger (first (:schemas e))))

schema.core.Recursive
(convert [e _ _]
(convert [e _]
(->swagger (:derefable e)))

schema.core.EqSchema
(convert [e _ _]
(convert [e _]
(merge (->swagger (class (:v e)))
{:enum [(:v e)]}))

schema.core.NamedSchema
(convert [e _ schema-type]
(->swagger (:schema e) {} schema-type))
(convert [e _]
(->swagger (:schema e) {}))

schema.core.One
(convert [e _ _]
(convert [e _]
(->swagger (:schema e)))

schema.core.AnythingSchema
(convert [_ {:keys [in] :as opts} schema-type]
(convert [_ {:keys [in] :as opts}]
(if (and in (not= :body in))
(->swagger (s/maybe s/Str) opts schema-type)
(->swagger (s/maybe s/Str) opts)
{}))

schema.core.ConditionalSchema
(convert [e _ _]
(convert [e _]
{:x-oneOf (vec (keep (comp ->swagger second) (:preds-and-schemas e)))})

schema.core.CondPre
(convert [e _ _]
(convert [e _]
{:x-oneOf (mapv ->swagger (:schemas e))})

schema.core.Constrained
(convert [e _ _]
(convert [e _]
(->swagger (:schema e)))

java.util.regex.Pattern
(convert [e _ _]
(convert [e _]
{:type "string" :pattern (str e)})

;; Collections

clojure.lang.Sequential
(convert [e options schema-type]
(coll-schema e options schema-type))
(convert [e options]
(coll-schema e options))

clojure.lang.IPersistentSet
(convert [e options schema-type]
(assoc (coll-schema e options schema-type) :uniqueItems true))
(convert [e options]
(assoc (coll-schema e options) :uniqueItems true))

clojure.lang.IPersistentMap
(convert [e {:keys [properties?]} schema-type]
(convert [e {:keys [properties? schema-type]}]
(if properties?
{:properties (properties e schema-type)}
(reference e schema-type)))

clojure.lang.Var
(convert [e _ schema-type]
(convert [e {:keys [schema-type]}]
(reference e schema-type)))

;;
Expand Down
14 changes: 7 additions & 7 deletions src/ring/swagger/json_schema_dirty.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@

(extend-protocol json-schema/JsonSchema
schema.experimental.abstract_map.AbstractSchema
(convert [e {:keys [properties?]
:or {properties? true}} schema-type]
(convert [e {:keys [properties? schema-type]
:or {properties? true}} ]
(if properties?
(merge {:discriminator (name (:dispatch-key e))}
(json-schema/->swagger (:schema e) {:properties? properties?} schema-type))
(json-schema/->swagger (:schema e) {:properties? properties? :schema-type schema-type}))
(json-schema/reference e schema-type)))

schema.experimental.abstract_map.SchemaExtension
(convert [e options schema-type]
{:allOf [(json-schema/->swagger (:base-schema e) {:properties? false} schema-type)
(convert [e {:keys [schema-type] :as options}]
{:allOf [(json-schema/->swagger (:base-schema e) {:properties? false :schema-type schema-type})
; Find which keys are also in base-schema and don't include them in these properties
(json-schema/->swagger (let [base-keys (set (keys (:schema (:base-schema e))))
m (:extended-schema e)]
m (:extended-schema e)]
(into (empty m) (remove (comp base-keys key) m)))
{:properties? true} schema-type)]}))
{:properties? true :schema-type schema-type})]}))
10 changes: 5 additions & 5 deletions src/ring/swagger/swagger2.clj
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@
;; Paths, parameters, responses
;;

(defmulti extract-parameter (fn [in _ _] in))
(defmulti ^:private extract-parameter (fn [in _ _] in))

(defmethod extract-parameter :body [_ model options]
(if model
(let [schema (rsc/peek-schema model)
schema-json (rsjs/->swagger model options :swagger)]
schema-json (rsjs/->swagger model options)]
(vector
{:in "body"
:name (or (common/title schema) "")
Expand All @@ -55,7 +55,7 @@
(for [[k v] (-> model common/value-of stc/schema-value rsc/strict-schema)
:when (s/specific-key? k)
:let [rk (s/explicit-schema-key k)
json-schema (rsjs/->swagger v options :swagger)]
json-schema (rsjs/->swagger v options)]
:when json-schema]
(merge
{:in (name in)
Expand All @@ -81,11 +81,11 @@
(let [responses (p/for-map [[k v] responses
:let [{:keys [schema headers]} v]]
k (-> v
(cond-> schema (update-in [:schema] rsjs/->swagger options :swagger))
(cond-> schema (update-in [:schema] rsjs/->swagger options))
(cond-> headers (update-in [:headers] (fn [headers]
(if headers
(->> (for [[k v] headers]
[k (rsjs/->swagger v options :swagger)])
[k (rsjs/->swagger v options)])
(into {}))))))
(update-in [:description] #(or %
(:description (rsjs/json-schema-meta v))
Expand Down
22 changes: 11 additions & 11 deletions test/ring/swagger/json_schema_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
(s/defrecord User [age :- s/Int, keyboard :- Keyboard])

;; Make currency return nil for testing purporses
(defmethod rsjs/convert-class java.util.Currency [_ _ _] nil)
(defmethod rsjs/convert-class java.util.Currency [_ _] nil)

(facts "type transformations"
(fact "mapped to nil"
Expand Down Expand Up @@ -82,13 +82,13 @@
(fact "uses wrapped value by default with x-nullable true"
(rsjs/->swagger (s/maybe Long)) => (assoc (rsjs/->swagger Long) :x-nullable true))
(fact "adds allowEmptyValue when for query and formData as defined by the spec"
(rsjs/->swagger (s/maybe Long) {:in :query} :swagger) => (assoc (rsjs/->swagger Long) :allowEmptyValue true)
(rsjs/->swagger (s/maybe Long) {:in :formData} :swagger) => (assoc (rsjs/->swagger Long) :allowEmptyValue true))
(rsjs/->swagger (s/maybe Long) {:in :query}) => (assoc (rsjs/->swagger Long) :allowEmptyValue true)
(rsjs/->swagger (s/maybe Long) {:in :formData}) => (assoc (rsjs/->swagger Long) :allowEmptyValue true))
(fact "uses wrapped value by default with x-nullable true with body"
(rsjs/->swagger (s/maybe Long) {:in :body} :swagger) => (assoc (rsjs/->swagger Long) :x-nullable true))
(rsjs/->swagger (s/maybe Long) {:in :body}) => (assoc (rsjs/->swagger Long) :x-nullable true))
(fact "uses wrapped value for other parameters"
(rsjs/->swagger (s/maybe Long) {:in :header} :swagger) => (rsjs/->swagger Long)
(rsjs/->swagger (s/maybe Long) {:in :path} :swagger) => (rsjs/->swagger Long)))
(rsjs/->swagger (s/maybe Long) {:in :header}) => (rsjs/->swagger Long)
(rsjs/->swagger (s/maybe Long) {:in :path}) => (rsjs/->swagger Long)))

(fact "s/defrecord"
(rsjs/->swagger User) => {:type "object",
Expand Down Expand Up @@ -122,11 +122,11 @@

(fact "s/Any"
(rsjs/->swagger s/Any) => {}
(rsjs/->swagger s/Any {:in :body} :swagger) => {}
(rsjs/->swagger s/Any {:in :header} :swagger) => {:type "string"}
(rsjs/->swagger s/Any {:in :path} :swagger) => {:type "string"}
(rsjs/->swagger s/Any {:in :query} :swagger) => {:type "string", :allowEmptyValue true}
(rsjs/->swagger s/Any {:in :formData} :swagger) => {:type "string", :allowEmptyValue true})
(rsjs/->swagger s/Any {:in :body}) => {}
(rsjs/->swagger s/Any {:in :header}) => {:type "string"}
(rsjs/->swagger s/Any {:in :path}) => {:type "string"}
(rsjs/->swagger s/Any {:in :query}) => {:type "string", :allowEmptyValue true}
(rsjs/->swagger s/Any {:in :formData}) => {:type "string", :allowEmptyValue true})

(fact "s/conditional"
(rsjs/->swagger (s/conditional (constantly true) Long (constantly false) String))
Expand Down

0 comments on commit 94f1d01

Please sign in to comment.