Skip to content

Commit

Permalink
Merge branch 'master' of github.com:wandersoncferreira/spec-tools int…
Browse files Browse the repository at this point in the history
…o bugfix/or-spec-coercion
  • Loading branch information
wandersoncferreira committed Apr 4, 2020
2 parents 4f452db + b02175b commit c627300
Show file tree
Hide file tree
Showing 16 changed files with 857 additions and 61 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ Status: **Alpha** (as spec is still alpha too).
Copyright © 2016-2019 [Metosin Oy](http://www.metosin.fi)

Distributed under the Eclipse Public License, the same as Clojure.

The spell-spec library, Copyright © 2018 Bruce Hauman, is distributed under the Eclipse Public License as well.
19 changes: 19 additions & 0 deletions docs/04_json_schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@ Utility to transform Specs into JSON Schemas. The generated schema conforms to [
; :title "user/user"}
```

You also has an option to disable the title inference from specs that
does not have an explicit `:name` attribute assigned to them, see more
details below. Therefore, the current example would become:

```clj
(json-schema/transform ::user {:infer-titles false})
;{:type "object"
; :properties {"id" {:type "string"}
; "name" {:type "string"}
; "address" {:type "object"
; :properties {"street" {:type "string"}
; "city" {:enum [:tre :hki]}}
; :required ["street" "city"]
; }}
; :required ["id" "name" "address"]}
```



## Annotated Specs

Specs can be annotated to populate the JSON Schemas.
Expand Down
5 changes: 4 additions & 1 deletion project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@
[org.clojure/test.check "0.10.0"]
[org.clojure/tools.namespace "0.3.1"]
[com.gfredericks/test.chuck "0.2.10"]
[com.bhauman/spell-spec "0.1.1"]
; 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.8.4"]
[metosin/ring-swagger "0.26.2"]
[metosin/muuntaja "0.6.4"]
Expand Down
16 changes: 12 additions & 4 deletions src/spec_tools/impl.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,18 @@
res (if cljs? (partial cljs-resolve env) clojure.core/resolve)]
(->> pred
(walk/postwalk
(fn [x]
(if (symbol? x)
(or (some->> x res ->sym) 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]
Expand Down
85 changes: 48 additions & 37 deletions src/spec_tools/json_schema.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,13 @@
(defmethod accept-spec ::visitor/set [dispatch spec children _]
{:enum children})

(defn- maybe-with-title [schema spec]
(if-let [title (st/spec-name spec)]
(assoc schema :title (impl/qualified-name title))
schema))
(defn- maybe-with-title [schema spec options]
(letfn [(infer-titles? [options] (-> options :infer-titles false? not))]
(if-let [title (and (infer-titles? options) (st/spec-name spec))]
(assoc schema :title (impl/qualified-name title))
schema)))

(defmethod accept-spec 'clojure.spec.alpha/keys [_ spec children _]
(defmethod accept-spec 'clojure.spec.alpha/keys [_ spec children options]
(let [{:keys [req req-un opt opt-un]} (impl/parse-keys (impl/extract-form spec))
names-un (map name (concat req-un opt-un))
names (map impl/qualified-name (concat req opt))
Expand All @@ -196,46 +197,50 @@
:properties (zipmap (concat names names-un) children)}
(when all-required
{:required (vec all-required)}))
spec)))
spec
options)))

(defmethod accept-spec 'clojure.spec.alpha/or [_ _ children _]
{:anyOf children})

(defmethod accept-spec 'clojure.spec.alpha/and [_ _ children _]
(simplify-all-of {:allOf children}))

(defn- accept-merge [children]
{: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 []))})

(defmethod accept-spec 'clojure.spec.alpha/merge [_ _ children _]
(accept-merge children))

(defmethod accept-spec 'spec-tools.core/merge [_ _ children _]
(accept-merge children))

(defmethod accept-spec 'clojure.spec.alpha/every [_ spec children _]
(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))

(defmethod accept-spec 'clojure.spec.alpha/merge [_ spec children options]
(accept-merge children spec options))

(defmethod accept-spec 'spec-tools.core/merge [_ spec children options]
(accept-merge children spec options))

(defmethod accept-spec 'clojure.spec.alpha/every [_ spec children options]
(let [form (impl/extract-form spec)
{:keys [type]} (parse/parse-spec form)]
(case type
:map (maybe-with-title {:type "object", :additionalProperties (impl/unwrap children)} spec)
:map (maybe-with-title {:type "object", :additionalProperties (impl/unwrap children)} spec options)
:set {:type "array", :uniqueItems true, :items (impl/unwrap children)}
:vector {:type "array", :items (impl/unwrap children)})))

(defmethod accept-spec 'clojure.spec.alpha/every-kv [_ spec children _]
(maybe-with-title {:type "object", :additionalProperties (second children)} spec))
(defmethod accept-spec 'clojure.spec.alpha/every-kv [_ spec children options]
(maybe-with-title {:type "object", :additionalProperties (second children)} spec options))

(defmethod accept-spec ::visitor/map-of [_ spec children _]
(maybe-with-title {:type "object", :additionalProperties (second children)} spec))
(defmethod accept-spec ::visitor/map-of [_ spec children options]
(maybe-with-title {:type "object", :additionalProperties (second children)} spec options))

(defmethod accept-spec ::visitor/set-of [_ _ children _]
{:type "array", :items (impl/unwrap children), :uniqueItems true})
Expand All @@ -252,12 +257,18 @@
(defmethod accept-spec 'clojure.spec.alpha/? [_ _ children _]
{:type "array" :items (impl/unwrap children) :minItems 0})

(defmethod accept-spec 'clojure.spec.alpha/alt [_ _ children _]
{:anyOf children})

(defmethod accept-spec 'clojure.spec.alpha/cat [_ _ children _]
{:type "array"
:items {:anyOf children}})
(defmethod accept-spec 'clojure.spec.alpha/alt [_ spec children options]
(maybe-with-title
{:anyOf children}
spec
options))

(defmethod accept-spec 'clojure.spec.alpha/cat [_ spec children options]
(maybe-with-title
{:type "array"
:items {:anyOf children}}
spec
options))

; &

Expand Down
27 changes: 19 additions & 8 deletions src/spec_tools/parse.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

;; a predicate
(ifn? x)
(parse-form (form/resolve-form x) nil)
(parse-form (impl/normalize-symbol (form/resolve-form x)) nil)

;; default
:else (parse-form x nil)))
Expand Down Expand Up @@ -158,13 +158,24 @@
deref
methods
(map (fn [[spec-k method]]
[spec-k (method nil)]))))))

#?(:clj
(defmethod parse-form 'clojure.spec.alpha/multi-spec [_ form]
{:type :multi-spec
::key (last form)
::dispatch (into {} (get-multi-spec-sub-specs form))}))
[spec-k (method nil)])))))
:cljs
(defn get-multi-spec-sub-specs
"Given a multi-spec form, call its multi method methods to retrieve
its subspecs."
[multi-spec-form]
(let [[_ multi-method-symbol & _ :as form] multi-spec-form]
(when-let [spec (first (filter (fn [v] (= form (s/form v))) (vals (s/registry))))]
(->> (.-mmvar spec)
deref
methods
(map (fn [[spec-k method]]
[spec-k (method nil)])))))))

(defmethod parse-form 'clojure.spec.alpha/multi-spec [_ form]
{:type :multi-spec
::key (last form)
::dispatch (into {} (get-multi-spec-sub-specs form))})

(defmethod parse-form 'clojure.spec.alpha/or [_ form]
(let [specs (mapv (comp parse-spec-with-spec-ref second) (partition 2 (rest form)))]
Expand Down
4 changes: 1 addition & 3 deletions src/spec_tools/spell.cljc
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
(ns spec-tools.spell
(:require [spell-spec.alpha :as ssa]
[spell-spec.expound]
(:require [spec-tools.spell-spec.alpha :as ssa]
[spec-tools.parse :as parse]
[clojure.spec.alpha :as s]
[spec-tools.core :as st]
[spec-tools.impl :as impl]
[spell-spec.expound]
[expound.alpha :as expound]
[expound.ansi :as ansi]))

Expand Down
Loading

0 comments on commit c627300

Please sign in to comment.