Skip to content

Commit

Permalink
Set JSON Schema types and formats for numbers properly
Browse files Browse the repository at this point in the history
- Note that number formats aren't mentioned in JSON Schema spec, but are
  from OpenAPI spec.
- Comparators shouldn't automatically set format to double.
- Set format double for :double and double?.
- decimal? is BigDecimal, so that is not a double.
- Fix rational? type to number, double isn't valid type
- Fix ratio? type to number, ratios aren't integers.
  • Loading branch information
Deraen committed Jan 26, 2021
1 parent 516e923 commit 1e524eb
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 17 deletions.
27 changes: 16 additions & 11 deletions src/malli/json_schema.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,21 @@

(defmulti accept (fn [name _schema _children _options] name) :default ::default)

;; Note, format value for integer/number is from OpenAPI spec.
;; JSON Schema spesifies formats for strings like uuid and date-time, but
;; not numbers, but custom values are allowed.

(defmethod accept ::default [_ _ _ _] {})
(defmethod accept 'any? [_ _ _ _] {})
(defmethod accept 'some? [_ _ _ _] {})
(defmethod accept 'number? [_ _ _ _] {:type "number" :format "double"})
(defmethod accept 'integer? [_ _ _ _] {:type "integer"})
(defmethod accept 'integer? [_ _ _ _] {:type "integer" :format "int32"})
(defmethod accept 'int? [_ _ _ _] {:type "integer" :format "int64"})
(defmethod accept 'pos-int? [_ _ _ _] {:type "integer", :format "int64", :minimum 1})
(defmethod accept 'neg-int? [_ _ _ _] {:type "integer", :format "int64", :maximum -1})
(defmethod accept 'nat-int? [_ _ _ _] {:type "integer", :format "int64" :minimum 0})
(defmethod accept 'float? [_ _ _ _] {:type "number"})
(defmethod accept 'double? [_ _ _ _] {:type "number"})
(defmethod accept 'float? [_ _ _ _] {:type "number" :format "float"})
(defmethod accept 'double? [_ _ _ _] {:type "number" :format "double"})
(defmethod accept 'pos? [_ _ _ _] {:type "number" :exclusiveMininum 0})
(defmethod accept 'neg? [_ _ _ _] {:type "number" :exclusiveMaximum 0})
(defmethod accept 'boolean? [_ _ _ _] {:type "boolean"})
Expand All @@ -48,7 +52,7 @@
(defmethod accept 'qualified-symbol? [_ _ _ _] {:type "string"})
(defmethod accept 'uuid? [_ _ _ _] {:type "string" :format "uuid"})
(defmethod accept 'uri? [_ _ _ _] {:type "string" :format "uri"})
(defmethod accept 'decimal? [_ _ _ _] {:type "number" :format "double"})
(defmethod accept 'decimal? [_ _ _ _] {:type "number"})
(defmethod accept 'inst? [_ _ _ _] {:type "string" :format "date-time"})
(defmethod accept 'seqable? [_ _ _ _] {:type "array"})
(defmethod accept 'indexed? [_ _ _ _] {:type "array"})
Expand All @@ -62,18 +66,18 @@
(defmethod accept 'false? [_ _ _ _] {:type "boolean"})
(defmethod accept 'true? [_ _ _ _] {:type "boolean"})
(defmethod accept 'zero? [_ _ _ _] {:type "integer"})
#?(:clj (defmethod accept 'rational? [_ _ _ _] {:type "double"}))
#?(:clj (defmethod accept 'rational? [_ _ _ _] {:type "number"}))
(defmethod accept 'coll? [_ _ _ _] {:type "object"})
(defmethod accept 'empty? [_ _ _ _] {:type "array" :maxItems 0 :minItems 0})
(defmethod accept 'associative? [_ _ _ _] {:type "object"})
(defmethod accept 'sequential? [_ _ _ _] {:type "array"})
(defmethod accept 'ratio? [_ _ _ _] {:type "integer"})
(defmethod accept 'ratio? [_ _ _ _] {:type "number"})
(defmethod accept 'bytes? [_ _ _ _] {:type "string" :format "byte"})

(defmethod accept :> [_ _ [value] _] {:type "number" :format "double" :exclusiveMinimum value})
(defmethod accept :>= [_ _ [value] _] {:type "number" :format "double" :minimum value})
(defmethod accept :< [_ _ [value] _] {:type "number" :format "double" :exclusiveMaximum value})
(defmethod accept :<= [_ _ [value] _] {:type "number" :format "double" :maximum value})
(defmethod accept :> [_ _ [value] _] {:type "number" :exclusiveMinimum value})
(defmethod accept :>= [_ _ [value] _] {:type "number" :minimum value})
(defmethod accept :< [_ _ [value] _] {:type "number" :exclusiveMaximum value})
(defmethod accept :<= [_ _ [value] _] {:type "number" :maximum value})
(defmethod accept := [_ _ [value] _] {:const value})
(defmethod accept :not= [_ _ _ _] {})

Expand Down Expand Up @@ -105,7 +109,8 @@
(merge {:type "integer"} (-> schema m/properties (select-keys [:min :max]) (set/rename-keys {:min :minimum, :max :maximum}))))

(defmethod accept :double [_ schema _ _]
(merge {:type "number"} (-> schema m/properties (select-keys [:min :max]) (set/rename-keys {:min :minimum, :max :maximum}))))
(merge {:type "number" :format "double"}
(-> schema m/properties (select-keys [:min :max]) (set/rename-keys {:min :minimum, :max :maximum}))))

(defmethod accept :boolean [_ _ _ _] {:type "boolean"})
(defmethod accept :keyword [_ _ _ _] {:type "string"})
Expand Down
16 changes: 10 additions & 6 deletions test/malli/json_schema_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
(def expectations
[;; predicates
[pos-int? {:type "integer", :format "int64", :minimum 1}]
[float? {:type "number"}]
[float? {:type "number" :format "float"}]
;; comparators
[[:> 6] {:type "number", :format "double", :exclusiveMinimum 6}]
[[:>= 6] {:type "number", :format "double", :minimum 6}]
[[:< 6] {:type "number", :format "double", :exclusiveMaximum 6}]
[[:<= 6] {:type "number", :format "double", :maximum 6}]
[[:> 6] {:type "number", :exclusiveMinimum 6}]
[[:>= 6] {:type "number", :minimum 6}]
[[:< 6] {:type "number", :exclusiveMaximum 6}]
[[:<= 6] {:type "number", :maximum 6}]
[[:= "x"] {:const "x"}]
;; base
[[:and int? pos-int?] {:allOf [{:type "integer", :format "int64"}
Expand Down Expand Up @@ -58,12 +58,16 @@
[[:re "^[a-z]+\\.[a-z]+$"] {:type "string", :pattern "^[a-z]+\\.[a-z]+$"}]
[[:string {:min 1, :max 4}] {:type "string", :minLength 1, :maxLength 4}]
[[:int {:min 1, :max 4}] {:type "integer", :minimum 1, :maximum 4}]
[[:double {:min 1, :max 4}] {:type "number", :minimum 1, :maximum 4}]
[[:double {:min 1, :max 4}] {:type "number", :format "double", :minimum 1, :maximum 4}]
[:keyword {:type "string"}]
[:qualified-keyword {:type "string"}]
[:symbol {:type "string"}]
[:qualified-symbol {:type "string"}]
[:uuid {:type "string", :format "uuid"}]

[integer? {:type "integer" :format "int32"}]
[ratio? {:type "number"}]
#?(:clj [rational? {:type "number"}])
;; protocols
[(reify
m/Schema
Expand Down

0 comments on commit 1e524eb

Please sign in to comment.