From 91cd37abaadf396bbb82493430c3407c53e478d8 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Tue, 26 Jan 2021 19:48:18 +0200 Subject: [PATCH] Move number formats to malli.swagger --- src/malli/json_schema.cljc | 24 +++++++---------- src/malli/swagger.cljc | 24 ++++++++++++++--- test/malli/json_schema_test.cljc | 45 +++++++++++++++----------------- test/malli/swagger_test.cljc | 26 ++++++++++++++---- 4 files changed, 73 insertions(+), 46 deletions(-) diff --git a/src/malli/json_schema.cljc b/src/malli/json_schema.cljc index fa09e875f..674c9ff02 100644 --- a/src/malli/json_schema.cljc +++ b/src/malli/json_schema.cljc @@ -22,21 +22,17 @@ (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" :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" :format "float"}) -(defmethod accept 'double? [_ _ _ _] {:type "number" :format "double"}) +(defmethod accept 'number? [_ _ _ _] {:type "number"}) +(defmethod accept 'integer? [_ _ _ _] {:type "integer"}) +(defmethod accept 'int? [_ _ _ _] {:type "integer"}) +(defmethod accept 'pos-int? [_ _ _ _] {:type "integer", :minimum 1}) +(defmethod accept 'neg-int? [_ _ _ _] {:type "integer", :maximum -1}) +(defmethod accept 'nat-int? [_ _ _ _] {:type "integer", :minimum 0}) +(defmethod accept 'float? [_ _ _ _] {:type "number"}) +(defmethod accept 'double? [_ _ _ _] {:type "number"}) (defmethod accept 'pos? [_ _ _ _] {:type "number" :exclusiveMininum 0}) (defmethod accept 'neg? [_ _ _ _] {:type "number" :exclusiveMaximum 0}) (defmethod accept 'boolean? [_ _ _ _] {:type "boolean"}) @@ -71,7 +67,7 @@ (defmethod accept 'empty? [_ _ _ _] {:type "array" :maxItems 0 :minItems 0}) (defmethod accept 'associative? [_ _ _ _] {:type "object"}) (defmethod accept 'sequential? [_ _ _ _] {:type "array"}) -(defmethod accept 'ratio? [_ _ _ _] {:type "number"}) +#?(:clj (defmethod accept 'ratio? [_ _ _ _] {:type "number"})) (defmethod accept 'bytes? [_ _ _ _] {:type "string" :format "byte"}) (defmethod accept :> [_ _ [value] _] {:type "number" :exclusiveMinimum value}) @@ -109,7 +105,7 @@ (merge {:type "integer"} (-> schema m/properties (select-keys [:min :max]) (set/rename-keys {:min :minimum, :max :maximum})))) (defmethod accept :double [_ schema _ _] - (merge {:type "number" :format "double"} + (merge {:type "number"} (-> schema m/properties (select-keys [:min :max]) (set/rename-keys {:min :minimum, :max :maximum})))) (defmethod accept :boolean [_ _ _ _] {:type "boolean"}) diff --git a/src/malli/swagger.cljc b/src/malli/swagger.cljc index 831ad64af..25c24ab06 100644 --- a/src/malli/swagger.cljc +++ b/src/malli/swagger.cljc @@ -1,6 +1,7 @@ (ns malli.swagger (:require [malli.json-schema :as json-schema] - [malli.core :as m])) + [malli.core :as m] + [clojure.set :as set])) (defprotocol SwaggerSchema (-accept [this children options] "transforms schema to Swagger Schema")) @@ -8,8 +9,6 @@ (defmulti accept (fn [name _schema _children _options] name) :default ::default) (defmethod accept ::default [name schema children options] (json-schema/accept name schema children options)) -(defmethod accept 'float? [_ _ _ _] {:type "number" :format "float"}) -(defmethod accept 'double? [_ _ _ _] {:type "number" :format "double"}) (defmethod accept 'nil? [_ _ _ _] {}) (defmethod accept :and [_ _ children _] (assoc (first children) :x-allOf children)) @@ -22,6 +21,25 @@ (defmethod accept :tuple [_ _ children _] {:type "array" :items {} :x-items children}) +;; Number formats are only defined in Swagger/OpenAPI spec. + +(defmethod accept 'number? [_ _ _ _] {:type "number" :format "double"}) +(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" :format "float"}) +(defmethod accept 'double? [_ _ _ _] {:type "number" :format "double"}) + +(defmethod accept :int [_ schema _ _] + (merge {:type "integer" :format "int64"} + (-> schema m/properties (select-keys [:min :max]) (set/rename-keys {:min :minimum, :max :maximum})))) + +(defmethod accept :double [_ schema _ _] + (merge {:type "number" :format "double"} + (-> schema m/properties (select-keys [:min :max]) (set/rename-keys {:min :minimum, :max :maximum})))) + (defn- -swagger-walker [schema _ children options] (let [p (merge (m/type-properties schema) (m/properties schema))] (or (json-schema/unlift p :swagger) diff --git a/test/malli/json_schema_test.cljc b/test/malli/json_schema_test.cljc index ccd83fc07..b51a533cc 100644 --- a/test/malli/json_schema_test.cljc +++ b/test/malli/json_schema_test.cljc @@ -7,8 +7,8 @@ (def expectations [;; predicates - [pos-int? {:type "integer", :format "int64", :minimum 1}] - [float? {:type "number" :format "float"}] + [pos-int? {:type "integer", :minimum 1}] + [float? {:type "number"}] ;; comparators [[:> 6] {:type "number", :exclusiveMinimum 6}] [[:>= 6] {:type "number", :minimum 6}] @@ -16,9 +16,9 @@ [[:<= 6] {:type "number", :maximum 6}] [[:= "x"] {:const "x"}] ;; base - [[:and int? pos-int?] {:allOf [{:type "integer", :format "int64"} - {:type "integer", :format "int64" :minimum 1}]}] - [[:or int? string?] {:anyOf [{:type "integer", :format "int64"} {:type "string"}]}] + [[:and int? pos-int?] {:allOf [{:type "integer"} + {:type "integer", :minimum 1}]}] + [[:or int? string?] {:anyOf [{:type "integer"} {:type "string"}]}] [[:map [:a string?] [:b {:optional true} string?] @@ -33,8 +33,7 @@ [:human [:map {:gen/fmap '#(assoc % :type :human)} [:type keyword?] [:name string?] [:address [:map [:country keyword?]]]]]] {:oneOf [{:type "object", :properties {:type {:type "string"} - :size {:type "integer" - :format "int64"}}, + :size {:type "integer"}}, :required [:type :size]} {:type "object", :properties {:type {:type "string"}, @@ -58,16 +57,17 @@ [[: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", :format "double", :minimum 1, :maximum 4}] + [[:double {:min 1, :max 4}] {:type "number", :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"}]) + [integer? {:type "integer"}] + #?@(:clj [[ratio? {:type "number"}] + [rational? {:type "number"}]] + :cljs []) ;; protocols [(reify m/Schema @@ -109,7 +109,7 @@ [:x5 {:json-schema {:type "x-string"}} [:string {:json-schema {:default "x"}}]]])))) (testing "with properties" - (is (= {:allOf [{:type "integer", :format "int64"}] + (is (= {:allOf [{:type "integer"}] :title "age" :description "blabla" :default 42} @@ -117,7 +117,7 @@ [:and {:title "age" :description "blabla" :default 42} int?]))) - (is (= {:allOf [{:type "integer", :format "int64"}] + (is (= {:allOf [{:type "integer"}] :title "age2" :description "blabla2" :default 422 @@ -137,9 +137,9 @@ (testing "merge" (is (= {:title "merge", :type "object", - :properties {:x {:type "integer", :format "int64", :example 42}, - :y {:type "integer", :format "int64"}, - :z {:type "integer", :format "int64"}}, + :properties {:x {:type "integer", :example 42}, + :y {:type "integer"}, + :z {:type "integer"}}, :required [:x :y :z]} (json-schema/transform [:merge {:title "merge"} @@ -150,8 +150,8 @@ (testing "union" (is (= {:title "union", :type "object", - :properties {:x {:anyOf [{:type "integer", :format "int64"} {:type "string"}]} - :y {:type "integer", :format "int64"}}, + :properties {:x {:anyOf [{:type "integer"} {:type "string"}]} + :y {:type "integer"}}, :required [:x :y]} (json-schema/transform [:union {:title "union"} @@ -162,7 +162,7 @@ (testing "select-keys" (is (= {:title "select-keys" :type "object" - :properties {:x {:type "integer", :format "int64"}} + :properties {:x {:type "integer"}} :required [:x]} (json-schema/transform [:select-keys {:title "select-keys"} @@ -182,13 +182,11 @@ :description {:type "string"}, :origin {:oneOf [{:$ref "#/definitions/Country"} {:type "null"}]}, :price {:type "integer" - :format "int64" :minimum 1}}, :required [:name :origin :price]}, "OrderLine" {:type "object", :properties {:burger {:$ref "#/definitions/Burger"}, - :amount {:type "integer" - :format "int64"}}, + :amount {:type "integer"}}, :required [:burger :amount]}, "Order" {:type "object", :properties {:lines {:type "array" @@ -197,8 +195,7 @@ :properties {:delivered {:type "boolean"}, :address {:type "object", :properties {:street {:type "string"}, - :zip {:type "integer", - :format "int64"}, + :zip {:type "integer"}, :country {:$ref "#/definitions/Country"}}, :required [:street :zip :country]}}, :required [:delivered :address]}}, diff --git a/test/malli/swagger_test.cljc b/test/malli/swagger_test.cljc index 903522550..46821c822 100644 --- a/test/malli/swagger_test.cljc +++ b/test/malli/swagger_test.cljc @@ -1,5 +1,6 @@ (ns malli.swagger-test (:require [clojure.test :refer [deftest testing is are]] + [malli.core-test] [malli.swagger :as swagger] [malli.core :as m] [malli.util :as mu])) @@ -9,10 +10,10 @@ [pos-int? {:type "integer", :format "int64", :minimum 1}] [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}] ;; base [[:and int? pos-int?] {:type "integer" :format "int64" @@ -66,11 +67,26 @@ {:type "string"}]}] [[: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", :format "int64", :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"}] + #?@(:clj [[ratio? {:type "number"}] + [rational? {:type "number"}]] + :cljs []) ;; protocols [(reify m/Schema - (-type-properties [_]) (-properties [_]) + (-type [_]) + (-form [_]) + (-type-properties [_]) + (-validator [_] int?) (-walk [t w p o] (m/-outer w t p nil o)) swagger/SwaggerSchema (-accept [_ _ _] {:type "custom"})) {:type "custom"}]