From 6b64ba451dea654f4edd0a85f5e3d23ac8fb0b35 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 29 Jun 2024 16:52:44 +0300 Subject: [PATCH 1/8] . --- .../com/gfredericks/test/chuck/checking.clj | 19 +++ .../com.gfredericks/test.chuck/config.edn | 4 + .clj-kondo/prismatic/schema/config.edn | 1 + .gitignore | 1 + src/spec_tools/core.cljc | 139 +++++++++--------- 5 files changed, 94 insertions(+), 70 deletions(-) create mode 100644 .clj-kondo/com.gfredericks/test.chuck/clj_kondo/com/gfredericks/test/chuck/checking.clj create mode 100644 .clj-kondo/com.gfredericks/test.chuck/config.edn create mode 100644 .clj-kondo/prismatic/schema/config.edn diff --git a/.clj-kondo/com.gfredericks/test.chuck/clj_kondo/com/gfredericks/test/chuck/checking.clj b/.clj-kondo/com.gfredericks/test.chuck/clj_kondo/com/gfredericks/test/chuck/checking.clj new file mode 100644 index 00000000..8df553fb --- /dev/null +++ b/.clj-kondo/com.gfredericks/test.chuck/clj_kondo/com/gfredericks/test/chuck/checking.clj @@ -0,0 +1,19 @@ +(ns clj-kondo.com.gfredericks.test.chuck.checking + (:require + [clj-kondo.hooks-api :as api])) + +(defn checking + [{{:keys [children]} :node}] + (let [[_checking desc & opt+bindings+body] children + [opts binding-vec & body] (if (api/vector-node? (first opt+bindings+body)) + (into [(api/map-node {})] opt+bindings+body) + opt+bindings+body)] + (when-not (even? (count (:children binding-vec))) + (throw (ex-info "checking requires an even number of bindings" {}))) + {:node (api/list-node + (list* + (api/token-node 'let) + (api/vector-node (into [(api/token-node (symbol (gensym "_checking-desc"))) desc] + (:children binding-vec))) + opts + body))})) diff --git a/.clj-kondo/com.gfredericks/test.chuck/config.edn b/.clj-kondo/com.gfredericks/test.chuck/config.edn new file mode 100644 index 00000000..79dcc169 --- /dev/null +++ b/.clj-kondo/com.gfredericks/test.chuck/config.edn @@ -0,0 +1,4 @@ +{:hooks + {:analyze-call + {com.gfredericks.test.chuck.clojure-test/checking + clj-kondo.com.gfredericks.test.chuck.checking/checking}}} diff --git a/.clj-kondo/prismatic/schema/config.edn b/.clj-kondo/prismatic/schema/config.edn new file mode 100644 index 00000000..2c341bce --- /dev/null +++ b/.clj-kondo/prismatic/schema/config.edn @@ -0,0 +1 @@ +{:lint-as {schema.test/deftest clojure.test/deftest}} diff --git a/.gitignore b/.gitignore index c604ffc9..aae9b8cd 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ /target pom.xml pom.xml.asc +.cache \ No newline at end of file diff --git a/src/spec_tools/core.cljc b/src/spec_tools/core.cljc index 5ffef1ea..9a1df39b 100644 --- a/src/spec_tools/core.cljc +++ b/src/spec_tools/core.cljc @@ -13,9 +13,8 @@ [cljs.reader] [cljs.spec.gen.alpha :as gen]])) (:import - #?@(:clj - [(clojure.lang AFn IFn Var) - (java.io Writer)]))) + #?@(:clj [(clojure.lang IFn) + (java.io Writer)]))) ;; ;; helpers @@ -47,12 +46,12 @@ if no spec was found." [name-or-spec] (or - (and (spec? name-or-spec) name-or-spec) - (get-spec name-or-spec) - (throw - (ex-info - (str "can't coerce to spec: " name-or-spec) - {:name-or-spec name-or-spec})))) + (and (spec? name-or-spec) name-or-spec) + (get-spec name-or-spec) + (throw + (ex-info + (str "can't coerce to spec: " name-or-spec) + {:name-or-spec name-or-spec})))) (defn ^:skip-wiki serialize "Writes specs into a string that can be read by the reader. @@ -144,36 +143,36 @@ (def json-transformer "Transformer that transforms data between JSON and EDN." (type-transformer - {:name :json - :decoders stt/json-type-decoders - :encoders stt/json-type-encoders - :default-encoder stt/any->any})) + {:name :json + :decoders stt/json-type-decoders + :encoders stt/json-type-encoders + :default-encoder stt/any->any})) (def string-transformer "Transformer that transforms data between Strings and EDN." (type-transformer - {:name :string - :decoders stt/string-type-decoders - :encoders stt/string-type-encoders - :default-encoder stt/any->any})) + {:name :string + :decoders stt/string-type-decoders + :encoders stt/string-type-encoders + :default-encoder stt/any->any})) (def strip-extra-keys-transformer "Transformer that drop extra keys from `s/keys` specs." (type-transformer - {:name ::strip-extra-keys - :decoders stt/strip-extra-keys-type-decoders})) + {:name ::strip-extra-keys + :decoders stt/strip-extra-keys-type-decoders})) (def strip-extra-values-transformer "Transformer that drop extra values from `s/tuple` specs." (type-transformer - {:name ::strip-extra-values - :decoders stt/strip-extra-values-type-decoders})) + {:name ::strip-extra-values + :decoders stt/strip-extra-values-type-decoders})) (def fail-on-extra-keys-transformer "Transformer that fails on extra keys in `s/keys` specs." (type-transformer - {:name ::fail-on-extra-keys - :decoders stt/fail-on-extra-keys-type-decoders})) + {:name ::fail-on-extra-keys + :decoders stt/fail-on-extra-keys-type-decoders})) ;; ;; Transforming @@ -291,20 +290,20 @@ (defmethod walk :or [{:keys [::parse/items]} value accept options] (reduce - (fn [v item] - (let [transformed (accept item v options) - valid? (some-> item :spec (s/valid? transformed))] - (if valid? - (reduced transformed) - transformed))) - value items)) + (fn [v item] + (let [transformed (accept item v options) + valid? (some-> item :spec (s/valid? transformed))] + (if valid? + (reduced transformed) + transformed))) + value items)) (defmethod walk :and [{:keys [::parse/items]} value accept options] (reduce - (fn [v item] - (let [transformed (accept item v options)] - transformed)) - value items)) + (fn [v item] + (let [transformed (accept item v options)] + transformed)) + value items)) (defmethod walk :nilable [{:keys [::parse/item]} value accept options] (accept item value options)) @@ -334,23 +333,23 @@ (defmethod walk :map [{:keys [::parse/key->spec]} value accept options] (if (map? value) (reduce-kv - (fn [acc k v] - (let [spec (if (qualified-keyword? k) (s/get-spec k) (s/get-spec (get key->spec k))) - value (if spec (accept spec v options) v)] - (assoc acc k value))) - value - value) + (fn [acc k v] + (let [spec (if (qualified-keyword? k) (s/get-spec k) (s/get-spec (get key->spec k))) + value (if spec (accept spec v options) v)] + (assoc acc k value))) + value + value) value)) (defmethod walk :map-of [{:keys [::parse/key ::parse/value]} data accept options] (if (map? data) (reduce-kv - (fn [acc k v] - (let [k' (accept key k options) - v' (accept value v options)] - (assoc acc k' v'))) - (empty data) - data) + (fn [acc k v] + (let [k' (accept key k options) + v' (accept value v options)] + (assoc acc k' v'))) + (empty data) + data) data)) (defmethod walk :multi-spec [{:keys [::parse/key ::parse/dispatch]} data accept options] @@ -367,16 +366,16 @@ (defn- extra-spec-map [data] (->> (dissoc data :form :spec) (reduce - (fn [acc [k v]] - (if (= "spec-tools.parse" (namespace k)) acc (assoc acc k v))) - {}))) + (fn [acc [k v]] + (if (= "spec-tools.parse" (namespace k)) acc (assoc acc k v))) + {}))) (defn- fail-on-invoke [spec] (throw - (ex-info - (str - "Can't invoke spec with a non-function predicate: " spec) - {:spec spec}))) + (ex-info + (str + "Can't invoke spec with a non-function predicate: " spec) + {:spec spec}))) (defn- leaf? [spec] (:leaf? (into-spec spec))) @@ -460,8 +459,8 @@ spec-reason (:reason this) with-reason (fn [problem] (cond-> problem - (and spec-reason (not (:reason problem))) - (assoc :reason spec-reason)))] + (and spec-reason (not (:reason problem))) + (assoc :reason spec-reason)))] (if problems (map with-reason problems)))) @@ -469,8 +468,8 @@ (if-let [gen (:gen this)] (gen) (or - (gen/gen-for-pred spec) - (s/gen* (or (s/spec? spec) (s/specize* spec)) overrides path rmap)))) + (gen/gen-for-pred spec) + (s/gen* (or (s/spec? spec) (s/specize* spec)) overrides path rmap)))) (with-gen* [this gfn] (assoc this :gen gfn)) @@ -488,9 +487,9 @@ [^Spec t ^Writer w] (.write w (str "#Spec" (clojure.core/merge - (select-keys t [:form]) - (if (:type t) (select-keys t [:type])) - (extra-spec-map t)))))) + (select-keys t [:form]) + (if (:type t) (select-keys t [:type])) + (extra-spec-map t)))))) (defn spec? [x] (if (instance? Spec x) x)) @@ -550,7 +549,7 @@ type (if (contains? m :type) type (:type info)) name (-> spec meta ::s/name) record (map->Spec - (clojure.core/merge m info {:spec spec :form form :type type :leaf? (parse/leaf-type? type)}))] + (clojure.core/merge m info {:spec spec :form form :type type :leaf? (parse/leaf-type? type)}))] (cond-> record name (with-meta {::s/name name})))) #?(:clj @@ -575,10 +574,10 @@ form# '~(impl/resolve-form &env pred)] (assert (map? info#) (str "spec info should be a map, was: " info#)) (create-spec - (clojure.core/merge - info# - {:form form# - :spec ~pred})))))) + (clojure.core/merge + info# + {:form form# + :spec ~pred})))))) (defn- into-spec [x] (cond @@ -613,11 +612,11 @@ (gen* [_ overrides path rmap] (s/gen* merge-spec overrides path rmap)))] (create-spec - (clojure.core/merge - {:spec spec - :form spec-form - :type :map} - (apply merge-with set/union form-keys))))) + (clojure.core/merge + {:spec spec + :form spec-form + :type :map} + (apply merge-with set/union form-keys))))) #?(:clj (defmacro merge [& forms] From 534cd2da42bf70c97475be07f558599daddd4b71 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 29 Jun 2024 16:56:02 +0300 Subject: [PATCH 2/8] . --- test/cljc/spec_tools/core_test.cljc | 214 ++++++++++++++-------------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/test/cljc/spec_tools/core_test.cljc b/test/cljc/spec_tools/core_test.cljc index bbc2a70d..82964ca7 100644 --- a/test/cljc/spec_tools/core_test.cljc +++ b/test/cljc/spec_tools/core_test.cljc @@ -44,16 +44,16 @@ (is (= ::regex (st/spec-name (s/get-spec ::regex)))) (is (= ::spec (st/spec-name (s/get-spec ::spec)))) (is (= ::overridden (st/spec-name - (st/spec - {:spec (s/get-spec ::spec) - :name ::overridden}))))) + (st/spec + {:spec (s/get-spec ::spec) + :name ::overridden}))))) (deftest spec-description-test (is (= nil (st/spec-description #{1 2}))) (is (= "description" (st/spec-description - (st/spec - {:spec (s/get-spec ::spec) - :description "description"}))))) + (st/spec + {:spec (s/get-spec ::spec) + :description "description"}))))) (deftest spec?-test (testing "spec" @@ -90,17 +90,17 @@ (testing ":form default to ::s/unknown" (let [spec (st/create-spec - {:name "positive?" - :spec (fn [x] (pos? x))})] + {:name "positive?" + :spec (fn [x] (pos? x))})] (is (st/spec? spec)) (is (= (:form spec) ::s/unknown)))) (testing ":form and :type can be provided" (let [spec (st/create-spec - {:name "positive?" - :spec (fn [x] (pos? x)) - :type :long - :form `(fn [x] (pos? x))})] + {:name "positive?" + :spec (fn [x] (pos? x)) + :type :long + :form `(fn [x] (pos? x))})] (is (st/spec? spec)))))) (testing "registered specs are inlined" @@ -121,42 +121,42 @@ (st/spec integer?) `(spec-tools.core/spec - {:spec integer? - :type :long - :leaf? true}) + {:spec integer? + :type :long + :leaf? true}) (st/spec #{pos? neg?}) `(spec-tools.core/spec - {:spec #{neg? pos?} - :type nil - :leaf? true}) + {:spec #{neg? pos?} + :type nil + :leaf? true}) (st/spec ::string) `(spec-tools.core/spec - {:spec string? - :type :string - :leaf? true}) + {:spec string? + :type :string + :leaf? true}) (st/spec ::lat) `(spec-tools.core/spec - {:spec (spec-tools.core/spec - {:spec double? - :type :double - :leaf? true}) - :type :double - :leaf? true}) + {:spec (spec-tools.core/spec + {:spec double? + :type :double + :leaf? true}) + :type :double + :leaf? true}) (st/spec (fn [x] (> x 10))) `(spec-tools.core/spec - {:spec (clojure.core/fn [~'x] (> ~'x 10)) - :type nil - :leaf? true}) + {:spec (clojure.core/fn [~'x] (> ~'x 10)) + :type nil + :leaf? true}) (st/spec #(> % 10)) `(spec-tools.core/spec - {:spec (clojure.core/fn [~'%] (> ~'% 10)) - :type nil - :leaf? true}))) + {:spec (clojure.core/fn [~'%] (> ~'% 10)) + :type nil + :leaf? true}))) (testing "wrapped predicate work as a predicate" (is (true? (my-integer? 1))) @@ -274,10 +274,10 @@ (s/def ::my-spec (st/spec - {:spec #(and (simple-keyword? %) (-> % name str/lower-case keyword (= %))) - :description "a lowercase keyword, encoded in uppercase in string-mode" - :decode/string #(-> %2 name str/lower-case keyword) - :encode/string #(-> %2 name str/upper-case)})) + {:spec #(and (simple-keyword? %) (-> % name str/lower-case keyword (= %))) + :description "a lowercase keyword, encoded in uppercase in string-mode" + :decode/string #(-> %2 name str/lower-case keyword) + :encode/string #(-> %2 name str/upper-case)})) (s/def ::my-spec-map (s/keys :req [::my-spec])) (s/def ::my-type (st/spec keyword?)) @@ -325,14 +325,14 @@ (is (= encoded (st/encode ::my-type-map decoded st/string-transformer))))))) (testing "roundtrip" (is (= :kikka (as-> "KikKa" $ - (st/decode ::my-spec $ st/string-transformer)))) + (st/decode ::my-spec $ st/string-transformer)))) (is (= "KIKKA" (as-> "KikKa" $ - (st/decode ::my-spec $ st/string-transformer) - (st/encode ::my-spec $ st/string-transformer)))) + (st/decode ::my-spec $ st/string-transformer) + (st/encode ::my-spec $ st/string-transformer)))) (is (= :kikka (as-> "KikKa" $ - (st/decode ::my-spec $ st/string-transformer) - (st/encode ::my-spec $ st/string-transformer) - (st/decode ::my-spec $ st/string-transformer))))) + (st/decode ::my-spec $ st/string-transformer) + (st/encode ::my-spec $ st/string-transformer) + (st/decode ::my-spec $ st/string-transformer))))) (testing "encode and decode also unform" (is (= "1" (st/encode ::regex 1 st/string-transformer))) (is (= 1 (st/decode ::regex "1" st/string-transformer)))))) @@ -340,8 +340,8 @@ (deftest late-bind-transformers-on-decode (let [times (atom 0) spec (st/spec - {:spec int? - :decode/string (fn [_ value] (swap! times inc) value)})] + {:spec int? + :decode/string (fn [_ value] (swap! times inc) value)})] (st/decode spec 1 st/string-transformer) (is (= 1 @times)))) @@ -355,17 +355,17 @@ (st/-options (st/type-transformer st/json-transformer st/json-transformer)))) (is (= {:c3 'kikka, :c2 :abba} (st/coerce - (s/keys :req-un [::c3 ::c2]) - {:c3 "kikka", :c2 "abba"} - (st/type-transformer st/json-transformer st/string-transformer)) + (s/keys :req-un [::c3 ::c2]) + {:c3 "kikka", :c2 "abba"} + (st/type-transformer st/json-transformer st/string-transformer)) (st/coerce - (s/keys :req-un [::c3 ::c2]) - {:c3 "kikka", :c2 "abba"} - (st/type-transformer st/string-transformer st/json-transformer)))) + (s/keys :req-un [::c3 ::c2]) + {:c3 "kikka", :c2 "abba"} + (st/type-transformer st/string-transformer st/json-transformer)))) (is (= :bumblebee (st/-name (st/type-transformer - st/string-transformer - st/json-transformer - {:name :bumblebee}))))) + st/string-transformer + st/json-transformer + {:name :bumblebee}))))) (deftest coercion-test (testing "predicates" @@ -414,8 +414,8 @@ (is (= "1" (st/coerce (s/tuple keyword? int?) "1" st/string-transformer))) (is (= [:kikka 1 "2"] (st/coerce (s/tuple keyword? int?) ["kikka" "1" "2"] st/string-transformer))) (is (= [:kikka 1] (st/coerce (s/tuple keyword? int?) ["kikka" "1" "2"] (st/type-transformer - st/string-transformer - st/strip-extra-values-transformer))))) + st/string-transformer + st/strip-extra-values-transformer))))) (testing "referenced specs, #165" (s/def ::pos? (st/spec {:spec (partial pos?), :decode/string transform/string->long})) (is (= 1 (st/coerce (s/and ::pos?) "1" st/string-transformer))) @@ -426,11 +426,11 @@ (is (= 1 (st/coerce (s/nilable ::pos?) "1" st/string-transformer)))) (testing "composed" (let [spec (s/nilable - (s/nilable - (s/map-of - keyword? - (s/or :keys (s/keys :req-un [::c1]) - :ks (s/coll-of (s/and int?) :into #{}))))) + (s/nilable + (s/map-of + keyword? + (s/or :keys (s/keys :req-un [::c1]) + :ks (s/coll-of (s/and int?) :into #{}))))) value {"keys" {:c1 "1" ::c2 "kikka"} "keys2" {:c1 true} "ints" [1 "1" "invalid" "3"]}] @@ -471,13 +471,13 @@ (s/def ::multi-test (s/multi-spec multi-test :group)) (is (= {:group :a - :lat 12.0} + :lat 12.0} (st/coerce ::multi-test {:group "a" - :lat "12"} + :lat "12"} st/string-transformer))) - (is (= {:group :b + (is (= {:group :b :language :clojure} - (st/coerce ::multi-test {:group "b" + (st/coerce ::multi-test {:group "b" :language "clojure"} st/string-transformer)))))) @@ -565,11 +565,11 @@ (testing "deeply nested" (is (= {:persons [{:weight 80, :height 200}]} (st/select-spec - (s/keys :req-un [::persons]) - {:TOO "MUCH" - :persons [{:INFOR "MATION" - :height 200 - :weight 80}]})))) + (s/keys :req-un [::persons]) + {:TOO "MUCH" + :persons [{:INFOR "MATION" + :height 200 + :weight 80}]})))) (testing "failing on extra keys" (is (not (s/invalid? (st/conform ::person @@ -600,7 +600,7 @@ (testing "bmi-transformer" (is (= {:height 200, :weight 80, :bmi 20.0} (st/conform ::human person (st/type-transformer - {:decoders {::human bmi-conformer}}))))))) + {:decoders {::human bmi-conformer}}))))))) (deftest unform-test (let [unform-conform #(s/unform %1 (st/conform %1 %2 st/string-transformer))] @@ -619,31 +619,31 @@ (deftest extending-test (let [my-transformer (st/type-transformer - {:decoders - (assoc - stt/string-type-decoders - :keyword - (fn [_ value] - (-> value - str/upper-case - str/reverse - keyword)))})] + {:decoders + (assoc + stt/string-type-decoders + :keyword + (fn [_ value] + (-> value + str/upper-case + str/reverse + keyword)))})] (testing "string-transformer" (is (= :kikka (st/conform spec/keyword? "kikka" st/string-transformer)))) (testing "my-transformer" (is (= :AKKIK (st/conform spec/keyword? "kikka" my-transformer)))))) (s/def ::collect-info-spec (s/keys - :req [::age] - :req-un [::lat] - :opt [::truth] - :opt-un [::uuid])) + :req [::age] + :req-un [::lat] + :opt [::truth] + :opt-un [::uuid])) (deftest collect-info-test (testing "doesn't fail with ::s/unknown" (is (= nil (info/parse-spec - ::s/unknown)))) + ::s/unknown)))) (testing "all keys types are extracted" (is (= {:type :map @@ -658,24 +658,24 @@ ;; named spec (info/parse-spec - ::collect-info-spec) + ::collect-info-spec) ;; spec (info/parse-spec - (s/keys - :req [::age] - :req-un [::lat] - :opt [::truth] - :opt-un [::uuid])) + (s/keys + :req [::age] + :req-un [::lat] + :opt [::truth] + :opt-un [::uuid])) ;; form (info/parse-spec - (s/form - (s/keys - :req [::age] - :req-un [::lat] - :opt [::truth] - :opt-un [::uuid])))))) + (s/form + (s/keys + :req [::age] + :req-un [::lat] + :opt [::truth] + :opt-un [::uuid])))))) (testing "ands and ors are flattened" (is (= {:type :map @@ -686,8 +686,8 @@ ::parse/keys #{::age ::lat ::uuid} ::parse/keys-req #{::age ::lat ::uuid}} (info/parse-spec - (s/keys - :req [(or ::age (and ::uuid ::lat))])))))) + (s/keys + :req [(or ::age (and ::uuid ::lat))])))))) (deftest type-inference-test (testing "works for core predicates" @@ -787,9 +787,9 @@ (deftest issue-145 (is (= {:data {:id 41, :type "user", :attributes {:name "string"}}} (st/coerce - :response/user - {:data {:id "41", :type "user", :attributes {:name "string"}}} - st/string-transformer)))) + :response/user + {:data {:id "41", :type "user", :attributes {:name "string"}}} + st/string-transformer)))) (deftest issue-123 (testing "s/conform can transform composite types" @@ -867,7 +867,7 @@ (defn dbconn->url [_ {:keys [hostname port database]}] - #?(:clj (clojure.core/format "jdbc:postgres://%s:%s/%s" hostname port database) + #?(:clj (clojure.core/format "jdbc:postgres://%s:%s/%s" hostname port database) :cljs (cljs.pprint/cl-format nil "jdbc:postgres://~a:~a/~a" hostname port database))) (def jdbc-transformer @@ -877,13 +877,13 @@ :default-encoder stt/any->any})) (s/def :db/connection-string (st/spec {:spec string? - :type :dbconn})) + :type :dbconn})) (deftest issue-241 (testing "provide a spec to validate transformed values" (let [valid-input {:hostname "127.0.0.1" :port 5432 :database "postgres"}] (is (thrown? #?(:clj ClassCastException :cljs js/Error) - (st/encode ::jdbc-connection valid-input jdbc-transformer))) + (st/encode ::jdbc-connection valid-input jdbc-transformer))) (is (= (st/encode ::jdbc-connection valid-input jdbc-transformer :db/connection-string) "jdbc:postgres://127.0.0.1:5432/postgres"))))) @@ -893,8 +893,8 @@ {:core-test/overflow [:core-test/stackoverflow {:core-test/over [:core-test/flow]}]}] wrong-data [:core-test/stack - {:core-test/overflow [:core-test/stackoverflow - {:core-test/over [:flow :is :not :right]}]}]] + {:core-test/overflow [:core-test/stackoverflow + {:core-test/over [:flow :is :not :right]}]}]] (is (s/valid? ::rec-pattern correct-data)) (is (s/valid? (st/spec ::rec-pattern) correct-data)) (is (not (s/valid? ::rec-pattern wrong-data))) From 6751798b00ff83c96818e530860d970825e83fd0 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 29 Jun 2024 16:56:08 +0300 Subject: [PATCH 3/8] test 267 --- test/cljc/spec_tools/core_test.cljc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/cljc/spec_tools/core_test.cljc b/test/cljc/spec_tools/core_test.cljc index 82964ca7..ea68c97d 100644 --- a/test/cljc/spec_tools/core_test.cljc +++ b/test/cljc/spec_tools/core_test.cljc @@ -928,3 +928,18 @@ {:epoch "Epoch converted" :nano 20 :time-basis :UTC}))))) + +(s/def ::k1 (st/spec {:spec string? :reason "k1"})) +(s/def ::k2 (st/spec {:spec string? :reason "k2"})) +(s/def ::spec (s/keys :req-un [::k1 ::k2])) +(s/def ::spec-with-reason (st/spec {:spec (s/keys :req-un [::k1 ::k2]), :reason "missingKey"})) + +(deftest issue-267 + (is (= "k1" (-> (st/explain-data ::spec-with-reason {:k2 "2" :k1 1}) + ::s/problems + (first) + :reason))) + (is (= "missingKey" (-> (st/explain-data ::spec-with-reason {:k2 "2"}) + ::s/problems + (first) + :reason)))) From f1e751158292ea3acd728eaa80ecda5115dd5afc Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 29 Jun 2024 17:00:53 +0300 Subject: [PATCH 4/8] format --- src/spec_tools/data_spec.cljc | 74 +-- src/spec_tools/impl.cljc | 144 +++--- src/spec_tools/json_schema.cljc | 56 +-- src/spec_tools/openapi/core.cljc | 40 +- src/spec_tools/openapi/spec.cljc | 274 +++++----- src/spec_tools/parse.cljc | 92 ++-- src/spec_tools/spell.cljc | 20 +- src/spec_tools/spell_spec/alpha.cljc | 80 +-- src/spec_tools/spell_spec/expound.cljc | 18 +- src/spec_tools/swagger/core.cljc | 62 +-- src/spec_tools/swagger/spec.cljc | 124 ++--- src/spec_tools/transform.cljc | 40 +- src/spec_tools/visitor.cljc | 34 +- test/clj/spec_tools/perf_test.clj | 68 +-- test/cljc/spec_tools/data_spec_test.cljc | 162 +++--- test/cljc/spec_tools/impl_test.cljc | 30 +- test/cljc/spec_tools/json_schema_test.cljc | 70 +-- test/cljc/spec_tools/openapi/core_test.cljc | 466 +++++++++--------- test/cljc/spec_tools/parse_test.cljc | 24 +- .../spec_tools/spell_spec/alpha_test.cljc | 8 +- .../spec_tools/spell_spec/expound_test.cljc | 2 +- test/cljc/spec_tools/spell_test.cljc | 74 +-- test/cljc/spec_tools/swagger/core_test.cljc | 104 ++-- test/cljc/spec_tools/transform_test.cljc | 2 +- test/cljc/spec_tools/visitor_all_test.cljc | 56 +-- test/cljc/spec_tools/visitor_test.cljc | 30 +- 26 files changed, 1077 insertions(+), 1077 deletions(-) diff --git a/src/spec_tools/data_spec.cljc b/src/spec_tools/data_spec.cljc index 76cfa122..afdab8b2 100644 --- a/src/spec_tools/data_spec.cljc +++ b/src/spec_tools/data_spec.cljc @@ -91,32 +91,32 @@ (if-let [[k' v'] (and (= 1 (count data)) (let [[k v] (first data)] (and - (not - (clojure.core/or (keyword? k) - (wrapped-key? k))) - [k v])))] + (not + (clojure.core/or (keyword? k) + (wrapped-key? k))) + [k v])))] (st/create-spec {:spec (impl/map-of-spec (spec n k') (spec {:name n, :spec v'}))}) ;; keyword keys (let [m (reduce-kv - (fn [acc k v] - (let [k (if (and keys-default (keyword? k)) (keys-default k) k) - kv (unwrap-key k) - rk (keyword - (str (if (req? k) "req" "opt") - (if-not (qualified-keyword? kv) "-un"))) - [v wrap] (if (maybe? v) - [(:v v) (comp #(st/create-spec {:spec %}) impl/nilable-spec)] - [v identity]) - [k' n'] (if (qualified-keyword? kv) - [kv (if (not= kv v) kv)] - (let [k' (-nested-key n (unwrap-key kv))] - [k' k'])) - v' (if n' (wrap (spec (-> opts (assoc :name n') (assoc :spec v)))))] - (-> acc - (update rk (fnil conj []) k') - (cond-> v' (update ::defs (fnil conj []) [k' v']))))) - {} - data) + (fn [acc k v] + (let [k (if (and keys-default (keyword? k)) (keys-default k) k) + kv (unwrap-key k) + rk (keyword + (str (if (req? k) "req" "opt") + (if-not (qualified-keyword? kv) "-un"))) + [v wrap] (if (maybe? v) + [(:v v) (comp #(st/create-spec {:spec %}) impl/nilable-spec)] + [v identity]) + [k' n'] (if (qualified-keyword? kv) + [kv (if (not= kv v) kv)] + (let [k' (-nested-key n (unwrap-key kv))] + [k' k'])) + v' (if n' (wrap (spec (-> opts (assoc :name n') (assoc :spec v)))))] + (-> acc + (update rk (fnil conj []) k') + (cond-> v' (update ::defs (fnil conj []) [k' v']))))) + {} + data) defs (::defs m) data (apply hash-map (apply concat (dissoc m ::defs)))] (doseq [[k s] defs] @@ -127,26 +127,26 @@ (defn- -coll-spec [data {n :name kind :kind}] (when-not (= 1 (count data)) (throw - (ex-info - (str "data-spec collection " kind - " should be homogeneous, " (count data) - " values found") - {:name n - :kind kind - :values data}))) + (ex-info + (str "data-spec collection " kind + " should be homogeneous, " (count data) + " values found") + {:name n + :kind kind + :values data}))) (let [spec (spec n (first data))] (st/create-spec {:spec (impl/coll-of-spec spec kind)}))) (defn- -or-spec [n v] (when-not (and - (map? v) - (every? keyword? (keys v))) + (map? v) + (every? keyword? (keys v))) (throw - (ex-info - (str "data-spec or must be a map of keyword keys -> specs, " - v " found") - {:name n - :value v}))) + (ex-info + (str "data-spec or must be a map of keyword keys -> specs, " + v " found") + {:name n + :value v}))) (impl/or-spec (-> (for [[k v] v] [k (spec (-nested-key n k) v)]) (into {})))) diff --git a/src/spec_tools/impl.cljc b/src/spec_tools/impl.cljc index c3a3e2ba..b9311581 100644 --- a/src/spec_tools/impl.cljc +++ b/src/spec_tools/impl.cljc @@ -2,13 +2,13 @@ (:refer-clojure :exclude [resolve]) #?(:cljs (:require-macros [spec-tools.impl :refer [resolve]])) (:require - #?(:cljs [cljs.analyzer.api]) - [clojure.spec.alpha :as s] - [spec-tools.form :as form] - [clojure.walk :as walk]) + #?(:cljs [cljs.analyzer.api]) + [clojure.spec.alpha :as s] + [spec-tools.form :as form] + [clojure.walk :as walk]) (:import - #?@(:clj - [(clojure.lang Var)]))) + #?@(:clj + [(clojure.lang Var)]))) #?(:clj (defn in-cljs? [env] @@ -70,16 +70,16 @@ (defn with-real-keys [{:keys [req-un opt-un] :as data}] (cond-> data - req-un (update :req-un (partial mapv un-key)) - opt-un (update :opt-un (partial mapv un-key)))) + req-un (update :req-un (partial mapv un-key)) + opt-un (update :opt-un (partial mapv un-key)))) (defn parse-keys [form] (let [m (some->> form (rest) (apply hash-map))] (cond-> m - (:req m) (update :req #(->> % flatten (keep polish) (into []))) - (:req-un m) (update :req-un #(->> % flatten (keep polish) (into []))) - (:opt-un m) (update :opt-un #(->> % (keep polish) (into []))) - true (-> with-key->spec with-real-keys)))) + (:req m) (update :req #(->> % flatten (keep polish) (into []))) + (:req-un m) (update :req-un #(->> % flatten (keep polish) (into []))) + (:opt-un m) (update :opt-un #(->> % (keep polish) (into []))) + true (-> with-key->spec with-real-keys)))) (defn extract-keys [form] (let [{:keys [req opt req-un opt-un]} (some->> form (rest) (apply hash-map))] @@ -91,18 +91,18 @@ res (if cljs? (partial cljs-resolve env) clojure.core/resolve)] (->> pred (walk/postwalk - (fn [x] - (if (symbol? x) - (let [y (res x) - -var-get (fn [v] (if cljs? @v (var-get v))) - sym-or-x (fn [v] (or (->sym v) x))] - (cond - (var? y) (if (s/get-spec (-var-get y)) - (-var-get y) - (sym-or-x y)) - (some? y) (sym-or-x y) - :else x)) - x))) + (fn [x] + (if (symbol? x) + (let [y (res x) + -var-get (fn [v] (if cljs? @v (var-get v))) + sym-or-x (fn [v] (or (->sym v) x))] + (cond + (var? y) (if (s/get-spec (-var-get y)) + (-var-get y) + (sym-or-x y)) + (some? y) (sym-or-x y) + :else x)) + x))) (unfn cljs?))))) (defn extract-pred-and-info [x] @@ -138,12 +138,12 @@ (defn nilable-spec? [spec] (let [form (and spec (s/form spec))] (boolean - (if (seq? form) - (some-> form - seq - first - #{'clojure.spec.alpha/nilable - 'cljs.spec.alpha/nilable}))))) + (if (seq? form) + (some-> form + seq + first + #{'clojure.spec.alpha/nilable + 'cljs.spec.alpha/nilable}))))) (defn unwrap "Unwrap [x] to x. Asserts that coll has exactly one element." @@ -164,11 +164,11 @@ (defn unlift-keys [data ns-name] (reduce - (fn [acc [k v]] - (if (= ns-name (namespace k)) - (assoc acc (keyword (name k)) v) - acc)) - {} data)) + (fn [acc [k v]] + (if (= ns-name (namespace k)) + (assoc acc (keyword (name k)) v) + acc)) + {} data)) ;; ;; FIXME: using ^:skip-wiki functions from clojure.spec. might break. @@ -185,29 +185,29 @@ (let [form (form/resolve-form pred) cpred (if (set? type) set? vector?)] (clojure.spec.alpha/every-impl - form - pred - {:into type - ::s/conform-all true - ::s/describe `(s/coll-of ~form :into ~type), - ::s/cpred cpred, - ::s/kind-form (quote nil)} - nil))) + form + pred + {:into type + ::s/conform-all true + ::s/describe `(s/coll-of ~form :into ~type), + ::s/cpred cpred, + ::s/kind-form (quote nil)} + nil))) (defn map-of-spec [kpred vpred] (let [forms (map form/resolve-form [kpred vpred]) tuple (s/tuple-impl forms [kpred vpred])] (clojure.spec.alpha/every-impl - `(s/tuple ~@forms) - tuple - {:into {} - :conform-keys true - ::s/kfn (fn [_ v] (nth v 0)) - ::s/conform-all true - ::s/describe `(s/map-of ~@forms :conform-keys true), - ::s/cpred map?, - ::s/kind-form (quote nil)} - nil))) + `(s/tuple ~@forms) + tuple + {:into {} + :conform-keys true + ::s/kfn (fn [_ v] (nth v 0)) + ::s/conform-all true + ::s/describe `(s/map-of ~@forms :conform-keys true), + ::s/cpred map?, + ::s/kind-form (quote nil)} + nil))) (defn keys-spec [{:keys [req opt req-un opt-un]}] (let [req-specs (flatten (map polish (concat req req-un))) @@ -215,30 +215,30 @@ req-keys (flatten (concat (map polish req) (map polish-un req-un))) opt-keys (flatten (concat (map polish opt) (map polish-un opt-un))) pred-exprs (concat - [#(map? %)] - (map (fn [x] #(contains? % x)) req-keys)) + [#(map? %)] + (map (fn [x] #(contains? % x)) req-keys)) pred-forms (concat - [`(fn [~'%] (map? ~'%))] - (map (fn [k] `(fn [~'%] (contains? ~'% ~k))) req-keys)) + [`(fn [~'%] (map? ~'%))] + (map (fn [k] `(fn [~'%] (contains? ~'% ~k))) req-keys)) keys-pred (fn [x] (reduce - (fn [_ p] - (clojure.core/or (p x) (reduced false))) - true - pred-exprs))] + (fn [_ p] + (clojure.core/or (p x) (reduced false))) + true + pred-exprs))] (s/map-spec-impl - {:req-un req-un - :opt-un opt-un - :pred-exprs pred-exprs - :keys-pred keys-pred - :opt-keys opt-keys - :req-specs req-specs - :req req - :req-keys req-keys - :opt-specs opt-specs - :pred-forms pred-forms - :opt opt}))) + {:req-un req-un + :opt-un opt-un + :pred-exprs pred-exprs + :keys-pred keys-pred + :opt-keys opt-keys + :req-specs req-specs + :req req + :req-keys req-keys + :opt-specs opt-specs + :pred-forms pred-forms + :opt opt}))) (defn nilable-spec [pred] (let [form (form/resolve-form pred)] diff --git a/src/spec_tools/json_schema.cljc b/src/spec_tools/json_schema.cljc index 02f00683..ef094bec 100644 --- a/src/spec_tools/json_schema.cljc +++ b/src/spec_tools/json_schema.cljc @@ -192,13 +192,13 @@ required-un (map name req-un) all-required (not-empty (concat required required-un))] (maybe-with-title - (merge - {:type "object" - :properties (zipmap (concat names names-un) children)} - (when all-required - {:required (vec all-required)})) - spec - options))) + (merge + {:type "object" + :properties (zipmap (concat names names-un) children)} + (when all-required + {:required (vec all-required)})) + spec + options))) (defmethod accept-spec 'clojure.spec.alpha/or [_ _ children _] {:anyOf children}) @@ -208,19 +208,19 @@ (defn- accept-merge [children spec options] (maybe-with-title - {:type "object" - :properties (->> (concat children - (mapcat :anyOf children) - (mapcat :allOf children)) - (map :properties) - (reduce merge {})) - :required (->> (concat children - (mapcat :allOf children)) - (map :required) - (reduce into (sorted-set)) - (into []))} - spec - options)) + {:type "object" + :properties (->> (concat children + (mapcat :anyOf children) + (mapcat :allOf children)) + (map :properties) + (reduce merge {})) + :required (->> (concat children + (mapcat :allOf children)) + (map :required) + (reduce into (sorted-set)) + (into []))} + spec + options)) (defmethod accept-spec 'clojure.spec.alpha/merge [_ spec children options] (accept-merge children spec options)) @@ -262,16 +262,16 @@ (defmethod accept-spec 'clojure.spec.alpha/alt [_ spec children options] (maybe-with-title - {:anyOf children} - spec - options)) + {:anyOf children} + spec + options)) (defmethod accept-spec 'clojure.spec.alpha/cat [_ spec children options] (maybe-with-title - {:type "array" - :items {:anyOf children}} - spec - options)) + {:type "array" + :items {:anyOf children}} + spec + options)) ; & @@ -296,7 +296,7 @@ json-schema-meta (impl/unlift-keys data "json-schema") extra-info (-> (select-keys data [:description]) (cond-> (and name (not synthetic?)) - (assoc :title (impl/qualified-name name))))] + (assoc :title (impl/qualified-name name))))] (or (:json-schema data) (merge (impl/unwrap children) extra-info json-schema-meta)))) diff --git a/src/spec_tools/openapi/core.cljc b/src/spec_tools/openapi/core.cljc index d6cb9c43..4a73e47b 100644 --- a/src/spec_tools/openapi/core.cljc +++ b/src/spec_tools/openapi/core.cljc @@ -15,22 +15,22 @@ {:type "number" :format "double"}) (defmethod accept-spec 'clojure.spec.alpha/tuple [_ _ children _] - {:type "array" + {:type "array" :items {:anyOf children}}) (defmethod accept-spec 'clojure.core/sequential? [_ _ _ _] - {:type "array" + {:type "array" :items {}}) (defmethod accept-spec 'clojure.spec.alpha/alt [_ _ children _] - {:type "array" + {:type "array" :items {:oneOf children}}) (defmethod accept-spec ::visitor/set [_ _ children _] {:enum children :type "string"}) (defmethod accept-spec ::visitor/spec [dispatch spec children options] - (let [[_ data] (impl/extract-form spec) + (let [[_ data] (impl/extract-form spec) openapi-meta (impl/unlift-keys data "openapi")] (or (:openapi data) (merge (json-schema/accept-spec dispatch spec children options) @@ -69,32 +69,32 @@ new-spec (if nilable? (extract-nilable spec) spec)] - {:name (or (:title new-spec) (:type new-spec)) - :in in + {:name (or (:title new-spec) (:type new-spec)) + :in in :description (or (:description new-spec) "") - :required (case in - :path true - (not nilable?)) - :schema new-spec})) + :required (case in + :path true + (not nilable?)) + :schema new-spec})) (defn- extract-object-param [in {:keys [properties required]}] (mapv (fn [[k {:keys [description] :as schema}]] - {:name k - :in (name in) + {:name k + :in (name in) :description (or description "") - :required (case in - :path true - (contains? (set required) k)) - :schema schema}) + :required (case in + :path true + (contains? (set required) k)) + :schema schema}) properties)) (defn extract-parameter [in spec] (let [parameter-spec (transform spec) - object? (and (contains? parameter-spec :properties) - (= "object" (:type parameter-spec)))] + object? (and (contains? parameter-spec :properties) + (= "object" (:type parameter-spec)))] (if object? (extract-object-param in parameter-spec) (-> (extract-single-param in parameter-spec) vector)))) @@ -121,8 +121,8 @@ {content-type {:schema (transform schema)}}))}) (defmethod expand ::parameters [_ v acc _] - (let [old (or (:parameters acc) []) - new (mapcat (fn [[in spec]] (extract-parameter in spec)) v) + (let [old (or (:parameters acc) []) + new (mapcat (fn [[in spec]] (extract-parameter in spec)) v) merged (->> (into old new) (reverse) (reduce diff --git a/src/spec_tools/openapi/spec.cljc b/src/spec_tools/openapi/spec.cljc index bad28e0f..8ea25619 100644 --- a/src/spec_tools/openapi/spec.cljc +++ b/src/spec_tools/openapi/spec.cljc @@ -5,45 +5,45 @@ (s/def ::contact (ds/spec ::contact - {(ds/opt :name) string? - (ds/opt :url) string? + {(ds/opt :name) string? + (ds/opt :url) string? (ds/opt :email) string?})) (s/def ::license (ds/spec ::license {(ds/req :name) string? - (ds/opt :url) string?})) + (ds/opt :url) string?})) (s/def ::info (ds/spec ::info - {(ds/req :title) string? - (ds/opt :description) string? + {(ds/req :title) string? + (ds/opt :description) string? (ds/opt :termsOfService) string? - (ds/opt :contact) ::contact - (ds/opt :license) ::license - (ds/req :version) string?})) + (ds/opt :contact) ::contact + (ds/opt :license) ::license + (ds/req :version) string?})) (s/def ::server-variable (ds/spec ::server-variable - {(ds/opt :enum) [string?] - (ds/req :default) string? + {(ds/opt :enum) [string?] + (ds/req :default) string? (ds/opt :description) string?})) (s/def ::server (ds/spec ::server - {(ds/req :url) string? + {(ds/req :url) string? (ds/opt :description) string? - (ds/opt :variables) {string? ::server-variable}})) + (ds/opt :variables) {string? ::server-variable}})) (s/def ::external-documentation (ds/spec ::external-documentation {(ds/opt :description) string? - (ds/req :url) string?})) + (ds/req :url) string?})) ;; FIXME: Not sure about this (s/def ::schema (s/map-of @@ -53,110 +53,110 @@ (s/def ::example (ds/spec ::example - {(ds/opt :summary) string? - (ds/opt :description) string? - (ds/opt :value) any? + {(ds/opt :summary) string? + (ds/opt :description) string? + (ds/opt :value) any? (ds/opt :externalValue) string?})) (s/def ::header (ds/spec ::header - {(ds/opt :description) string? - (ds/req :required) (s/and boolean? true?) - (ds/opt :deprecated) boolean? + {(ds/opt :description) string? + (ds/req :required) (s/and boolean? true?) + (ds/opt :deprecated) boolean? (ds/opt :allowEmptyValue) boolean? - (ds/opt :style) (s/spec #{"simple"}) - (ds/opt :explode) (s/and boolean? false?) - (ds/opt :schema) ::schema - (ds/opt :example) any? - (ds/opt :examples) {string? ::example}})) + (ds/opt :style) (s/spec #{"simple"}) + (ds/opt :explode) (s/and boolean? false?) + (ds/opt :schema) ::schema + (ds/opt :example) any? + (ds/opt :examples) {string? ::example}})) (s/def ::encoding (ds/spec ::encoding - {(ds/opt :contentType) string? - (ds/opt :headers) {string? ::header} - (ds/opt :style) (s/spec #{"form" - "spaceDelimited" - "pipeDelimited" - "deepObject"}) - (ds/opt :explode) boolean? + {(ds/opt :contentType) string? + (ds/opt :headers) {string? ::header} + (ds/opt :style) (s/spec #{"form" + "spaceDelimited" + "pipeDelimited" + "deepObject"}) + (ds/opt :explode) boolean? (ds/opt :allowReserved) boolean?})) (s/def ::media-object (ds/spec ::media-object - {(ds/opt :schema) ::schema - (ds/opt :example) any? + {(ds/opt :schema) ::schema + (ds/opt :example) any? (ds/opt :examples) {string? ::example} (ds/opt :encoding) {string? ::encoding}})) (s/def ::parameter-path (ds/spec ::parameter - {(ds/req :name) string? - (ds/req :in) (s/spec #{"path"}) - (ds/opt :description) string? - (ds/req :required) (s/and boolean? true?) - (ds/opt :deprecated) boolean? + {(ds/req :name) string? + (ds/req :in) (s/spec #{"path"}) + (ds/opt :description) string? + (ds/req :required) (s/and boolean? true?) + (ds/opt :deprecated) boolean? (ds/opt :allowEmptyValue) boolean? - (ds/opt :style) (s/spec #{"matrix" - "label" - "form" - "simple" - "spaceDelimited" - "pipeDelimited" - "deepObject"}) - (ds/opt :explode) boolean? - (ds/opt :allowReserved) boolean? - (ds/opt :schema) ::schema - (ds/opt :example) any? - (ds/opt :examples) {string? ::example} - (ds/opt :content) {string? ::media-object}})) + (ds/opt :style) (s/spec #{"matrix" + "label" + "form" + "simple" + "spaceDelimited" + "pipeDelimited" + "deepObject"}) + (ds/opt :explode) boolean? + (ds/opt :allowReserved) boolean? + (ds/opt :schema) ::schema + (ds/opt :example) any? + (ds/opt :examples) {string? ::example} + (ds/opt :content) {string? ::media-object}})) (s/def ::parameter-other (ds/spec ::parameter-other - {(ds/req :name) string? - (ds/req :in) (s/spec #{"query" "header" "cookie"}) - (ds/opt :description) string? - (ds/opt :required) boolean? - (ds/opt :deprecated) boolean? + {(ds/req :name) string? + (ds/req :in) (s/spec #{"query" "header" "cookie"}) + (ds/opt :description) string? + (ds/opt :required) boolean? + (ds/opt :deprecated) boolean? (ds/opt :allowEmptyValue) boolean? - (ds/opt :style) (s/spec #{"matrix" - "label" - "form" - "simple" - "spaceDelimited" - "pipeDelimited" - "deepObject"}) - (ds/opt :explode) boolean? - (ds/opt :allowReserved) boolean? - (ds/opt :schema) ::schema - (ds/opt :example) any? - (ds/opt :examples) {string? ::example} - (ds/opt :content) {string? ::media-object}})) + (ds/opt :style) (s/spec #{"matrix" + "label" + "form" + "simple" + "spaceDelimited" + "pipeDelimited" + "deepObject"}) + (ds/opt :explode) boolean? + (ds/opt :allowReserved) boolean? + (ds/opt :schema) ::schema + (ds/opt :example) any? + (ds/opt :examples) {string? ::example} + (ds/opt :content) {string? ::media-object}})) (s/def ::parameter - (s/or :path-parameter ::parameter-path + (s/or :path-parameter ::parameter-path :other-parameter ::parameter-other)) (s/def ::request-body (ds/spec ::request-body {(ds/opt :description) string? - (ds/req :content) {string? ::media-object} - (ds/opt :required) boolean?})) + (ds/req :content) {string? ::media-object} + (ds/opt :required) boolean?})) (s/def ::link (ds/spec ::link {(ds/opt :operationRef) string? - (ds/opt :operationId) string? - (ds/opt :parameters) {string? any?} - (ds/opt :requestBody) any? - (ds/opt :description) string? - (ds/opt :server) ::server})) + (ds/opt :operationId) string? + (ds/opt :parameters) {string? any?} + (ds/opt :requestBody) any? + (ds/opt :description) string? + (ds/opt :server) ::server})) (s/def ::response-code (s/or :code (s/int-in 100 600) :default #{:default})) @@ -165,40 +165,40 @@ (ds/spec ::response {(ds/req :description) string? - (ds/opt :headers) {string? ::header} - (ds/opt :content) {string? ::media-object} - (ds/opt :links) {string? ::link}})) + (ds/opt :headers) {string? ::header} + (ds/opt :content) {string? ::media-object} + (ds/opt :links) {string? ::link}})) (s/def ::operation (ds/spec ::operation - {(ds/opt :tags) [string?] - (ds/opt :summary) string? - (ds/opt :description) string? + {(ds/opt :tags) [string?] + (ds/opt :summary) string? + (ds/opt :description) string? (ds/opt :externalDocs) ::external-documentation - (ds/opt :operationId) string? - (ds/opt :parameters) [::parameter] - (ds/opt :requestBody) ::request-body - (ds/req :responses) ::response + (ds/opt :operationId) string? + (ds/opt :parameters) [::parameter] + (ds/opt :requestBody) ::request-body + (ds/req :responses) ::response ;; (ds/opt :callbacks) {string? ::callback} - (ds/opt :deprecated) boolean? - (ds/opt :security) {string? [string?]} - (ds/opt :servers) [::server]})) + (ds/opt :deprecated) boolean? + (ds/opt :security) {string? [string?]} + (ds/opt :servers) [::server]})) (s/def ::path (ds/spec ::path - {(ds/opt :summary) string? + {(ds/opt :summary) string? (ds/opt :description) string? - (ds/opt :get) ::operation - (ds/opt :put) ::operation - (ds/opt :post) ::operation - (ds/opt :delete) ::operation - (ds/opt :options) ::operation - (ds/opt :head) ::operation - (ds/opt :patch) ::operation - (ds/opt :servers) [::server] - (ds/opt :parameters) any?})) + (ds/opt :get) ::operation + (ds/opt :put) ::operation + (ds/opt :post) ::operation + (ds/opt :delete) ::operation + (ds/opt :options) ::operation + (ds/opt :head) ::operation + (ds/opt :patch) ::operation + (ds/opt :servers) [::server] + (ds/opt :parameters) any?})) (s/def ::callback (ds/spec @@ -208,72 +208,72 @@ (s/def ::tag (ds/spec ::tag - {(ds/req :name) string? - (ds/opt :description) string? + {(ds/req :name) string? + (ds/opt :description) string? (ds/opt :externalDocs) ::external-documentation})) (s/def ::security-scheme-api-key (ds/spec ::security-scheme-api-key - {(ds/req :type) (s/spec #{"apiKey"}) + {(ds/req :type) (s/spec #{"apiKey"}) (ds/opt :description) string? - (ds/req :name) string? - (ds/req :in) (s/spec #{"query" "header" "cookie"})})) + (ds/req :name) string? + (ds/req :in) (s/spec #{"query" "header" "cookie"})})) (s/def ::security-scheme-http (ds/spec ::security-scheme-http - {(ds/req :type) (s/spec #{"http"}) - (ds/opt :description) string? - (ds/req :scheme) string? + {(ds/req :type) (s/spec #{"http"}) + (ds/opt :description) string? + (ds/req :scheme) string? (ds/req :bearerFormat) string?})) (s/def ::security-scheme-oauth2 (ds/spec ::security-scheme-oauth2 - {(ds/req :type) (s/spec #{"oauth2"}) + {(ds/req :type) (s/spec #{"oauth2"}) (ds/opt :description) string? (ds/req :flows) - {(ds/opt :implicit) {(ds/req :authorizationUrl) string? - (ds/opt :refreshUrl) string? - (ds/req :scopes) {string? string?}} - (ds/opt :password) {(ds/req :tokenUrl) string? - (ds/opt :refreshUrl) string? - (ds/req :scopes) {string? string?}} - (ds/opt :clientCredentials) {(ds/req :tokenUrl) string? + {(ds/opt :implicit) {(ds/req :authorizationUrl) string? + (ds/opt :refreshUrl) string? + (ds/req :scopes) {string? string?}} + (ds/opt :password) {(ds/req :tokenUrl) string? + (ds/opt :refreshUrl) string? + (ds/req :scopes) {string? string?}} + (ds/opt :clientCredentials) {(ds/req :tokenUrl) string? (ds/opt :refreshUrl) string? - (ds/req :scopes) {string? string?}} + (ds/req :scopes) {string? string?}} (ds/opt :authorizationCode) {(ds/req :authorizationUrl) string? - (ds/req :tokenUrl) string? - (ds/opt :refreshUrl) string? - (ds/req :scopes) {string? string?}}}})) + (ds/req :tokenUrl) string? + (ds/opt :refreshUrl) string? + (ds/req :scopes) {string? string?}}}})) (s/def ::security-scheme (s/or :api-key ::security-scheme-api-key - :http ::security-scheme-http - :oauth2 ::security-scheme-oauth2)) + :http ::security-scheme-http + :oauth2 ::security-scheme-oauth2)) (s/def ::components (ds/spec ::components - {(ds/opt :schemas) {string? ::schema} - (ds/opt :responses) {string? ::response} - (ds/opt :parameters) {string? ::parameter} - (ds/opt :examples) {string? ::example} - (ds/opt :requestBodies) {string? ::request-body} - (ds/opt :headers) {string? ::header} + {(ds/opt :schemas) {string? ::schema} + (ds/opt :responses) {string? ::response} + (ds/opt :parameters) {string? ::parameter} + (ds/opt :examples) {string? ::example} + (ds/opt :requestBodies) {string? ::request-body} + (ds/opt :headers) {string? ::header} (ds/opt :securitySchemes) {string? ::security-scheme} - (ds/opt :links) {string? ::link} - (ds/opt :callbacks) {string? ::callback}})) + (ds/opt :links) {string? ::link} + (ds/opt :callbacks) {string? ::callback}})) (s/def ::openapi (ds/spec ::openapi - {(ds/req :openapi) (s/and string? #(re-matches #"^3\.\d\.\d$" %)) - (ds/req :info) ::info - (ds/opt :servers) [::server] - (ds/req :paths) {string? ::path} - (ds/opt :components) ::components - (ds/opt :security) {string? [string?]} - (ds/opt :tags) [::tag] + {(ds/req :openapi) (s/and string? #(re-matches #"^3\.\d\.\d$" %)) + (ds/req :info) ::info + (ds/opt :servers) [::server] + (ds/req :paths) {string? ::path} + (ds/opt :components) ::components + (ds/opt :security) {string? [string?]} + (ds/opt :tags) [::tag] (ds/opt :externalDocs) ::external-documentation})) diff --git a/src/spec_tools/parse.cljc b/src/spec_tools/parse.cljc index dfb7d866..5d59909f 100644 --- a/src/spec_tools/parse.cljc +++ b/src/spec_tools/parse.cljc @@ -33,7 +33,7 @@ (if (get (::visited options) x) {:spec x} (parse-spec (s/form (s/get-spec x)) (update options ::visited (fnil conj #{}) x))) - + ;; symbol (symbol? x) (parse-form (impl/normalize-symbol x) nil options) @@ -97,50 +97,50 @@ (->> (filter symbol?)) set)) -(defmethod parse-form 'clojure.core/any? [_ _ _] {:spec any?}) -(defmethod parse-form 'clojure.core/some? [_ _ _] {:spec some?}) -(defmethod parse-form 'clojure.core/number? [_ _ _] {:spec number?, :type :double}) -(defmethod parse-form 'clojure.core/integer? [_ _ _] {:spec integer?, :type :long}) -(defmethod parse-form 'clojure.core/int? [_ _ _] {:spec int?, :type :long}) -(defmethod parse-form 'clojure.core/pos-int? [_ _ _] {:spec pos-int?, :type :long}) -(defmethod parse-form 'clojure.core/neg-int? [_ _ _] {:spec neg-int?, :type :long}) -(defmethod parse-form 'clojure.core/nat-int? [_ _ _] {:spec nat-int?, :type :long}) -(defmethod parse-form 'clojure.core/float? [_ _ _] {:spec float?, :type :double}) -(defmethod parse-form 'clojure.core/double? [_ _ _] {:spec double?, :type :double}) -(defmethod parse-form 'clojure.core/boolean? [_ _ _] {:spec boolean?, :type :boolean}) -(defmethod parse-form 'clojure.core/string? [_ _ _] {:spec string?, :type :string}) -(defmethod parse-form 'clojure.core/ident? [_ _ _] {:spec ident? :type :keyword}) -(defmethod parse-form 'clojure.core/simple-ident? [_ _ _] {:spec simple-ident?, :type :keyword}) -(defmethod parse-form 'clojure.core/qualified-ident? [_ _ _] {:spec qualified-ident?, :type :keyword}) -(defmethod parse-form 'clojure.core/keyword? [_ _ _] {:spec keyword?, :type :keyword}) -(defmethod parse-form 'clojure.core/simple-keyword? [_ _ _] {:spec simple-keyword?, :type :keyword}) +(defmethod parse-form 'clojure.core/any? [_ _ _] {:spec any?}) +(defmethod parse-form 'clojure.core/some? [_ _ _] {:spec some?}) +(defmethod parse-form 'clojure.core/number? [_ _ _] {:spec number?, :type :double}) +(defmethod parse-form 'clojure.core/integer? [_ _ _] {:spec integer?, :type :long}) +(defmethod parse-form 'clojure.core/int? [_ _ _] {:spec int?, :type :long}) +(defmethod parse-form 'clojure.core/pos-int? [_ _ _] {:spec pos-int?, :type :long}) +(defmethod parse-form 'clojure.core/neg-int? [_ _ _] {:spec neg-int?, :type :long}) +(defmethod parse-form 'clojure.core/nat-int? [_ _ _] {:spec nat-int?, :type :long}) +(defmethod parse-form 'clojure.core/float? [_ _ _] {:spec float?, :type :double}) +(defmethod parse-form 'clojure.core/double? [_ _ _] {:spec double?, :type :double}) +(defmethod parse-form 'clojure.core/boolean? [_ _ _] {:spec boolean?, :type :boolean}) +(defmethod parse-form 'clojure.core/string? [_ _ _] {:spec string?, :type :string}) +(defmethod parse-form 'clojure.core/ident? [_ _ _] {:spec ident? :type :keyword}) +(defmethod parse-form 'clojure.core/simple-ident? [_ _ _] {:spec simple-ident?, :type :keyword}) +(defmethod parse-form 'clojure.core/qualified-ident? [_ _ _] {:spec qualified-ident?, :type :keyword}) +(defmethod parse-form 'clojure.core/keyword? [_ _ _] {:spec keyword?, :type :keyword}) +(defmethod parse-form 'clojure.core/simple-keyword? [_ _ _] {:spec simple-keyword?, :type :keyword}) (defmethod parse-form 'clojure.core/qualified-keyword? [_ _ _] {:spec qualified-keyword? :type :keyword}) -(defmethod parse-form 'clojure.core/symbol? [_ _ _] {:spec symbol?, :type :symbol}) -(defmethod parse-form 'clojure.core/simple-symbol? [_ _ _] {:spec simple-symbol?, :type :symbol}) -(defmethod parse-form 'clojure.core/qualified-symbol? [_ _ _] {:spec qualified-symbol?, :type :symbol}) -(defmethod parse-form 'clojure.core/uuid? [_ _ _] {:spec uuid?, :type :uuid}) -#?(:clj (defmethod parse-form 'clojure.core/uri? [_ _ _] {:spec uri?, :type :uri})) -#?(:clj (defmethod parse-form 'clojure.core/decimal? [_ _ _] {:spec decimal?, :type :bigdec})) -(defmethod parse-form 'clojure.core/inst? [_ _ _] {:spec inst?, :type :date}) -(defmethod parse-form 'clojure.core/seqable? [_ _ _] {:spec seqable?}) -(defmethod parse-form 'clojure.core/indexed? [_ _ _] {:spec indexed?}) -(defmethod parse-form 'clojure.core/map? [_ _ _] {:spec map?}) -(defmethod parse-form 'clojure.core/vector? [_ _ _] {:spec vector?}) -(defmethod parse-form 'clojure.core/list? [_ _ _] {:spec list?}) -(defmethod parse-form 'clojure.core/seq? [_ _ _] {:spec seq?}) -(defmethod parse-form 'clojure.core/char? [_ _ _] {:spec char?}) -(defmethod parse-form 'clojure.core/set? [_ _ _] {:spec set?}) -(defmethod parse-form 'clojure.core/nil? [_ _ _] {:spec nil?}) -(defmethod parse-form 'clojure.core/false? [_ _ _] {:spec false?, :type :boolean}) -(defmethod parse-form 'clojure.core/true? [_ _ _] {:spec true?, :type :boolean}) -(defmethod parse-form 'clojure.core/zero? [_ _ _] {:spec zero?, :type :long}) -#?(:clj (defmethod parse-form 'clojure.core/rational? [_ _ _] {:spec rational?, :type :long})) -(defmethod parse-form 'clojure.core/coll? [_ _ _] {:spec coll?}) -(defmethod parse-form 'clojure.core/empty? [_ _ _] {:spec empty?}) -(defmethod parse-form 'clojure.core/associative? [_ _ _] {:spec associative?, :type nil}) -(defmethod parse-form 'clojure.core/sequential? [_ _ _] {:spec sequential?}) -#?(:clj (defmethod parse-form 'clojure.core/ratio? [_ _ _] {:spec ratio?, :type :ratio})) -#?(:clj (defmethod parse-form 'clojure.core/bytes? [_ _ _] {:spec bytes?})) +(defmethod parse-form 'clojure.core/symbol? [_ _ _] {:spec symbol?, :type :symbol}) +(defmethod parse-form 'clojure.core/simple-symbol? [_ _ _] {:spec simple-symbol?, :type :symbol}) +(defmethod parse-form 'clojure.core/qualified-symbol? [_ _ _] {:spec qualified-symbol?, :type :symbol}) +(defmethod parse-form 'clojure.core/uuid? [_ _ _] {:spec uuid?, :type :uuid}) +#?(:clj (defmethod parse-form 'clojure.core/uri? [_ _ _] {:spec uri?, :type :uri})) +#?(:clj (defmethod parse-form 'clojure.core/decimal? [_ _ _] {:spec decimal?, :type :bigdec})) +(defmethod parse-form 'clojure.core/inst? [_ _ _] {:spec inst?, :type :date}) +(defmethod parse-form 'clojure.core/seqable? [_ _ _] {:spec seqable?}) +(defmethod parse-form 'clojure.core/indexed? [_ _ _] {:spec indexed?}) +(defmethod parse-form 'clojure.core/map? [_ _ _] {:spec map?}) +(defmethod parse-form 'clojure.core/vector? [_ _ _] {:spec vector?}) +(defmethod parse-form 'clojure.core/list? [_ _ _] {:spec list?}) +(defmethod parse-form 'clojure.core/seq? [_ _ _] {:spec seq?}) +(defmethod parse-form 'clojure.core/char? [_ _ _] {:spec char?}) +(defmethod parse-form 'clojure.core/set? [_ _ _] {:spec set?}) +(defmethod parse-form 'clojure.core/nil? [_ _ _] {:spec nil?}) +(defmethod parse-form 'clojure.core/false? [_ _ _] {:spec false?, :type :boolean}) +(defmethod parse-form 'clojure.core/true? [_ _ _] {:spec true?, :type :boolean}) +(defmethod parse-form 'clojure.core/zero? [_ _ _] {:spec zero?, :type :long}) +#?(:clj (defmethod parse-form 'clojure.core/rational? [_ _ _] {:spec rational?, :type :long})) +(defmethod parse-form 'clojure.core/coll? [_ _ _] {:spec coll?}) +(defmethod parse-form 'clojure.core/empty? [_ _ _] {:spec empty?}) +(defmethod parse-form 'clojure.core/associative? [_ _ _] {:spec associative?, :type nil}) +(defmethod parse-form 'clojure.core/sequential? [_ _ _] {:spec sequential?}) +#?(:clj (defmethod parse-form 'clojure.core/ratio? [_ _ _] {:spec ratio?, :type :ratio})) +#?(:clj (defmethod parse-form 'clojure.core/bytes? [_ _ _] {:spec bytes?})) (defmethod parse-form :clojure.spec.alpha/unknown [_ _ _]) @@ -149,8 +149,8 @@ (cond-> {:type :map ::key->spec key->spec ::keys (set (concat req opt req-un opt-un))} - (or req req-un) (assoc ::keys-req (set (concat req req-un))) - (or opt opt-un) (assoc ::keys-opt (set (concat opt opt-un)))))) + (or req req-un) (assoc ::keys-req (set (concat req req-un))) + (or opt opt-un) (assoc ::keys-opt (set (concat opt opt-un)))))) #?(:clj (defn get-multi-spec-sub-specs diff --git a/src/spec_tools/spell.cljc b/src/spec_tools/spell.cljc index 4c5fe0c2..7ac08169 100644 --- a/src/spec_tools/spell.cljc +++ b/src/spec_tools/spell.cljc @@ -37,10 +37,10 @@ :misspelled {:spec (ssa/not-misspelled keys) :form `(ssa/not-misspelled ~keys)})] (pre-check - (ssa/warning-spec - (impl/map-of-spec - (ssa/map-explain ssa/enhance-problem (st/create-spec data)) any?)) - spec)) + (ssa/warning-spec + (impl/map-of-spec + (ssa/map-explain ssa/enhance-problem (st/create-spec data)) any?)) + spec)) (throw (ex-info (str "Can't read keys from spec: " spec) {:spec spec, :mode mode})))) ;; @@ -57,12 +57,12 @@ (if-let [explain-data (s/explain-data spec data)] (binding [ansi/*enable-color* true] (#'expound/printer-str - {:print-specs? false - :show-valid-values? false} - (if explain-data - (assoc explain-data - ::s/value data) - nil))))) + {:print-specs? false + :show-valid-values? false} + (if explain-data + (assoc explain-data + ::s/value data) + nil))))) (defn explain [spec data] (some-> (explain-str spec data) (println))) diff --git a/src/spec_tools/spell_spec/alpha.cljc b/src/spec_tools/spell_spec/alpha.cljc index 07bd7f3b..2f9b5449 100644 --- a/src/spec_tools/spell_spec/alpha.cljc +++ b/src/spec_tools/spell_spec/alpha.cljc @@ -1,10 +1,10 @@ (ns spec-tools.spell-spec.alpha (:refer-clojure :exclude [keys]) (:require - [#?(:clj clojure.spec.alpha - :cljs cljs.spec.alpha) - :as s] - #?(:cljs [goog.string])) + [#?(:clj clojure.spec.alpha + :cljs cljs.spec.alpha) + :as s] + #?(:cljs [goog.string])) #?(:cljs (:require-macros [spec-tools.spell-spec.alpha :refer [keys warn-keys strict-keys warn-strict-keys]]))) (def ^:dynamic *value* {}) @@ -36,21 +36,21 @@ (defn- next-row [previous current other-seq] (reduce - (fn [row [diagonal above other]] - (let [update-val (if (= other current) - diagonal - (inc (min diagonal above (peek row))))] - (conj row update-val))) - [(inc (first previous))] - (map vector previous (next previous) other-seq))) + (fn [row [diagonal above other]] + (let [update-val (if (= other current) + diagonal + (inc (min diagonal above (peek row))))] + (conj row update-val))) + [(inc (first previous))] + (map vector previous (next previous) other-seq))) (defn- levenshtein "Compute the levenshtein distance between two [sequences]." [sequence1 sequence2] (peek - (reduce (fn [previous current] (next-row previous current sequence2)) - (map #(identity %2) (cons nil sequence2) (range)) - sequence1))) + (reduce (fn [previous current] (next-row previous current sequence2)) + (map #(identity %2) (cons nil sequence2) (range)) + sequence1))) (defn- similar-key* [thresh ky ky2] (let [dist (levenshtein (str ky) (str ky2))] @@ -62,7 +62,7 @@ min-len (apply min (map (comp count #(if (starts-with? % ":") (subs % 1) %) str) [ky ky2]))] (similar-key* (#?(:clj *length->threshold* :cljs length->threshold) - min-len) ky ky2))) + min-len) ky ky2))) ;; a tricky part is is that a keyword is not considered misspelled ;; if its substitute is already present in the original map @@ -73,7 +73,7 @@ (filter #(similar-key % key)) (remove (set (#?(:clj clojure.core/keys :cljs cljs.core/keys) - *value*))) + *value*))) not-empty)))) (defn not-misspelled [known-keys] (complement (likely-misspelled known-keys))) @@ -119,11 +119,11 @@ (defn- problem-warnings [value problems] (#?@(:clj [binding [*out* *err*]] :cljs [do]) - (doseq [prob problems] - (*warning-handler* - (assoc prob - ::value value - ::warning-message (warning-message* prob value)))))) + (doseq [prob problems] + (*warning-handler* + (assoc prob + ::value value + ::warning-message (warning-message* prob value)))))) (defn warning-spec "Wraps a spec and will behave just like the wrapped spec but if @@ -174,10 +174,10 @@ #?(:clj (defn spec-ns-var [var-sym] (symbol - (if (in-cljs-compile?) - "cljs.spec.alpha" - "clojure.spec.alpha") - (name var-sym)))) + (if (in-cljs-compile?) + "cljs.spec.alpha" + "clojure.spec.alpha") + (name var-sym)))) ;; ---------------------------------------------------------------------- ;; Misspelled and Unknown-keys @@ -213,16 +213,16 @@ :else nil)] (most-similar-to val known-keys))] (assoc prob - :expound.spec.problem/type ::misspelled-key - ;; limiting the predicate to the matches - ;; makes the default expound errors pretty good - ;; but could be confusing in other circumstances - :pred (set sim) - ::misspelled-key val - ::likely-misspelling-of sim) + :expound.spec.problem/type ::misspelled-key + ;; limiting the predicate to the matches + ;; makes the default expound errors pretty good + ;; but could be confusing in other circumstances + :pred (set sim) + ::misspelled-key val + ::likely-misspelling-of sim) (assoc prob - :expound.spec.problem/type ::unknown-key - ::unknown-key val))) + :expound.spec.problem/type ::unknown-key + ::unknown-key val))) #?(:clj (defmacro not-misspelled-spec @@ -308,9 +308,9 @@ this in mind and is fairly conservative in its spelling checks." [& args] `(pre-check - (warning-spec (~(spec-ns-var 'map-of) - (not-misspelled-spec ~(get-known-keys args)) any?)) - (~(spec-ns-var 'keys) ~@args)))) + (warning-spec (~(spec-ns-var 'map-of) + (not-misspelled-spec ~(get-known-keys args)) any?)) + (~(spec-ns-var 'keys) ~@args)))) #?(:clj (defmacro strict-keys @@ -326,9 +326,9 @@ `strict-keys`" [& args] `(pre-check ;~(spec-ns-var 'and) - (warning-spec (~(spec-ns-var 'map-of) - (known-keys-spec ~(get-known-keys args)) any?)) - (~(spec-ns-var 'keys) ~@args)))) + (warning-spec (~(spec-ns-var 'map-of) + (known-keys-spec ~(get-known-keys args)) any?)) + (~(spec-ns-var 'keys) ~@args)))) ;; ---------------------------------------------------------------------- ;; Warning only specs diff --git a/src/spec_tools/spell_spec/expound.cljc b/src/spec_tools/spell_spec/expound.cljc index 29c216d9..8ed16370 100644 --- a/src/spec_tools/spell_spec/expound.cljc +++ b/src/spec_tools/spell_spec/expound.cljc @@ -1,10 +1,10 @@ (ns spec-tools.spell-spec.expound (:require - [clojure.string :as string] - [expound.alpha :as exp] - [expound.ansi :as ansi] - [expound.printer :as printer] - [expound.problems :as problems])) + [clojure.string :as string] + [expound.alpha :as exp] + [expound.ansi :as ansi] + [expound.printer :as printer] + [expound.problems :as problems])) (defn format-correction-list [options] (str (when (> (count options) 1) " one of") @@ -15,10 +15,10 @@ (defn exp-formated [header _type spec-name val path problems opts] (printer/format - "%s\n\n%s\n\n%s" - (#'exp/header-label header) - (printer/indent (#'exp/*value-str-fn* spec-name val path (problems/value-in val path))) - (exp/expected-str _type spec-name val path problems opts))) + "%s\n\n%s\n\n%s" + (#'exp/header-label header) + (printer/indent (#'exp/*value-str-fn* spec-name val path (problems/value-in val path))) + (exp/expected-str _type spec-name val path problems opts))) (defmethod exp/problem-group-str :spec-tools.spell-spec.alpha/misspelled-key [_type spec-name val path problems opts] (exp-formated "Misspelled map key" _type spec-name val path problems opts)) diff --git a/src/spec_tools/swagger/core.cljc b/src/spec_tools/swagger/core.cljc index 5587a448..dc093b24 100644 --- a/src/spec_tools/swagger/core.cljc +++ b/src/spec_tools/swagger/core.cljc @@ -117,15 +117,15 @@ (defmethod extract-parameter :default [in spec] (let [{:keys [properties required]} (transform spec {:in in, :type :parameter})] (mapv - (fn [[k {:keys [type] :as schema}]] - (merge - {:in (name in) - :name k - :description (-> spec st/spec-description (or "")) - :type type - :required (contains? (set required) k)} - schema)) - properties))) + (fn [[k {:keys [type] :as schema}]] + (merge + {:in (name in) + :name k + :description (-> spec st/spec-description (or "")) + :type type + :required (contains? (set required) k)} + schema)) + properties))) ;; ;; expand the spec @@ -136,11 +136,11 @@ (defmethod expand ::responses [_ v acc _] {:responses (into - (or (:responses acc) {}) - (for [[status response] v] - [status (as-> response $ - (if (:schema $) (update $ :schema transform {:type :schema}) $) - (update $ :description (fnil identity "")))]))}) + (or (:responses acc) {}) + (for [[status response] v] + [status (as-> response $ + (if (:schema $) (update $ :schema transform {:type :schema}) $) + (update $ :description (fnil identity "")))]))}) (defmethod expand ::parameters [_ v acc _] (let [old (or (:parameters acc) []) @@ -148,12 +148,12 @@ merged (->> (into old new) (reverse) (reduce - (fn [[ps cache :as acc] p] - (let [c (select-keys p [:in :name])] - (if-not (cache c) - [(conj ps p) (conj cache c)] - acc))) - [[] #{}]) + (fn [[ps cache :as acc] p] + (let [c (select-keys p [:in :name])] + (if-not (cache c) + [(conj ps p) (conj cache c)] + acc))) + [[] #{}]) (first) (reverse) (vec))] @@ -162,17 +162,17 @@ (defn expand-qualified-keywords [x options] (let [accept? (set (keys (methods expand)))] (walk/postwalk - (fn [x] - (if (map? x) - (reduce-kv - (fn [acc k v] - (if (accept? k) - (-> acc (dissoc k) (merge (expand k v acc options))) - acc)) - x - x) - x)) - x))) + (fn [x] + (if (map? x) + (reduce-kv + (fn [acc k v] + (if (accept? k) + (-> acc (dissoc k) (merge (expand k v acc options))) + acc)) + x + x) + x)) + x))) ;; ;; generate the swagger spec diff --git a/src/spec_tools/swagger/spec.cljc b/src/spec_tools/swagger/spec.cljc index eb1c4ea8..b251b522 100644 --- a/src/spec_tools/swagger/spec.cljc +++ b/src/spec_tools/swagger/spec.cljc @@ -4,26 +4,26 @@ (s/def ::external-docs (ds/spec - ::external-docs - {(ds/opt :description) string? - :url string?})) + ::external-docs + {(ds/opt :description) string? + :url string?})) (s/def ::security-definitions (ds/spec - ::security-definitions - {string? {:type (s/spec #{"basic" "apiKey" "oauth2"}) - (ds/opt :description) string? - (ds/opt :name) string? - (ds/opt :in) (s/spec #{"query" "header"}) - (ds/opt :flow) (s/spec #{"implicit" "password" "application" "accessCode"}) - (ds/opt :authorizationUrl) string? - (ds/opt :tokenUrl) string? - (ds/opt :scopes) {string? string?}}})) + ::security-definitions + {string? {:type (s/spec #{"basic" "apiKey" "oauth2"}) + (ds/opt :description) string? + (ds/opt :name) string? + (ds/opt :in) (s/spec #{"query" "header"}) + (ds/opt :flow) (s/spec #{"implicit" "password" "application" "accessCode"}) + (ds/opt :authorizationUrl) string? + (ds/opt :tokenUrl) string? + (ds/opt :scopes) {string? string?}}})) (s/def ::security-requirements (ds/spec - ::security-requirements - {string? [string?]})) + ::security-requirements + {string? [string?]})) (s/def ::header-object any?) @@ -33,57 +33,57 @@ (s/def ::response (ds/spec - ::response - {(ds/opt :description) string? - (ds/opt :schema) ::spec - (ds/opt :headers) {string? ::header-object} - (ds/opt :examples) {string? any?}})) + ::response + {(ds/opt :description) string? + (ds/opt :schema) ::spec + (ds/opt :headers) {string? ::header-object} + (ds/opt :examples) {string? any?}})) (s/def ::operation (ds/spec - ::operation - {(ds/opt :tags) [string?] - (ds/opt :summary) string? - (ds/opt :description) string? - (ds/opt :externalDocs) ::external-docs - (ds/opt :operationId) string? - (ds/opt :consumes) #{string?} - (ds/opt :produces) #{string?} - (ds/opt :parameters) {(ds/opt :query) ::spec - (ds/opt :header) ::spec - (ds/opt :path) ::spec - (ds/opt :formData) ::spec - (ds/opt :body) ::spec} - (ds/opt :responses) (s/map-of ::response-code ::response) - (ds/opt :schemes) (s/coll-of #{"http", "https", "ws", "wss"} :into #{}) - (ds/opt :deprecated) boolean? - (ds/opt :security) ::security-requirements})) + ::operation + {(ds/opt :tags) [string?] + (ds/opt :summary) string? + (ds/opt :description) string? + (ds/opt :externalDocs) ::external-docs + (ds/opt :operationId) string? + (ds/opt :consumes) #{string?} + (ds/opt :produces) #{string?} + (ds/opt :parameters) {(ds/opt :query) ::spec + (ds/opt :header) ::spec + (ds/opt :path) ::spec + (ds/opt :formData) ::spec + (ds/opt :body) ::spec} + (ds/opt :responses) (s/map-of ::response-code ::response) + (ds/opt :schemes) (s/coll-of #{"http", "https", "ws", "wss"} :into #{}) + (ds/opt :deprecated) boolean? + (ds/opt :security) ::security-requirements})) (s/def ::swagger (ds/spec - ::swagger - {:swagger (s/spec #{"2.0"}) - :info {:title string? - (ds/opt :description) string? - (ds/opt :termsOfService) string? - (ds/opt :contact) {(ds/opt :name) string? - (ds/opt :url) string? - (ds/opt :email) string?} - (ds/opt :license) {:name string? - (ds/opt :url) string?} - :version string?} - (ds/opt :host) string? - (ds/opt :basePath) string? - (ds/opt :schemes) (s/coll-of #{"http", "https", "ws", "wss"} :into #{}) - (ds/opt :consumes) #{string?} - (ds/opt :produces) #{string?} - (ds/opt :paths) {string? {(s/spec #{:get :put :post :delete :options :head :patch}) ::operation}} - ;(ds/opt :definitions) map? - ;(ds/opt :parameters) map? - ;(ds/opt :responses) map? - (ds/opt :securityDefinitions) ::security-definitions - (ds/opt :security) ::security-requirements - (ds/opt :tags) [{:name string? - (ds/opt :description) string? - (ds/opt :externalDocs) ::external-docs}] - (ds/opt :externalDocs) ::external-docs})) + ::swagger + {:swagger (s/spec #{"2.0"}) + :info {:title string? + (ds/opt :description) string? + (ds/opt :termsOfService) string? + (ds/opt :contact) {(ds/opt :name) string? + (ds/opt :url) string? + (ds/opt :email) string?} + (ds/opt :license) {:name string? + (ds/opt :url) string?} + :version string?} + (ds/opt :host) string? + (ds/opt :basePath) string? + (ds/opt :schemes) (s/coll-of #{"http", "https", "ws", "wss"} :into #{}) + (ds/opt :consumes) #{string?} + (ds/opt :produces) #{string?} + (ds/opt :paths) {string? {(s/spec #{:get :put :post :delete :options :head :patch}) ::operation}} + ;(ds/opt :definitions) map? + ;(ds/opt :parameters) map? + ;(ds/opt :responses) map? + (ds/opt :securityDefinitions) ::security-definitions + (ds/opt :security) ::security-requirements + (ds/opt :tags) [{:name string? + (ds/opt :description) string? + (ds/opt :externalDocs) ::external-docs}] + (ds/opt :externalDocs) ::external-docs})) diff --git a/src/spec_tools/transform.cljc b/src/spec_tools/transform.cljc index 4bbc8364..07b46526 100644 --- a/src/spec_tools/transform.cljc +++ b/src/spec_tools/transform.cljc @@ -5,7 +5,7 @@ [goog.date.Date] [goog.Uri]]) [clojure.set :as set] - [clojure.edn :as edn] + #?(:clj [clojure.edn :as edn]) [spec-tools.parse :as parse] [spec-tools.impl :as impl]) #?(:clj @@ -206,25 +206,25 @@ (def json-type-decoders (merge - {:keyword string->keyword - :uuid (keyword-or-string-> string->uuid) - :date (keyword-or-string-> string->date) - :symbol (keyword-or-string-> string->symbol) - :long (keyword-> string->long) - :double (keyword-> string->double) - :boolean (keyword-> string->boolean) - :string keyword->string} - #?(:clj - {:uri string->uri - :bigdec (number-or-string-> string->decimal) - :ratio string->ratio}))) + {:keyword string->keyword + :uuid (keyword-or-string-> string->uuid) + :date (keyword-or-string-> string->date) + :symbol (keyword-or-string-> string->symbol) + :long (keyword-> string->long) + :double (keyword-> string->double) + :boolean (keyword-> string->boolean) + :string keyword->string} + #?(:clj + {:uri string->uri + :bigdec (number-or-string-> string->decimal) + :ratio string->ratio}))) (def string-type-decoders (merge - json-type-decoders - {:long (keyword-or-string-> string->long) - :double (keyword-or-string-> string->double) - :boolean (keyword-or-string-> string->boolean)})) + json-type-decoders + {:long (keyword-or-string-> string->long) + :double (keyword-or-string-> string->double) + :boolean (keyword-or-string-> string->boolean)})) (def strip-extra-keys-type-decoders {:map strip-extra-keys}) @@ -253,6 +253,6 @@ (def string-type-encoders (merge - json-type-encoders - {:long any->string - :double any->string})) + json-type-encoders + {:long any->string + :double any->string})) diff --git a/src/spec_tools/visitor.cljc b/src/spec_tools/visitor.cljc index a4dfc2b3..ece3401e 100644 --- a/src/spec_tools/visitor.cljc +++ b/src/spec_tools/visitor.cljc @@ -90,8 +90,8 @@ (defmethod visit-spec 'clojure.spec.alpha/every-kv [spec accept options] (let [[_ inner-spec1 inner-spec2] (impl/extract-form spec)] (accept 'clojure.spec.alpha/every-kv spec (mapv - #(visit % accept options) - [inner-spec1 inner-spec2]) options))) + #(visit % accept options) + [inner-spec1 inner-spec2]) options))) (defmethod visit-spec 'clojure.spec.alpha/coll-of [spec accept options] (let [form (impl/extract-form spec) @@ -167,18 +167,18 @@ ;; TODO: uses ^:skip-wiki functions from clojure.spec (comment - (defn convert-specs! - "Collects all registered subspecs from a spec and - transforms their registry values into Spec Records. - Does not convert clojure.spec.alpha regex ops." - [spec] - (let [specs (visit spec (spec-collector)) - report (atom #{})] - (doseq [[k v] specs] - (if (keyword? v) - (swap! report into (convert-specs! v)) - (when-not (or (s/regex? v) (st/spec? v)) - (let [s (st/create-spec {:spec v})] - (impl/register-spec! k s) - (swap! report conj k))))) - @report))) + (defn convert-specs! + "Collects all registered subspecs from a spec and + transforms their registry values into Spec Records. + Does not convert clojure.spec.alpha regex ops." + [spec] + (let [specs (visit spec (spec-collector)) + report (atom #{})] + (doseq [[k v] specs] + (if (keyword? v) + (swap! report into (convert-specs! v)) + (when-not (or (s/regex? v) (st/spec? v)) + (let [s (st/create-spec {:spec v})] + (impl/register-spec! k s) + (swap! report conj k))))) + @report))) diff --git a/test/clj/spec_tools/perf_test.clj b/test/clj/spec_tools/perf_test.clj index 6a5d4210..1fc14d93 100644 --- a/test/clj/spec_tools/perf_test.clj +++ b/test/clj/spec_tools/perf_test.clj @@ -47,7 +47,7 @@ (let [call #(s/valid? ::age 12)] (assert (call)) (cc/quick-bench - (call))) + (call))) ; 1480ns ; 77ns (alpha12) @@ -56,14 +56,14 @@ (let [call #(s/valid? ::x-age 12)] (assert (call)) (cc/quick-bench - (call))) + (call))) ; 430ns (title "schema: s/Int") (let [call #(schema/check age 12)] (assert (nil? (call))) (cc/quick-bench - (call))) + (call))) ; 31ns (title "schema: s/Int (compiled)") @@ -71,7 +71,7 @@ call #(checker 12)] (assert (nil? (call))) (cc/quick-bench - (call)))) + (call)))) (defn conform-test [] @@ -84,7 +84,7 @@ (let [call #(s/conform ::age 12)] (assert (= (call) 12)) (cc/quick-bench - (call))) + (call))) ; 1430ns ; 95ns (alpha12) @@ -93,14 +93,14 @@ (let [call #(st/conform ::x-age 12)] (assert (= (call) 12)) (cc/quick-bench - (call))) + (call))) ; 425ns (title "schema: s/Int") (let [call #((coerce/coercer age (constantly nil)) 12)] (assert (= (call) 12)) (cc/quick-bench - (call))) + (call))) ; 25ns (title "schema: s/Int (compiled)") @@ -108,7 +108,7 @@ call #(coercer 12)] (assert (= (call) 12)) (cc/quick-bench - (call)))) + (call)))) (defn conform-test2 [] @@ -124,7 +124,7 @@ (let [call #(st/conform sizes-spec ["L" "M"] st/string-transformer)] (assert (= (call) #{:L :M})) (cc/quick-bench - (call))) + (call))) ; 3700ns ; 990ns (alpha12) @@ -133,7 +133,7 @@ (let [call #(st/conform sizes-spec #{:L :M} st/string-transformer)] (assert (= (call) #{:L :M})) (cc/quick-bench - (call))) + (call))) ; 1100ns (title "schema: conform keyword enum") @@ -141,7 +141,7 @@ call #(coercer ["L" "M"])] (assert (= (call) #{:L :M})) (cc/quick-bench - (call))) + (call))) ; 780ns (title "schema: conform keyword enum - no-op") @@ -149,7 +149,7 @@ call #(coercer #{:L :M})] (assert (= (call) #{:L :M})) (cc/quick-bench - (call))))) + (call))))) (s/def ::order-id spec/integer?) (s/def ::product-id spec/integer?) @@ -178,8 +178,8 @@ (s/keys :req-un [::company-id ::customer-type])) (s/def ::requester (s/multi-spec requester-type ::customer-type)) (s/def ::order (s/merge - ::requester - (s/keys :req-un [::order-id ::orderlines ::receiver]))) + ::requester + (s/keys :req-un [::order-id ::orderlines ::receiver]))) (s/def ::order-with-line (s/and ::order #(> (::orderlines 1)))) (def sample-order-valid @@ -209,7 +209,7 @@ (def sample-multi-order-corporate (merge sample-order-valid {:customer-type "corporate" - :company-id "12345"})) + :company-id "12345"})) (defn multi-spec-coercer-test [] @@ -218,21 +218,21 @@ ;87.668605 µs (title "Multi coercer") (let [coercer #(st/coerce ::order % st/string-transformer) - call #(coercer sample-multi-order-corporate) + call #(coercer sample-multi-order-corporate) expected (merge sample-order-valid {:customer-type :corporate - :company-id 12345})] - (assert (= (call) expected)) - (cc/quick-bench + :company-id 12345})] + (assert (= (call) expected)) + (cc/quick-bench (call)))) (s/form - (s/cat ::first keyword? - :integer-lists (s/+ - (s/coll-of - (s/keys :req-un [::order-id - ::orderlines - ::receiver]))))) + (s/cat ::first keyword? + :integer-lists (s/+ + (s/coll-of + (s/keys :req-un [::order-id + ::orderlines + ::receiver]))))) (schema/defschema Order {:order-id Long @@ -255,7 +255,7 @@ (let [call #(st/conform ::order sample-order st/string-transformer)] (assert (= (call) sample-order-valid)) (cc/quick-bench - (call))) + (call))) ; 2.8µs (alpha12) ; 2.7µs (alpha14) @@ -263,7 +263,7 @@ (let [call #(st/conform ::order sample-order-valid st/string-transformer)] (assert (= (call) sample-order-valid)) (cc/quick-bench - (call))) + (call))) ; 9.1µs (title "schema: conform") @@ -271,7 +271,7 @@ call #(coercer sample-order)] (assert (= (call) sample-order-valid)) (cc/quick-bench - (call))) + (call))) ; 9.3µs (title "schema: conform - no-op") @@ -279,11 +279,11 @@ call #(coercer sample-order-valid)] (assert (= (call) sample-order-valid)) (cc/quick-bench - (call)))) + (call)))) (comment - (valid-test) - (conform-test) - (conform-test2) - (conform-test3) - (multi-spec-coercer-test)) + (valid-test) + (conform-test) + (conform-test2) + (conform-test3) + (multi-spec-coercer-test)) diff --git a/test/cljc/spec_tools/data_spec_test.cljc b/test/cljc/spec_tools/data_spec_test.cljc index f35d6cfe..27151ed0 100644 --- a/test/cljc/spec_tools/data_spec_test.cljc +++ b/test/cljc/spec_tools/data_spec_test.cljc @@ -23,14 +23,14 @@ :zip string?})} person-spec (ds/spec ::person person) person-keys-spec (st/spec - (s/keys - :req [::id ::age] - :req-un [:spec-tools.data-spec-test$person/boss - :spec-tools.data-spec-test$person/name - :spec-tools.data-spec-test$person/languages - :spec-tools.data-spec-test$person/orders - :spec-tools.data-spec-test$person/address] - :opt-un [:spec-tools.data-spec-test$person/description]))] + (s/keys + :req [::id ::age] + :req-un [:spec-tools.data-spec-test$person/boss + :spec-tools.data-spec-test$person/name + :spec-tools.data-spec-test$person/languages + :spec-tools.data-spec-test$person/orders + :spec-tools.data-spec-test$person/address] + :opt-un [:spec-tools.data-spec-test$person/description]))] (testing "normal keys-spec-spec is generated" (is (= (s/form (dissoc person-keys-spec :name)) @@ -84,9 +84,9 @@ (testing "heterogenous lists" (is (thrown-with-msg? - #?(:clj Exception, :cljs js/Error) - #"should be homogeneous" - (ds/spec {:spec [int? int?]})))) + #?(:clj Exception, :cljs js/Error) + #"should be homogeneous" + (ds/spec {:spec [int? int?]})))) (testing "or spec" (let [strings-or-keywords (ds/or {::ui-target {:id string?} @@ -94,22 +94,22 @@ (is (thrown? ExceptionInfo (#'spec-tools.data-spec/-or-spec ::foo :bar))) (is (s/valid? - (ds/spec ::str-kw-vector strings-or-keywords) - {:id "1"})) + (ds/spec ::str-kw-vector strings-or-keywords) + {:id "1"})) (is (s/valid? - (ds/spec ::str-kw-vector strings-or-keywords) - [:foo :bar])) + (ds/spec ::str-kw-vector strings-or-keywords) + [:foo :bar])) (is (s/valid? - (ds/spec ::str-kw-vector [strings-or-keywords]) - [{:id "1"}])) + (ds/spec ::str-kw-vector [strings-or-keywords]) + [{:id "1"}])) (is (s/valid? - (ds/spec ::str-kw-map {:test strings-or-keywords}) - {:test {:id "1"}})) + (ds/spec ::str-kw-map {:test strings-or-keywords}) + {:test {:id "1"}})) (testing "non-qualified keywords are ok too" (is (= {:values [[:strings ["1" "2"]] [:ints [3]]]} (s/conform - (ds/spec ::values {:values [(ds/or {:ints [int?], :strings [string?]})]}) - {:values [["1" "2"] [3]]})))))) + (ds/spec ::values {:values [(ds/or {:ints [int?], :strings [string?]})]}) + {:values [["1" "2"] [3]]})))))) (testing "encoding with or spec" (testing "when value matches first form of the or" @@ -125,65 +125,65 @@ (testing "top-level vector" (is (true? - (s/valid? - (ds/spec ::vector [{:olipa {:kerran string?}}]) - [{:olipa {:kerran "avaruus"}} - {:olipa {:kerran "elämä"}}]))) + (s/valid? + (ds/spec ::vector [{:olipa {:kerran string?}}]) + [{:olipa {:kerran "avaruus"}} + {:olipa {:kerran "elämä"}}]))) (is (false? - (s/valid? - (ds/spec ::vector [{:olipa {:kerran string?}}]) - [{:olipa {:kerran :muumuu}}])))) + (s/valid? + (ds/spec ::vector [{:olipa {:kerran string?}}]) + [{:olipa {:kerran :muumuu}}])))) (testing "top-level set" (is (true? - (s/valid? - (ds/spec ::vector #{{:olipa {:kerran string?}}}) - #{{:olipa {:kerran "avaruus"}} - {:olipa {:kerran "elämä"}}}))) + (s/valid? + (ds/spec ::vector #{{:olipa {:kerran string?}}}) + #{{:olipa {:kerran "avaruus"}} + {:olipa {:kerran "elämä"}}}))) (is (false? - (s/valid? - (ds/spec ::vector #{{:olipa {:kerran string?}}}) - #{{:olipa {:kerran :muumuu}}})))) + (s/valid? + (ds/spec ::vector #{{:olipa {:kerran string?}}}) + #{{:olipa {:kerran :muumuu}}})))) (testing "mega-nested" (is (true? - (s/valid? - (ds/spec ::vector [[[[[[[[[[string?]]]]]]]]]]) - [[[[[[[[[["kikka" "kakka" "kukka"]]]]]]]]]]))) + (s/valid? + (ds/spec ::vector [[[[[[[[[[string?]]]]]]]]]]) + [[[[[[[[[["kikka" "kakka" "kukka"]]]]]]]]]]))) (is (false? - (s/valid? - (ds/spec ::vector [[[[[[[[[[string?]]]]]]]]]]) - [[[[[[[[[123]]]]]]]]])))) + (s/valid? + (ds/spec ::vector [[[[[[[[[[string?]]]]]]]]]]) + [[[[[[[[[123]]]]]]]]])))) (testing "predicate keys" (is - (true? - (s/valid? - (ds/spec ::pred-keys {string? {keyword? [integer?]}}) - {"winning numbers" {:are [1 12 46 45]} - "empty?" {:is []}}))) + (true? + (s/valid? + (ds/spec ::pred-keys {string? {keyword? [integer?]}}) + {"winning numbers" {:are [1 12 46 45]} + "empty?" {:is []}}))) (is - (false? - (s/valid? - (ds/spec ::pred-keys {string? {keyword? [integer?]}}) - {"invalid spec" "is this"})))) + (false? + (s/valid? + (ds/spec ::pred-keys {string? {keyword? [integer?]}}) + {"invalid spec" "is this"})))) (testing "set keys" (let [spec (ds/spec ::pred-keys {(s/spec #{:one :two}) string?})] (is - (= true - (s/valid? spec {:one "beer"}) - (s/valid? spec {:two "beers"}))) + (= true + (s/valid? spec {:one "beer"}) + (s/valid? spec {:two "beers"}))) (is - (= false - (s/valid? spec {:three "beers"}))))) + (= false + (s/valid? spec {:three "beers"}))))) (testing "map-of key transformer" (is (= {:thanks :alex} (st/conform - (ds/spec ::kikka {keyword? keyword?}) - {"thanks" "alex"} - st/string-transformer))))) + (ds/spec ::kikka {keyword? keyword?}) + {"thanks" "alex"} + st/string-transformer))))) (deftest top-level-maybe-test (let [spec (ds/spec ::maybe (ds/maybe {:n int?}))] @@ -201,9 +201,9 @@ (s/valid? spec1 {::i 1}) (s/valid? spec2 {::i 1}))) (is (= `(spec-tools.core/spec - {:spec (clojure.spec.alpha/keys :req [::i]) - :type :map - :leaf? false}) + {:spec (clojure.spec.alpha/keys :req [::i]) + :type :map + :leaf? false}) (s/form (dissoc spec1 :name)) (s/form (dissoc spec2 :name)))))) @@ -220,15 +220,15 @@ :c any?}] (testing "by default, plain keyword keys are required" (let [spec (ds/spec - {:name ::kikka - :spec data})] + {:name ::kikka + :spec data})] (is (s/valid? spec {:a 1, :b 1, :c 1})) (is (not (s/valid? spec {:a 1}))))) (testing "plain keyword keys can be made optional by default" (let [spec (ds/spec - {:name ::kikka - :spec data - :keys-default ds/opt})] + {:name ::kikka + :spec data + :keys-default ds/opt})] (is (s/valid? spec {:a 1, :b 1, :c 1})) (is (s/valid? spec {:a 1})))))) ;; TODO @@ -239,19 +239,19 @@ (deftest encode-decode-test (let [spec (ds/spec - {:name ::order - :spec {:id int? - :address {:street string? - :country keyword?} - :tags #{keyword?} - :symbol symbol? - :price double? - :uuid uuid? - :shipping inst? - :secret (st/spec - {:spec string? - :encode/string #(apply str (reverse %2)) - :decode/string #(apply str (reverse %2))})}}) + {:name ::order + :spec {:id int? + :address {:street string? + :country keyword?} + :tags #{keyword?} + :symbol symbol? + :price double? + :uuid uuid? + :shipping inst? + :secret (st/spec + {:spec string? + :encode/string #(apply str (reverse %2)) + :decode/string #(apply str (reverse %2))})}}) value {:id 1 :address {:street "Pellavatehtaankatu 10b" :country :fi} @@ -277,8 +277,8 @@ (is (= value (st/decode spec value-string st/string-transformer)))) (testing "roundtrip" (is (= value-string (as-> value-string $ - (st/decode spec $ st/string-transformer) - (st/encode spec $ st/string-transformer))))))) + (st/decode spec $ st/string-transformer) + (st/encode spec $ st/string-transformer))))))) (deftest spec-name-test diff --git a/test/cljc/spec_tools/impl_test.cljc b/test/cljc/spec_tools/impl_test.cljc index 9db07d69..f1525617 100644 --- a/test/cljc/spec_tools/impl_test.cljc +++ b/test/cljc/spec_tools/impl_test.cljc @@ -20,23 +20,23 @@ :b [1 2 3 4] :c {:a 2, :b 1}} (impl/deep-merge - {:a 1 - :b [1 2] - :c {:a 1}} - {:a [1 2] - :c {:a 2, :b 1}} - {:a 2 - :b [3 4]})))) + {:a 1 + :b [1 2] + :c {:a 1}} + {:a [1 2] + :c {:a 2, :b 1}} + {:a 2 + :b [3 4]})))) (deftest unlift-keys-test (is (= {:olut 0.5 :sielu true} (impl/unlift-keys - {:iso/olut 0.5 - :iso/sielu true - :olipa "kerran" - :kikka/kukka "kakka"} - "iso")))) + {:iso/olut 0.5 + :iso/sielu true + :olipa "kerran" + :kikka/kukka "kakka"} + "iso")))) (def ignoring-spec #(dissoc % ::s/spec)) @@ -75,9 +75,9 @@ (is (= (s/form spec) (s/form impl))) (is (= `(s/map-of - (st/spec {:spec string? :type :string :leaf? true}) - (st/spec {:spec string? :type :string :leaf? true}) - :conform-keys true) + (st/spec {:spec string? :type :string :leaf? true}) + (st/spec {:spec string? :type :string :leaf? true}) + :conform-keys true) (s/form (impl/map-of-spec spec/string? spec/string?)))) (is (= nil (s/explain-data spec {"key" "value"}) diff --git a/test/cljc/spec_tools/json_schema_test.cljc b/test/cljc/spec_tools/json_schema_test.cljc index 3b0d1bec..d6a76c0f 100644 --- a/test/cljc/spec_tools/json_schema_test.cljc +++ b/test/cljc/spec_tools/json_schema_test.cljc @@ -186,14 +186,14 @@ (def person-spec (ds/spec - ::person - {::id integer? - :age ::age - :name string? - :likes {string? boolean?} - (ds/req :languages) #{keyword?} - (ds/opt :address) {:street string? - :zip string?}})) + ::person + {::id integer? + :age ::age + :name string? + :likes {string? boolean?} + (ds/req :languages) #{keyword?} + (ds/opt :address) {:street string? + :zip string?}})) (deftest readme-test (is (= {:type "object" @@ -216,11 +216,11 @@ :description "it's an int" :default 42} (jsc/transform - (st/spec - {:spec integer? - :name "integer" - :description "it's an int" - :json-schema/default 42}))))) + (st/spec + {:spec integer? + :name "integer" + :description "it's an int" + :json-schema/default 42}))))) (deftest deeply-nested-test (is (= {:type "array" @@ -230,9 +230,9 @@ :items {:type "array" :items {:type "string"}}}}} (jsc/transform - (ds/spec - ::nested - [[[[string?]]]]))))) + (ds/spec + ::nested + [[[[string?]]]]))))) (s/def ::user any?) (s/def ::name string?) @@ -259,8 +259,8 @@ :properties {"foo" {:type "string"}} :required ["foo"]}]} (jsc/transform - (s/or :bar (s/keys :req-un [::bar]) - :foo (s/keys :req-un [::foo])))) + (s/or :bar (s/keys :req-un [::bar]) + :foo (s/keys :req-un [::foo])))) "s/or generates anyOf") (is (= {:type "object" @@ -269,9 +269,9 @@ "bar" {:type "string"}} :required ["a"]} (jsc/transform - (s/merge (s/keys :req-un [::a]) - (s/or :foo (s/keys :req-un [::foo]) - :bar (s/keys :req-un [::bar]))))) + (s/merge (s/keys :req-un [::a]) + (s/or :foo (s/keys :req-un [::foo]) + :bar (s/keys :req-un [::bar]))))) "anyOf properties are merged into properties") (is (= {:type "object" @@ -280,28 +280,28 @@ "bar" {:type "string"}} :required ["a" "bar" "foo"]} (jsc/transform - (s/merge (s/keys :req-un [::a]) - (s/and (s/keys :req-un [::bar]) - (s/keys :req-un [::foo]))))) + (s/merge (s/keys :req-un [::a]) + (s/and (s/keys :req-un [::bar]) + (s/keys :req-un [::foo]))))) "allOf properties are merged into properties and required")) (deftest backport-swagger-meta-unnamespaced (is (= (jsc/transform - (st/spec {:spec string? - :json-schema {:type "string" - :format "password" - :random-value "42"}})) + (st/spec {:spec string? + :json-schema {:type "string" + :format "password" + :random-value "42"}})) {:type "string" :format "password" :random-value "42"})) (is (= (jsc/transform - (st/spec {:spec string? - :json-schema {:type "object"} - :json-schema/format "password"})) + (st/spec {:spec string? + :json-schema {:type "object"} + :json-schema/format "password"})) {:type "object"})) (is (= (jsc/transform - (st/spec {:spec string? - :json-schema/type "string" - :json-schema/format "password" - :json-schema/random-value "42"})) + (st/spec {:spec string? + :json-schema/type "string" + :json-schema/format "password" + :json-schema/random-value "42"})) {:type "string" :format "password" :random-value "42"}))) diff --git a/test/cljc/spec_tools/openapi/core_test.cljc b/test/cljc/spec_tools/openapi/core_test.cljc index 1cea2179..9f9300a4 100644 --- a/test/cljc/spec_tools/openapi/core_test.cljc +++ b/test/cljc/spec_tools/openapi/core_test.cljc @@ -10,13 +10,13 @@ (s/def ::nilable-spec (s/nilable string?)) (s/def ::keys-spec (s/keys :req-un [::integer-spec])) (s/def ::spec (st/spec - {:spec string? - :description "Spec description" - :name "spec-name" + {:spec string? + :description "Spec description" + :name "spec-name" :json-schema/default "123" :json-schema/example "json-schema-exapmle" - :swagger/example "swagger-example" - :openapi/example "openapi3-example"})) + :swagger/example "swagger-example" + :openapi/example "openapi3-example"})) (s/def ::keys-spec2 (s/keys :req-un [::integer-spec ::spec])) (def expectations @@ -50,16 +50,16 @@ {:minimum 1 :maximum 10}]} (s/keys :req-un [::integer-spec] :opt-un [::string-spec]) - {:type "object" + {:type "object" :properties {"integer-spec" {:type "integer"} - "string-spec" {:type "string"}} - :required ["integer-spec"]} + "string-spec" {:type "string"}} + :required ["integer-spec"]} ::keys-spec - {:type "object" + {:type "object" :properties {"integer-spec" {:type "integer"}} - :required ["integer-spec"] - :title "spec-tools.openapi.core-test/keys-spec"} + :required ["integer-spec"] + :title "spec-tools.openapi.core-test/keys-spec"} (s/and int? pos?) {:allOf [{:type "integer" :format "int64"} @@ -74,10 +74,10 @@ (s/merge (s/keys :req-un [::integer-spec]) (s/keys :req-un [::string-spec])) - {:type "object" + {:type "object" :properties {"integer-spec" {:type "integer"} - "string-spec" {:type "string"}} - :required ["integer-spec" "string-spec"]} + "string-spec" {:type "string"}} + :required ["integer-spec" "string-spec"]} sequential? {:type "array" :items {}} @@ -86,61 +86,61 @@ {:type "array" :items {:type "integer"}} (s/every-kv string? integer?) - {:type "object" + {:type "object" :additionalProperties {:type "integer"}} (s/coll-of string?) - {:type "array" + {:type "array" :items {:type "string"}} (s/coll-of string? :into '()) - {:type "array" + {:type "array" :items {:type "string"}} (s/coll-of string? :into []) - {:type "array" + {:type "array" :items {:type "string"}} (s/coll-of string? :into #{}) - {:type "array" - :items {:type "string"} + {:type "array" + :items {:type "string"} :uniqueItems true} (s/map-of string? integer?) - {:type "object" + {:type "object" :additionalProperties {:type "integer"}} (s/* integer?) - {:type "array" + {:type "array" :items {:type "integer"}} (s/+ integer?) - {:type "array" - :items {:type "integer"} + {:type "array" + :items {:type "integer"} :minItems 1} (s/? integer?) - {:type "array" - :items {:type "integer"} + {:type "array" + :items {:type "integer"} :minItems 0} (s/alt :int integer? :string string?) - {:type "array" + {:type "array" :items {:oneOf [{:type "integer"} {:type "string"}]}} (s/cat :int integer? :string string?) - {:type "array" + {:type "array" :items {:anyOf [{:type "integer"} {:type "string"}]}} (s/tuple integer? string?) - {:type "array" + {:type "array" :items {:anyOf [{:type "integer"} {:type "string"}]}} (s/map-of string? clojure.core/integer?) - {:type "object" + {:type "object" :additionalProperties {:type "integer"}} (s/nilable string?) @@ -148,11 +148,11 @@ {:type "null"}]} ::spec - {:type "string" + {:type "string" :description "Spec description" - :title "spec-tools.openapi.core-test/spec" - :example "openapi3-example", - :default "123"}}) + :title "spec-tools.openapi.core-test/spec" + :example "openapi3-example", + :default "123"}}) (deftest transform-test (doseq [[spec openapi-spec] expectations] @@ -171,138 +171,138 @@ (deftest expand-test (testing "::parameters" (is (= {:parameters - [{:name "username" - :in "path" + [{:name "username" + :in "path" :description "username to fetch" - :required true - :schema {:type "string"} - :style "simple"} - {:name "id" - :in "path" + :required true + :schema {:type "string"} + :style "simple"} + {:name "id" + :in "path" :description "" - :required true - :schema {:type "integer" - :format "int64"}} - {:name "name" - :in "query" + :required true + :schema {:type "integer" + :format "int64"}} + {:name "name" + :in "query" :description "" - :required true - :schema {:type "string"}} - {:name "street" - :in "query" + :required true + :schema {:type "string"}} + {:name "street" + :in "query" :description "" - :required false - :schema {:type "string"}} - {:name "city" - :in "query" + :required false + :schema {:type "string"}} + {:name "city" + :in "query" :description "" - :required false - :schema {:oneOf [{:enum [:tre :hki], :type "string"} - {:type "null"}]}} - {:name "filters" - :in "query" + :required false + :schema {:oneOf [{:enum [:tre :hki], :type "string"} + {:type "null"}]}} + {:name "filters" + :in "query" :description "" - :required false - :schema {:type "array" - :items {:type "string"}}} - {:name "id" - :in "header" + :required false + :schema {:type "array" + :items {:type "string"}}} + {:name "id" + :in "header" :description "" - :required true - :schema {:type "integer" - :format "int64"}} - {:name "name" - :in "header" + :required true + :schema {:type "integer" + :format "int64"}} + {:name "name" + :in "header" :description "" - :required true - :schema {:type "string"}} - {:name "address" - :in "header" + :required true + :schema {:type "string"}} + {:name "address" + :in "header" :description "" - :required true + :required true :schema - {:type "object" + {:type "object" :properties {"street" {:type "string"} - "city" {:oneOf [{:enum [:tre :hki] :type "string"} - {:type "null"}]}} + "city" {:oneOf [{:enum [:tre :hki] :type "string"} + {:type "null"}]}} :required ["street" "city"] - :title "spec-tools.openapi.core-test/address"}}]} + :title "spec-tools.openapi.core-test/address"}}]} (openapi/openapi-spec {:parameters - [{:name "username" - :in "path" + [{:name "username" + :in "path" :description "username to fetch" - :required true - :schema {:type "string"} - :style "simple"}] + :required true + :schema {:type "string"} + :style "simple"}] ::openapi/parameters - {:path (s/keys :req-un [::id]) - :query (s/keys :req-un [::name] :opt-un [::street ::city ::filters]) + {:path (s/keys :req-un [::id]) + :query (s/keys :req-un [::name] :opt-un [::street ::city ::filters]) :header ::user}}))) (is (= {:parameters - [{:name "name2" - :in "query" + [{:name "name2" + :in "query" :description "Will be the same" - :required true - :schema {:type "string"}} - {:name "spec-tools.openapi.core-test/id" - :in "path" + :required true + :schema {:type "string"}} + {:name "spec-tools.openapi.core-test/id" + :in "path" :description "" - :required true - :schema {:type "integer" :format "int64"}} - {:name "city" - :in "query" + :required true + :schema {:type "integer" :format "int64"}} + {:name "city" + :in "query" :description "" - :required true + :required true :schema {:oneOf [{:enum [:tre :hki] :type "string"} {:type "null"}]}} - {:name "name" - :in "query" + {:name "name" + :in "query" :description "" - :required false - :schema {:type "string"}} - {:name "street" - :in "query" + :required false + :schema {:type "string"}} + {:name "street" + :in "query" :description "" - :required false - :schema {:type "string"}} - {:name "filters" - :in "query" + :required false + :schema {:type "string"}} + {:name "filters" + :in "query" :description "" - :required false - :schema {:type "array" :items {:type "string"}}} - {:name "street" - :in "cookie" + :required false + :schema {:type "array" :items {:type "string"}}} + {:name "street" + :in "cookie" :description "" - :required true - :schema {:type "string"}} - {:name "city" - :in "cookie" + :required true + :schema {:type "string"}} + {:name "city" + :in "cookie" :description "" - :required true + :required true :schema {:oneOf [{:enum [:tre :hki] :type "string"} {:type "null"}]}}]} (openapi/openapi-spec {:parameters - [{:name "name" - :in "query" + [{:name "name" + :in "query" :description "Will be overridden" - :required false - :schema {:type "string"}} - {:name "name2" - :in "query" + :required false + :schema {:type "string"}} + {:name "name2" + :in "query" :description "Will be the same" - :required true - :schema {:type "string"}}] + :required true + :schema {:type "string"}}] ::openapi/parameters - {:path (st/create-spec - {:spec - (s/keys :req [::id])}) - :query (st/create-spec - {:spec - (s/keys :req-un [::city] - :opt-un [::name ::street ::filters])}) + {:path (st/create-spec + {:spec + (s/keys :req [::id])}) + :query (st/create-spec + {:spec + (s/keys :req-un [::city] + :opt-un [::name ::street ::filters])}) :cookie (st/create-spec {:spec ::address})}})))) @@ -317,33 +317,33 @@ "desc" {:type "string"}}} :id {:type "integer" :format "int64"} :user - {:type "object" + {:type "object" :properties - {"id" {:type "integer" :format "int64"}, - "name" {:type "string"} - "address" {:type "object" + {"id" {:type "integer" :format "int64"}, + "name" {:type "string"} + "address" {:type "object" :properties {"street" {:type "string"}, - "city" {:oneOf [{:enum [:tre :hki] :type "string"} - {:type "null"}]}} + "city" {:oneOf [{:enum [:tre :hki] :type "string"} + {:type "null"}]}} :required ["street" "city"] - :title "spec-tools.openapi.core-test/address"}} + :title "spec-tools.openapi.core-test/address"}} :required ["id" "name" "address"] - :title "spec-tools.openapi.core-test/user"} + :title "spec-tools.openapi.core-test/user"} :address - {:type "object" + {:type "object" :properties {"street" {:type "string"} - "city" {:oneOf [{:enum [:tre :hki] :type "string"} - {:type "null"}]}} + "city" {:oneOf [{:enum [:tre :hki] :type "string"} + {:type "null"}]}} :required ["street" "city"] - :title "spec-tools.openapi.core-test/address"} + :title "spec-tools.openapi.core-test/address"} :some-request - {:type "object" + {:type "object" :properties - {"id" {:type "integer" :format "int64"} - "name" {:type "string"} - "street" {:type "string"} + {"id" {:type "integer" :format "int64"} + "name" {:type "string"} + "street" {:type "string"} "filters" {:type "array" :items {:type "string"}}} :required ["id" "name"]}}}} (openapi/openapi-spec @@ -355,12 +355,12 @@ {"name" {:type "string"} "desc" {:type "string"}}} :user - {:type "string" + {:type "string" :title "Will be overridden"}} ::openapi/schemas - {:id ::id - :user ::user - :address ::address + {:id ::id + :user ::user + :address ::address :some-request (s/keys :req-un [::id ::name] :opt-un [::street ::filters])}}})))) @@ -371,38 +371,38 @@ {:type "string"}} "application/json" {:schema - {:type "object" + {:type "object" :properties - {"id" {:type "integer" :format "int64"} + {"id" {:type "integer" :format "int64"} "name" {:type "string"} "address" - {:type "object" + {:type "object" :properties {"street" {:type "string"} "city" {:oneOf [{:enum [:tre :hki] :type "string"} {:type "null"}]}} :required ["street" "city"] - :title "spec-tools.openapi.core-test/address"}} + :title "spec-tools.openapi.core-test/address"}} :required ["id" "name" "address"] - :title "spec-tools.openapi.core-test/user"}} + :title "spec-tools.openapi.core-test/user"}} "application/xml" {:schema - {:type "object" + {:type "object" :properties {"street" {:type "string"} "city" {:oneOf [{:enum [:tre :hki] :type "string"} {:type "null"}]}} :required ["street" "city"] - :title "spec-tools.openapi.core-test/address"}} + :title "spec-tools.openapi.core-test/address"}} "*/*" {:schema - {:type "object" + {:type "object" :properties - {"id" {:type "integer" :format "int64"} - "name" {:type "string"} - "street" {:type "string"} + {"id" {:type "integer" :format "int64"} + "name" {:type "string"} + "street" {:type "string"} "filters" {:type "array" :items {:type "string"}}} :required ["id" "name"]}}}} (openapi/openapi-spec @@ -411,46 +411,46 @@ {:schema {:type "string"}}} ::openapi/content {"application/json" ::user - "application/xml" ::address - "*/*" (s/keys :req-un [::id ::name] - :opt-un [::street ::filters])}}))) + "application/xml" ::address + "*/*" (s/keys :req-un [::id ::name] + :opt-un [::street ::filters])}}))) (is (= {:content {"application/json" {:schema - {:type "object" + {:type "object" :properties - {"id" {:type "integer" :format "int64"} + {"id" {:type "integer" :format "int64"} "name" {:type "string"} "address" - {:type "object" + {:type "object" :properties {"street" {:type "string"} "city" {:oneOf [{:enum [:tre :hki] :type "string"} {:type "null"}]}} :required ["street" "city"] - :title "spec-tools.openapi.core-test/address"}} + :title "spec-tools.openapi.core-test/address"}} :required ["id" "name" "address"] - :title "spec-tools.openapi.core-test/user" - :example "Some examples here" + :title "spec-tools.openapi.core-test/user" + :example "Some examples here" :examples {:admin - {:summary "Admin user" - :description "Super user" - :value {:anything :here} + {:summary "Admin user" + :description "Super user" + :value {:anything :here} :externalValue "External value"}} :encoding {:contentType "application/json"}}}}} (openapi/openapi-spec {::openapi/content {"application/json" (st/spec - {:spec ::user - :openapi/example "Some examples here" + {:spec ::user + :openapi/example "Some examples here" :openapi/examples {:admin - {:summary "Admin user" - :description "Super user" - :value {:anything :here} + {:summary "Admin user" + :description "Super user" + :value {:anything :here} :externalValue "External value"}} :openapi/encoding {:contentType "application/json"}})}})))) @@ -458,44 +458,44 @@ (is (= {:headers {:X-Rate-Limit-Limit {:description "The number of allowed requests in the current period", - :schema {:type "integer"}}, + :schema {:type "integer"}}, :City {:description "", - :required false, + :required false, :schema {:enum [:tre :hki] :type "string"}} :Authorization {:description "" - :required true - :schema {:type "string"}} + :required true + :schema {:type "string"}} :User {:description "" - :required true + :required true :schema - {:type "object" + {:type "object" :properties - {"id" {:type "integer" :format "int64"} + {"id" {:type "integer" :format "int64"} "name" {:type "string"} "address" - {:type "object" + {:type "object" :properties {"street" {:type "string"} "city" {:oneOf [{:enum [:tre :hki] :type "string"} {:type "null"}]}} :required ["street" "city"] - :title "spec-tools.openapi.core-test/address"}} + :title "spec-tools.openapi.core-test/address"}} :required ["id" "name" "address"] - :title "spec-tools.openapi.core-test/user"}}}} + :title "spec-tools.openapi.core-test/user"}}}} (openapi/openapi-spec {:headers {:X-Rate-Limit-Limit {:description "The number of allowed requests in the current period" - :schema {:type "integer"}}} + :schema {:type "integer"}}} ::openapi/headers - {:City ::city + {:City ::city :Authorization ::token - :User ::user}}))))) + :User ::user}}))))) ;; TODO: This test is not really validate schema #?(:clj @@ -505,78 +505,78 @@ (openapi/openapi-spec {:openapi "3.0.3" :info - {:title "Sample Pet Store App" - :description "This is a sample server for a pet store." + {:title "Sample Pet Store App" + :description "This is a sample server for a pet store." :termsOfService "http://example.com/terms/" :contact - {:name "API Support", - :url "http://www.example.com/support" + {:name "API Support", + :url "http://www.example.com/support" :email "support@example.com"} :license {:name "Apache 2.0", - :url "https://www.apache.org/licenses/LICENSE-2.0.html"} - :version "1.0.1"} + :url "https://www.apache.org/licenses/LICENSE-2.0.html"} + :version "1.0.1"} :servers - [{:url "https://development.gigantic-server.com/v1" + [{:url "https://development.gigantic-server.com/v1" :description "Development server"} - {:url "https://staging.gigantic-server.com/v1" + {:url "https://staging.gigantic-server.com/v1" :description "Staging server"} - {:url "https://api.gigantic-server.com/v1" + {:url "https://api.gigantic-server.com/v1" :description "Production server"}] :components - {::openapi/schemas {:user ::user + {::openapi/schemas {:user ::user :address ::address} ::openapi/headers {:token ::token}} :paths {"/api/ping" {:get {:description "Returns all pets from the system that the user has access to" - :responses {200 {::openapi/content - {"application/xml" ::user - "application/json" - (st/spec - {:spec ::address - :openapi/example "Some examples here" - :openapi/examples {:admin - {:summary "Admin user" - :description "Super user" - :value {:anything :here} - :externalValue "External value"}} - :openapi/encoding {:contentType "application/json"}})}}}}} + :responses {200 {::openapi/content + {"application/xml" ::user + "application/json" + (st/spec + {:spec ::address + :openapi/example "Some examples here" + :openapi/examples {:admin + {:summary "Admin user" + :description "Super user" + :value {:anything :here} + :externalValue "External value"}} + :openapi/encoding {:contentType "application/json"}})}}}}} "/user/:id" {:post - {:tags ["user"] - :description "Returns pets based on ID" - :summary "Find pets by ID" - :operationId "getPetsById" - :requestBody {::openapi/content {"application/json" ::user}} - :responses {200 {:description "pet response" - ::openapi/content - {"application/json" ::user}} - :default {:description "error payload", - ::openapi/content - {"text/html" ::user}}} - ::openapi/parameters {:path (s/keys :req-un [::id]) + {:tags ["user"] + :description "Returns pets based on ID" + :summary "Find pets by ID" + :operationId "getPetsById" + :requestBody {::openapi/content {"application/json" ::user}} + :responses {200 {:description "pet response" + ::openapi/content + {"application/json" ::user}} + :default {:description "error payload", + ::openapi/content + {"text/html" ::user}}} + ::openapi/parameters {:path (s/keys :req-un [::id]) :header (s/keys :req-un [::token])}}}}})))))) (deftest backport-openapi-meta-unnamespaced (is (= (openapi/transform (st/spec - {:spec string? - :openapi {:type "string" - :format "password" + {:spec string? + :openapi {:type "string" + :format "password" :random-value "42"}})) {:type "string" :format "password" :random-value "42"})) (is (= (openapi/transform (st/spec - {:spec string? - :openapi {:type "object"} + {:spec string? + :openapi {:type "object"} :openapi/format "password"})) {:type "object"})) (is (= (openapi/transform (st/spec - {:spec string? - :openapi/type "string" - :openapi/format "password" + {:spec string? + :openapi/type "string" + :openapi/format "password" :openapi/random-value "42"})) {:type "string" :format "password" :random-value "42"}))) diff --git a/test/cljc/spec_tools/parse_test.cljc b/test/cljc/spec_tools/parse_test.cljc index 2b30627e..0ad94585 100644 --- a/test/cljc/spec_tools/parse_test.cljc +++ b/test/cljc/spec_tools/parse_test.cljc @@ -110,8 +110,8 @@ :type :map-of} (parse/parse-spec (s/coll-of (s/tuple int? keyword?) :into {}))))) (testing "s/multi-spec" - (is (= {:type :multi-spec - ::parse/key ::h + (is (= {:type :multi-spec + ::parse/key ::h ::parse/dispatch {:type-a ::ha :type-b ::hb}} (parse/parse-spec ::multi)))) @@ -126,13 +126,13 @@ (testing "get-keys" (is (= #{:a :b :c :d :e :f :g ::h ::i} (parse/get-keys - (parse/parse-spec - (s/or - :1 (s/or :1 (s/keys :opt-un [::a]) - :2 (s/and - (s/keys :opt-un [::b]) - (s/keys :opt-un [::c]) - (s/merge - (s/keys :opt-un [::d]) - (s/keys :opt-un [::e])))) - :2 (s/keys :opt-un [::f] :req-un [::g] :req [::h] :opt [::i])))))))) + (parse/parse-spec + (s/or + :1 (s/or :1 (s/keys :opt-un [::a]) + :2 (s/and + (s/keys :opt-un [::b]) + (s/keys :opt-un [::c]) + (s/merge + (s/keys :opt-un [::d]) + (s/keys :opt-un [::e])))) + :2 (s/keys :opt-un [::f] :req-un [::g] :req [::h] :opt [::i])))))))) diff --git a/test/cljc/spec_tools/spell_spec/alpha_test.cljc b/test/cljc/spec_tools/spell_spec/alpha_test.cljc index 7774795b..4c896746 100644 --- a/test/cljc/spec_tools/spell_spec/alpha_test.cljc +++ b/test/cljc/spec_tools/spell_spec/alpha_test.cljc @@ -11,9 +11,9 @@ [spec-tools.spell-spec.alpha :as spell :refer [warn-keys strict-keys warn-strict-keys]])) (defn fetch-warning-output [thunk] - #?(:clj (binding [*err* (java.io.StringWriter.)] - (thunk) - (str *err*)) + #?(:clj (binding [*err* (java.io.StringWriter.)] + (thunk) + (str *err*)) :cljs (with-out-str (thunk)))) (deftest check-misspell-test @@ -99,7 +99,7 @@ (when-let [{:keys [:expound.spec.problem/type ::spell/misspelled-key ::spell/likely-misspelling-of]} (first problems)] (is (= ::spell/misspelled-key type)) (is (= misspelled-key :helloo)) - (is (= '(:hello) likely-misspelling-of))) + (is (= '(:hello) likely-misspelling-of))) (when-let [{:keys [:expound.spec.problem/type ::spell/unknown-key]} (second problems)] (is (= ::spell/unknown-key type)) (is (= unknown-key :barabara)))))) diff --git a/test/cljc/spec_tools/spell_spec/expound_test.cljc b/test/cljc/spec_tools/spell_spec/expound_test.cljc index 5a0faaee..69111fe8 100644 --- a/test/cljc/spec_tools/spell_spec/expound_test.cljc +++ b/test/cljc/spec_tools/spell_spec/expound_test.cljc @@ -7,7 +7,6 @@ [clojure.string :as string] [spec-tools.spell-spec.alpha :as spell :refer [warn-keys strict-keys warn-strict-keys]] [expound.alpha :as exp] - [expound.ansi :as ansi] [spec-tools.spell-spec.expound])) (defn fetch-warning-output [thunk] @@ -94,6 +93,7 @@ ) ;; checking color +#_(require '[expound.ansi :as ansi]) #_(ansi/with-color (exp/expound (s/map-of keyword? any?) {"hello" 1 :there 1})) diff --git a/test/cljc/spec_tools/spell_test.cljc b/test/cljc/spec_tools/spell_test.cljc index 3031c60f..00533252 100644 --- a/test/cljc/spec_tools/spell_test.cljc +++ b/test/cljc/spec_tools/spell_test.cljc @@ -18,48 +18,48 @@ (testing "explain-data" (testing "simple specs" (is (not (s/explain-data - (s/keys :opt-un [::config]) - invalid-options))) + (s/keys :opt-un [::config]) + invalid-options))) (is (s/explain-data - (spell/closed (s/keys :opt-un [::config])) - invalid-options)) + (spell/closed (s/keys :opt-un [::config])) + invalid-options)) (is (s/explain-data - (spell/closed (s/keys :opt-un [::config])) - invalid-config)) + (spell/closed (s/keys :opt-un [::config])) + invalid-config)) (is (not (s/explain-data - (spell/closed (s/keys :opt-un [::config])) - valid)))) + (spell/closed (s/keys :opt-un [::config])) + valid)))) (testing "composite specs" (is (not (s/explain-data - (spell/closed - (s/merge - (s/keys :opt-un [::config]) - (s/keys :opt-un [::name]))) - valid))) + (spell/closed + (s/merge + (s/keys :opt-un [::config]) + (s/keys :opt-un [::name]))) + valid))) (is (s/explain-data - (spell/closed - (s/merge - (s/keys :opt-un [::config]) - (s/keys :opt-un [::name]))) - {:namez "kikka"}))) + (spell/closed + (s/merge + (s/keys :opt-un [::config]) + (s/keys :opt-un [::name]))) + {:namez "kikka"}))) (testing "explain-str" (is (str/includes? - (spell/explain-str - (spell/closed (s/keys :opt-un [::config])) - invalid-options) - "Misspelled map key"))) + (spell/explain-str + (spell/closed (s/keys :opt-un [::config])) + invalid-options) + "Misspelled map key"))) (testing "explain" (is (str/includes? - (with-out-str - (spell/explain - (spell/closed (s/keys :opt-un [::config])) - invalid-options)) - "Misspelled map key"))) + (with-out-str + (spell/explain + (spell/closed (s/keys :opt-un [::config])) + invalid-options)) + "Misspelled map key"))) (testing "errors" (is (thrown-with-msg? - #?(:clj Exception, :cljs js/Error) - #"Can't read keys from spec" - (spell/closed int?)))) + #?(:clj Exception, :cljs js/Error) + #"Can't read keys from spec" + (spell/closed int?)))) (testing "parsing" (is (= {:type :map, :spec-tools.parse/key->spec {:config ::config}, @@ -71,15 +71,15 @@ :spec-tools.parse/keys #{:config :name}, :spec-tools.parse/keys-opt #{:config :name}} (parse/parse-spec - (spell/closed - (s/merge - (s/keys :opt-un [::config]) - (s/keys :opt-un [::name]))))))) + (spell/closed + (s/merge + (s/keys :opt-un [::config]) + (s/keys :opt-un [::name]))))))) (testing "data-specs" (let [spec (ds/spec - {:spec {:config {:name string? :use-history boolean?}} - :name ::spec - :keys-spec spell/closed-keys})] + {:spec {:config {:name string? :use-history boolean?}} + :name ::spec + :keys-spec spell/closed-keys})] (is (s/explain-data spec invalid-options)) (is (s/explain-data spec invalid-config)) (is (not (s/explain-data spec valid))))))) diff --git a/test/cljc/spec_tools/swagger/core_test.cljc b/test/cljc/spec_tools/swagger/core_test.cljc index 11b5973b..f78f53d2 100644 --- a/test/cljc/spec_tools/swagger/core_test.cljc +++ b/test/cljc/spec_tools/swagger/core_test.cljc @@ -1,22 +1,22 @@ (ns spec-tools.swagger.core-test (:require - [clojure.test :refer [deftest testing is are]] - [spec-tools.swagger.core :as swagger] - [clojure.spec.alpha :as s] - [spec-tools.spec :as spec] - #?(:clj [ring.swagger.validator :as v]) - [spec-tools.core :as st])) + [clojure.test :refer [deftest testing is are]] + [spec-tools.swagger.core :as swagger] + [clojure.spec.alpha :as s] + [spec-tools.spec :as spec] + #?(:clj [ring.swagger.validator :as v]) + [spec-tools.core :as st])) (s/def ::integer integer?) (s/def ::string string?) (s/def ::set #{1 2 3}) (s/def ::keys (s/keys :req-un [::integer])) (s/def ::spec (st/spec - {:spec string? - :description "description" - :json-schema/default "123" - :json-schema/example "json-schema-example" - :swagger/example "swagger-example"})) + {:spec string? + :description "description" + :json-schema/default "123" + :json-schema/example "json-schema-example" + :swagger/example "swagger-example"})) (s/def ::keys2 (s/keys :req-un [::integer ::spec])) (def exceptations @@ -258,19 +258,19 @@ :x-nullable true}}, :required ["street" "city"]}}]} (swagger/swagger-spec - {:parameters [{:in "query" - :name "name" - :description "this will be overridden" - :required false} - {:in "query" - :name "name2" - :description "this survives the merge" - :type "string" - :required true}] - ::swagger/parameters - {:query (s/keys :opt-un [::name ::street ::city]) - :path (s/keys :req [::id]) - :body ::address}}))) + {:parameters [{:in "query" + :name "name" + :description "this will be overridden" + :required false} + {:in "query" + :name "name2" + :description "this survives the merge" + :type "string" + :required true}] + ::swagger/parameters + {:query (s/keys :opt-un [::name ::street ::city]) + :path (s/keys :req [::id]) + :body ::address}}))) (is (= {:parameters [{:in "query" :name "name2" :description "this survives the merge" @@ -310,19 +310,19 @@ :x-nullable true}}, :required ["street" "city"]}}]} (swagger/swagger-spec - {:parameters [{:in "query" - :name "name" - :description "this will be overridden" - :required false} - {:in "query" - :name "name2" - :description "this survives the merge" - :type "string" - :required true}] - ::swagger/parameters - {:query (st/create-spec {:spec (s/keys :opt-un [::name ::street ::city])}) - :path (st/create-spec {:spec (s/keys :req [::id])}) - :body (st/create-spec {:spec ::address})}})))) + {:parameters [{:in "query" + :name "name" + :description "this will be overridden" + :required false} + {:in "query" + :name "name2" + :description "this survives the merge" + :type "string" + :required true}] + ::swagger/parameters + {:query (st/create-spec {:spec (s/keys :opt-un [::name ::street ::city])}) + :path (st/create-spec {:spec (s/keys :req [::id])}) + :body (st/create-spec {:spec ::address})}})))) (testing "::responses" (is (= {:responses @@ -344,10 +344,10 @@ 404 {:description "Ohnoes."} 500 {:description "fail"}}} (swagger/swagger-spec - {:responses {404 {:description "fail"} - 500 {:description "fail"}} - ::swagger/responses {200 {:schema ::user} - 404 {:description "Ohnoes."}}}))))) + {:responses {404 {:description "fail"} + 500 {:description "fail"}} + ::swagger/responses {200 {:schema ::user} + 404 {:description "Ohnoes."}}}))))) #?(:clj (deftest test-schema-validation @@ -376,21 +376,21 @@ (deftest backport-swagger-meta-unnamespaced (is (= (swagger/transform - (st/spec {:spec string? - :swagger {:type "string" - :format "password" - :random-value "42"}})) + (st/spec {:spec string? + :swagger {:type "string" + :format "password" + :random-value "42"}})) {:type "string" :format "password" :random-value "42"})) (is (= (swagger/transform - (st/spec {:spec string? - :swagger {:type "object"} - :swagger/format "password"})) + (st/spec {:spec string? + :swagger {:type "object"} + :swagger/format "password"})) {:type "object"})) (is (= (swagger/transform - (st/spec {:spec string? - :swagger/type "string" - :swagger/format "password" - :swagger/random-value "42"})) + (st/spec {:spec string? + :swagger/type "string" + :swagger/format "password" + :swagger/random-value "42"})) {:type "string" :format "password" :random-value "42"}))) diff --git a/test/cljc/spec_tools/transform_test.cljc b/test/cljc/spec_tools/transform_test.cljc index 2e8aee96..80b98e96 100644 --- a/test/cljc/spec_tools/transform_test.cljc +++ b/test/cljc/spec_tools/transform_test.cljc @@ -75,7 +75,7 @@ (deftest number->string (is (string? (stt/number->string _ 42.42))) (is (string? (stt/number->string _ 42))) - (is (string? #?(:clj (stt/number->string _ (BigDecimal. 23123)) + (is (string? #?(:clj (stt/number->string _ (BigDecimal. 23123)) :cljs (stt/number->string _ js/Math.PI))))) #?(:clj (deftest number->decimal diff --git a/test/cljc/spec_tools/visitor_all_test.cljc b/test/cljc/spec_tools/visitor_all_test.cljc index a32e5be5..28b7908e 100644 --- a/test/cljc/spec_tools/visitor_all_test.cljc +++ b/test/cljc/spec_tools/visitor_all_test.cljc @@ -16,10 +16,10 @@ (s/def ::p5 string?) (s/def ::p6 string?) (s/def ::keys (s/keys - :req [(or ::p1 (and ::p2 ::p3))] - :opt [::p4] - :req-un [::p5] - :opt-un [::p6])) + :req [(or ::p1 (and ::p2 ::p3))] + :opt [::p4] + :req-un [::p5] + :opt-un [::p6])) (s/def ::p7 string?) (s/def ::p8 string?) @@ -81,10 +81,10 @@ (s/def ::p32 string?) (s/def ::p33 string?) (s/def ::keys* (s/keys* - :req [(or ::p28 (and ::p29 ::p30))] - :opt [::p31] - :req-un [::p32] - :opt-un [::p33])) + :req [(or ::p28 (and ::p29 ::p30))] + :opt [::p31] + :req-un [::p32] + :opt-un [::p33])) (s/def ::p34 string?) (s/def ::nilable (s/nilable ::p34)) @@ -95,24 +95,24 @@ (s/def ::all (s/tuple - ::pred - ::keys - ::or - ::and - ::merge - ::every - ::every-kv - ::coll-of - ::map-of - ::* - ::+ - ::? - ::alt - ::cat - ::& - ::tuple - ::keys* - ::nilable)) + ::pred + ::keys + ::or + ::and + ::merge + ::every + ::every-kv + ::coll-of + ::map-of + ::* + ::+ + ::? + ::alt + ::cat + ::& + ::tuple + ::keys* + ::nilable)) ;; ;; tests @@ -121,8 +121,8 @@ (deftest nested-regexp-test (is (= #{::p1 ::p2 ::p3} (->> (visitor/visit - (s/* (s/cat :prop ::p1 :val (s/alt :s ::p2 :b ::p3))) - (visitor/spec-collector)) + (s/* (s/cat :prop ::p1 :val (s/alt :s ::p2 :b ::p3))) + (visitor/spec-collector)) (keys) (set))))) diff --git a/test/cljc/spec_tools/visitor_test.cljc b/test/cljc/spec_tools/visitor_test.cljc index d0a1358f..eacc0076 100644 --- a/test/cljc/spec_tools/visitor_test.cljc +++ b/test/cljc/spec_tools/visitor_test.cljc @@ -40,14 +40,14 @@ (def person-spec (ds/spec - ::person - {::id integer? - :age ::age - :name string? - :likes {string? boolean?} - (ds/req :languages) #{keyword?} - (ds/opt :address) {:street string? - :zip string?}})) + ::person + {::id integer? + :age ::age + :name string? + :likes {string? boolean?} + (ds/req :languages) #{keyword?} + (ds/opt :address) {:street string? + :zip string?}})) (deftest readme-visitor-test (let [expected #{:spec-tools.visitor-test/id @@ -66,10 +66,10 @@ (-> specs vals set)))) (comment - (testing "convert-specs! transforms all specs into Spec records" - (visitor/convert-specs! person-spec) - (is (true? - (->> expected - (map s/get-spec) - (remove keyword?) - (every? st/spec?)))))))) + (testing "convert-specs! transforms all specs into Spec records" + (visitor/convert-specs! person-spec) + (is (true? + (->> expected + (map s/get-spec) + (remove keyword?) + (every? st/spec?)))))))) From 24e77507811108eb816f80e74ec0d688313361ac Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 29 Jun 2024 17:03:37 +0300 Subject: [PATCH 5/8] update deps --- CHANGELOG.md | 6 ++++++ project.clj | 16 ++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4c50bc1..9c87130b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Unreleased * Support transforming schema to JSON Schema. PR [#281](https://github.com/metosin/spec-tools/pull/281) +* FIX :reason doesn't compose as expected [#171](https://github.com/metosin/spec-tools/issues/171) +* Update Dependencies + +```clojure +[org.clojure/spec.alpha "0.5.238"] is available but we use "0.3.218" +``` # 0.10.6 (2023-08-28) diff --git a/project.clj b/project.clj index 76f9dd04..3d4ef4ab 100644 --- a/project.clj +++ b/project.clj @@ -16,9 +16,9 @@ :scm {:name "git" :url "https://github.com/metosin/spec-tools"} - :dependencies [[org.clojure/spec.alpha "0.3.218"]] + :dependencies [[org.clojure/spec.alpha "0.5.238"]] - :profiles {:dev {:plugins [[jonase/eastwood "1.4.0"] + :profiles {:dev {:plugins [[jonase/eastwood "1.4.2"] [lein-tach "1.1.0"] [lein-doo "0.1.11"] [lein-cljsbuild "1.1.8"] @@ -27,21 +27,21 @@ [lein-pprint "1.3.2"]] :jvm-opts ^:replace ["-server"] ;:global-vars {*warn-on-reflection* true} - :dependencies [[org.clojure/clojure "1.11.1"] - [org.clojure/clojurescript "1.11.60"] + :dependencies [[org.clojure/clojure "1.11.3"] + [org.clojure/clojurescript "1.11.132"] [criterium "0.4.6"] [prismatic/schema "1.4.1"] [org.clojure/test.check "1.1.1"] - [org.clojure/tools.namespace "1.4.4"] + [org.clojure/tools.namespace "1.5.0"] [com.gfredericks/test.chuck "0.2.14"] ; com.bhauman/spell-spec library doesn't get any updates, so it has to be copied here ; under spec-tools.spell-spec namespace in order to fix its bugs. ; If the library gets updated with fixes it would be desirable to switch back to it. ;[com.bhauman/spell-spec "0.1.1"] [expound "0.9.0"] - [metosin/muuntaja "0.6.8"] - [metosin/ring-swagger "0.26.2"] - [metosin/jsonista "0.3.7"] + [metosin/muuntaja "0.6.10"] + [metosin/ring-swagger "1.0.0"] + [metosin/jsonista "0.3.8"] [metosin/scjsv "0.6.2"]]} :perf {:jvm-opts ^:replace ["-server"]}} :aliases {"all" ["with-profile" "dev"] From e6248b15d94b843cff3ea0e3fe9a1d7d567fa55c Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 29 Jun 2024 17:16:55 +0300 Subject: [PATCH 6/8] . --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index aae9b8cd..83fa8d83 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ /target pom.xml pom.xml.asc -.cache \ No newline at end of file +.cache +node_modules \ No newline at end of file From d5e8159c18bfbeb97cac22f1fdf0583579fdbb8b Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 29 Jun 2024 18:06:40 +0300 Subject: [PATCH 7/8] fix cljs-tests --- test/cljc/spec_tools/json_schema_test.cljc | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/test/cljc/spec_tools/json_schema_test.cljc b/test/cljc/spec_tools/json_schema_test.cljc index d6a76c0f..27bd5197 100644 --- a/test/cljc/spec_tools/json_schema_test.cljc +++ b/test/cljc/spec_tools/json_schema_test.cljc @@ -44,6 +44,11 @@ (s/keys :req-un [:event.payload.result/action] :opt-un [:event.payload.result/payload])) +(s/def ::multi (s/multi-spec event-payload :action)) + +(defn =any-of [& bodies] + (->> bodies (map :anyOf) (map set) (apply =))) + (deftest simple-spec-test (testing "primitive predicates" ;; You're intented to call jsc/to-json with a registered spec, but to avoid @@ -116,11 +121,13 @@ :properties {"spec-tools.json-schema-test/integer" {:type "integer"} "spec-tools.json-schema-test/string" {:type "string"}} :required ["spec-tools.json-schema-test/integer" "spec-tools.json-schema-test/string"]})) - (is (= (jsc/transform (s/multi-spec event-payload :action)) - {:anyOf [{:type "object" :properties {"action" {:enum [:result]} "payload" {:type "null"}} :required ["action"]} - {:type "object" - :properties {"action" {:enum [:add]} "payload" {:type "integer" :format "int64"}} - :required ["action" "payload"]}]})) + (is (=any-of (jsc/transform ::multi) + {:anyOf [{:type "object" + :properties {"action" {:enum [:result]} "payload" {:type "null"}} + :required ["action"]} + {:type "object" + :properties {"action" {:enum [:add]} "payload" {:type "integer" :format "int64"}} + :required ["action" "payload"]}]})) (is (= (jsc/transform (s/every integer?)) {:type "array" :items {:type "integer"}})) (is (= (jsc/transform (s/every-kv string? integer?)) {:type "object" :additionalProperties {:type "integer"}})) From 70f69fc6b5215b5c02e1b47ab054f70fdf31971d Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 29 Jun 2024 18:09:12 +0300 Subject: [PATCH 8/8] CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c87130b..526984bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Unreleased -* Support transforming schema to JSON Schema. PR [#281](https://github.com/metosin/spec-tools/pull/281) +* Support transforming multi-spec to JSON Schema. PR [#281](https://github.com/metosin/spec-tools/pull/281) * FIX :reason doesn't compose as expected [#171](https://github.com/metosin/spec-tools/issues/171) * Update Dependencies