Skip to content

Commit 6a73fce

Browse files
author
ModestasPeciokas
committed
Add parameter to support custom format validators
1 parent 8a07329 commit 6a73fce

File tree

2 files changed

+95
-15
lines changed

2 files changed

+95
-15
lines changed

src/json_schema/core.clj

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
[org.everit.json.schema.loader
66
SchemaLoader SchemaClient SchemaLoader$SchemaLoaderBuilder]
77
[org.everit.json.schema Schema]
8-
[org.everit.json.schema ValidationException]))
8+
[org.everit.json.schema ValidationException]
9+
[org.everit.json.schema FormatValidator]
10+
[java.util Optional]))
911

1012
(defn- ^JSONTokener prepare-tokener
1113
"Prepares a JSONTokener instance for further processing"
@@ -37,10 +39,26 @@
3739
(when-not is-input-valid?
3840
(throw (ex-info "Unsupported Schema input" {:input input})))))
3941

42+
(defn- format-validator [key validation-fn]
43+
(reify FormatValidator
44+
(formatName [_] key)
45+
(validate [_ value]
46+
(if-let [validation-error-message (validation-fn value)]
47+
(Optional/of validation-error-message)
48+
(Optional/empty)))))
49+
4050
(defn ^JSONObject prepare-schema*
4151
"Prepares JSON Schema based on input string or map. An optional parameter map
4252
can be supplied to refer to relative file schemas via classpath in $ref fields.
4353
54+
Setting :format-validators to map of format name and validation function adds
55+
them as custom format validators.
56+
Validator takes value and returns validation error message or nil if success
57+
(prepare-schema* input {
58+
:format-validators {\"uuid\" (fn [value]
59+
(when-not (uuid? (parse-uuid value))
60+
(format \"[%s] is not a valid UUID value\" value)))}})
61+
4462
Setting :classpath-aware? to true enables absolute classpath resolution.
4563
(prepare-schema* input {:classpath-aware? true})
4664
@@ -54,20 +72,27 @@
5472
(SchemaLoader/load (JSONObject. (prepare-tokener input))))
5573
([input params]
5674
(assert-schema-input-valid! input)
57-
(if-not (:classpath-aware? params)
58-
(prepare-schema* input)
59-
(let [resolution-scope (:default-resolution-scope params)
60-
set-resolution-scope (fn [^SchemaLoader$SchemaLoaderBuilder builder]
61-
(if resolution-scope
62-
(.resolutionScope builder ^String resolution-scope)
63-
builder))
64-
schema-loader (-> (SchemaLoader/builder)
65-
(.schemaClient (SchemaClient/classPathAwareClient))
66-
^SchemaLoader$SchemaLoaderBuilder (set-resolution-scope)
67-
(.schemaJson
68-
^JSONObject (JSONObject. (prepare-tokener input)))
69-
(.build))]
70-
(.build (.load schema-loader))))))
75+
(let [set-format-validators (fn [^SchemaLoader$SchemaLoaderBuilder builder]
76+
(doseq [[k validation-fn] (:format-validators params)]
77+
(.addFormatValidator builder (format-validator k validation-fn)))
78+
builder)]
79+
(if-not (:classpath-aware? params)
80+
(.build (.load (-> (SchemaLoader/builder)
81+
^SchemaLoader$SchemaLoaderBuilder (set-format-validators)
82+
(.schemaJson ^JSONObject (JSONObject. (prepare-tokener input)))
83+
(.build))))
84+
(let [resolution-scope (:default-resolution-scope params)
85+
set-resolution-scope (fn [^SchemaLoader$SchemaLoaderBuilder builder]
86+
(if resolution-scope
87+
(.resolutionScope builder ^String resolution-scope)
88+
builder))
89+
schema-loader (-> (SchemaLoader/builder)
90+
(.schemaClient (SchemaClient/classPathAwareClient))
91+
^SchemaLoader$SchemaLoaderBuilder (set-resolution-scope)
92+
^SchemaLoader$SchemaLoaderBuilder (set-format-validators)
93+
(.schemaJson ^JSONObject (JSONObject. (prepare-tokener input)))
94+
(.build))]
95+
(.build (.load schema-loader)))))))
7196

7297
(def ^JSONObject prepare-schema (memoize prepare-schema*))
7398

test/json_schema/core_test.clj

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,58 @@
172172

173173
(testing "Valid input as EDN list"
174174
(is (= '("a" "b") (json/validate schema '("a" "b"))))))))
175+
176+
(deftest validate-uuid
177+
(testing "Validate EDN where data is uuid"
178+
(let [uuid-validator (fn [value]
179+
(when-not (uuid? (parse-uuid value))
180+
(format "[%s] is not a valid UUID value" value)))
181+
json-str-valid "{\"id\" : \"7c0fcade-fdcc-4d48-86a7-da55ebb82f04\"}"
182+
json-str-invalid "{\"id\" : \"Tc0fcade-fdcc-4d48-86a7-da55ebb82f04\"}"
183+
json-str-empty "{\"id\" : \"\"}"]
184+
185+
(testing "Class path unaware"
186+
(let [schema (json/prepare-schema {:$schema "http://json-schema.org/draft-04/schema"
187+
:id "https://luposlip.com/some-other-schema.json"
188+
:type "object"
189+
:properties {:id {:type "string"
190+
:format "uuid"}}}
191+
{:format-validators {"uuid" uuid-validator}})]
192+
(testing "Valid uuid"
193+
(is (= json-str-valid (json/validate schema json-str-valid))))
194+
195+
(testing "Invalid uuid"
196+
(is (thrown-with-msg?
197+
Exception
198+
#"JSON Validation error"
199+
(json/validate schema json-str-invalid))))
200+
201+
(testing "Empty instead of uuid"
202+
(is (thrown-with-msg?
203+
Exception
204+
#"JSON Validation error"
205+
(json/validate schema json-str-empty))))))
206+
207+
(testing "Class path aware"
208+
(let [schema (json/prepare-schema {:$schema "http://json-schema.org/draft-04/schema"
209+
:id "https://luposlip.com/some-other-schema.json"
210+
:type "object"
211+
:properties {:id {:type "string"
212+
:format "uuid"}}}
213+
{:classpath-aware? true
214+
:default-resolution-scope "classpath://ref/relative/"
215+
:format-validators {"uuid" uuid-validator}})]
216+
(testing "Valid uuid"
217+
(is (= json-str-valid (json/validate schema json-str-valid))))
218+
219+
(testing "Invalid uuid"
220+
(is (thrown-with-msg?
221+
Exception
222+
#"JSON Validation error"
223+
(json/validate schema json-str-invalid))))
224+
225+
(testing "Empty instead of uuid"
226+
(is (thrown-with-msg?
227+
Exception
228+
#"JSON Validation error"
229+
(json/validate schema json-str-empty)))))))))

0 commit comments

Comments
 (0)