git.iko.soy/typeable/hjsonschema
on ce5ebb7 help sign in
.travis.yml+56−67
since splitsnapshots
unifiedsplit
@@ -1,95 +1,84 @@
1−# This Travis job script has been generated by a script via
2−#
3−# make_travis_yml_2.hs 'hjsonschema.cabal'
4−#
5−# For more information, see https://github.com/hvr/multi-ghc-travis
1+# This file has been generated -- see https://github.com/hvr/multi-ghc-travis
6#
2#
3+# Manually modified to remove cabal check. I don't want a description field.
4+
7language: c
5language: c
8sudo: false
6sudo: false
9
7
10−git:
11− submodules: false # whether to recursively clone submodules
12−
13cache:
8cache:
14 directories:
9 directories:
10+ - $HOME/.cabsnap
15 - $HOME/.cabal/packages
11 - $HOME/.cabal/packages
16− - $HOME/.cabal/store
17
12
18before_cache:
13before_cache:
19 - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
14 - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
20− # remove files that are regenerated by 'cabal update'
21− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
15+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar
22− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
23− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
24− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
25− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
26
16
27matrix:
17matrix:
28 include:
18 include:
29− - compiler: "ghc-8.0.2"
19+ - env: CABALVER=1.18 GHCVER=7.8.4
30− # env: TEST=--disable-tests BENCH=--disable-benchmarks
20+ compiler: ": #GHC 7.8.4"
31− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.0.2], sources: [hvr-ghc]}}
21+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
32− - compiler: "ghc-8.2.2"
22+ - env: CABALVER=1.22 GHCVER=7.10.2
33− # env: TEST=--disable-tests BENCH=--disable-benchmarks
23+ compiler: ": #GHC 7.10.2"
34− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.2.2], sources: [hvr-ghc]}}
24+ addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.2], sources: [hvr-ghc]}}
35− - compiler: "ghc-8.4.1"
25+ - env: CABALVER=1.24 GHCVER=8.0.1
36− # env: TEST=--disable-tests BENCH=--disable-benchmarks
26+ compiler: ": #GHC 8.0.1"
37− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.4.1], sources: [hvr-ghc]}}
27+ addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.1], sources: [hvr-ghc]}}
38
28
39before_install:
29before_install:
40− - HC=${CC}
41− - HCPKG=${HC/ghc/ghc-pkg}
42 - unset CC
30 - unset CC
43− - PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$PATH
31+ - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
44− - PKGNAME='hjsonschema'
45
32
46install:
33install:
47 - cabal --version
34 - cabal --version
48− - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
35+ - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
49− - BENCH=${BENCH---enable-benchmarks}
36+ - if [ -f $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz ];
50− - TEST=${TEST---enable-tests}
37+ then
51− - HADDOCK=${HADDOCK-true}
38+ zcat $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz >
52− - INSTALLED=${INSTALLED-true}
39+ $HOME/.cabal/packages/hackage.haskell.org/00-index.tar;
40+ fi
53 - travis_retry cabal update -v
41 - travis_retry cabal update -v
54− - sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
42+ - sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
55− - rm -fv cabal.project.local
43+ - cabal install --only-dependencies --enable-tests --enable-benchmarks --dry -v > installplan.txt
56− - "echo 'packages: .' > cabal.project"
44+ - sed -i -e '1,/^Resolving /d' installplan.txt; cat installplan.txt
57− - rm -f cabal.project.freeze
45+
58− - cabal new-build -w ${HC} ${TEST} ${BENCH} --dep -j2 all
46+# check whether current requested install-plan matches cached package-db snapshot
59− - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --dep -j2 all
47+ - if diff -u installplan.txt $HOME/.cabsnap/installplan.txt;
48+ then
49+ echo "cabal build-cache HIT";
50+ rm -rfv .ghc;
51+ cp -a $HOME/.cabsnap/ghc $HOME/.ghc;
52+ cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/;
53+ else
54+ echo "cabal build-cache MISS";
55+ rm -rf $HOME/.cabsnap;
56+ mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin;
57+ cabal install --only-dependencies --enable-tests --enable-benchmarks;
58+ fi
59+
60+# snapshot package-db on cache miss
61+ - if [ ! -d $HOME/.cabsnap ];
62+ then
63+ echo "snapshotting package-db to build-cache";
64+ mkdir $HOME/.cabsnap;
65+ cp -a $HOME/.ghc $HOME/.cabsnap/ghc;
66+ cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;
67+ fi
60
68
61# Here starts the actual work to be performed for the package under test;
69# Here starts the actual work to be performed for the package under test;
62# any command which exits with a non-zero exit code causes the build to fail.
70# any command which exits with a non-zero exit code causes the build to fail.
63script:
71script:
64 - if [ -f configure.ac ]; then autoreconf -i; fi
72 - if [ -f configure.ac ]; then autoreconf -i; fi
65− - rm -rf .ghc.environment.* dist/
73+ - cabal configure --enable-tests --enable-benchmarks -v2 # -v2 provides useful information for debugging
74+ - cabal build # this builds all libraries and executables (including tests/benchmarks)
75+ - cabal test
66− - cabal sdist # test that a source-distribution can be generated
76+ - cabal sdist # tests that a source-distribution can be generated
67− - cd dist/
68− - SRCTAR=(${PKGNAME}-*.tar.gz)
69− - SRC_BASENAME="${SRCTAR/%.tar.gz}"
70− - tar -xvf "./$SRC_BASENAME.tar.gz"
71− - cd "$SRC_BASENAME/"
72−## from here on, CWD is inside the extracted source-tarball
73− - rm -fv cabal.project.local
74− - "echo 'packages: .' > cabal.project"
75− # this builds all libraries and executables (without tests/benchmarks)
76− - rm -f cabal.project.freeze
77− - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all
78− # this builds all libraries and executables (including tests/benchmarks)
79− # - rm -rf ./dist-newstyle
80−
81− # Build with installed constraints for packages in global-db
82− - if $INSTALLED; then
83− echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh;
84− else echo "Not building with installed constraints"; fi
85−
86− # build & run tests, build benchmarks
87− - cabal new-build -w ${HC} ${TEST} ${BENCH} all
88− - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} all; fi
89
77
90− # haddock
78+# Check that the resulting source distribution can be built & installed.
91− - rm -rf ./dist-newstyle
79+# If there are no other `.tar.gz` files in `dist`, this can be even simpler:
92− - if $HADDOCK; then cabal new-haddock -w ${HC} --disable-tests --disable-benchmarks all; else echo "Skipping haddock generation";fi
80+# `cabal install --force-reinstalls dist/*-*.tar.gz`
81+ - SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&
82+ (cd dist && cabal install --force-reinstalls "$SRC_TGZ")
93
83
94−# REGENDATA ["hjsonschema.cabal"]
95# EOF
84# EOF
JSON-Schema-Test-Suite/.travis.yml+2−7
@@ -1,9 +1,4 @@
1language: python
1language: python
2python: "2.7"
2python: "2.7"
3−node_js: "6"
4−install:
5− - pip install tox
3+install: pip install tox
6− - npm install
7−script:
4+script: tox
8− - tox
9− - npm test
JSON-Schema-Test-Suite/README.md+5−26
@@ -1,4 +1,4 @@
1−JSON Schema Test Suite [![Build Status](https://travis-ci.org/json-schema-org/JSON-Schema-Test-Suite.svg?branch=master)](https://travis-ci.org/json-schema-org/JSON-Schema-Test-Suite)
1+JSON Schema Test Suite [![Build Status](https://travis-ci.org/json-schema/JSON-Schema-Test-Suite.png?branch=develop)](https://travis-ci.org/json-schema/JSON-Schema-Test-Suite)
2======================
2======================
3
3
4This repository contains a set of JSON objects that implementors of JSON Schema
4This repository contains a set of JSON objects that implementors of JSON Schema
@@ -64,18 +64,10 @@ This suite is being used by:
64
64
65* [jsck](https://github.com/pandastrike/jsck)
65* [jsck](https://github.com/pandastrike/jsck)
66
66
67−### C++ ###
68−
69−* [Modern C++ JSON schema validator](https://github.com/pboettch/json-schema-validator)
70−
71### Dart ###
67### Dart ###
72
68
73* [json_schema](https://github.com/patefacio/json_schema)
69* [json_schema](https://github.com/patefacio/json_schema)
74
70
75−### Elixir ###
76−
77−* [ex_json_schema](https://github.com/jonasschmidt/ex_json_schema)
78−
79### Erlang ###
71### Erlang ###
80
72
81* [jesse](https://github.com/klarna/jesse)
73* [jesse](https://github.com/klarna/jesse)
@@ -92,9 +84,7 @@ This suite is being used by:
92
84
93### Java ###
85### Java ###
94
86
95−* [json-schema-validator](https://github.com/daveclayton/json-schema-validator)
87+* [json-schema-validator](https://github.com/fge/json-schema-validator)
96−* [everit-org/json-schema](https://github.com/everit-org/json-schema)
97−* [networknt/json-schema-validator](https://github.com/networknt/json-schema-validator)
98
88
99### JavaScript ###
89### JavaScript ###
100
90
@@ -112,25 +102,22 @@ This suite is being used by:
112* [z-schema](https://github.com/zaggino/z-schema)
102* [z-schema](https://github.com/zaggino/z-schema)
113* [jsen](https://github.com/bugventure/jsen)
103* [jsen](https://github.com/bugventure/jsen)
114* [ajv](https://github.com/epoberezkin/ajv)
104* [ajv](https://github.com/epoberezkin/ajv)
115−* [djv](https://github.com/korzio/djv)
116
105
117### Node.js ###
106### Node.js ###
118
107
119The JSON Schema Test Suite is also available as an
108The JSON Schema Test Suite is also available as an
120[npm](https://www.npmjs.com/package/json-schema-test-suite) package.
109[npm](https://www.npmjs.com/package/json-schema-test-suite) package.
121−Node-specific support is maintained on the [node branch](https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/node).
110+Node-specific support is maintained on the [node branch](https://github.com/json-schema/JSON-Schema-Test-Suite/tree/node).
122−See [NODE-README.md](https://github.com/json-schema-org/JSON-Schema-Test-Suite/blob/node/NODE-README.md)
111+See [NODE-README.md](https://github.com/json-schema/JSON-Schema-Test-Suite/blob/node/NODE-README.md)
123for more information.
112for more information.
124
113
125### .NET ###
114### .NET ###
126
115
127* [Newtonsoft.Json.Schema](https://github.com/JamesNK/Newtonsoft.Json.Schema)
116* [Newtonsoft.Json.Schema](https://github.com/JamesNK/Newtonsoft.Json.Schema)
128−* [Manatee.Json](https://github.com/gregsdennis/Manatee.Json)
129
117
130### PHP ###
118### PHP ###
131
119
132* [json-schema](https://github.com/justinrainbow/json-schema)
120* [json-schema](https://github.com/justinrainbow/json-schema)
133−* [json-guard](https://github.com/thephpleague/json-guard)
134
121
135### Python ###
122### Python ###
136
123
@@ -148,14 +135,6 @@ for more information.
148
135
149* [JSONSchema](https://github.com/kylef/JSONSchema.swift)
136* [JSONSchema](https://github.com/kylef/JSONSchema.swift)
150
137
151−### Clojure ###
152−
153−* [json-schema](https://github.com/tatut/json-schema)
154−
155−### PostgreSQL ###
156−
157−* [postgres-json-schema](https://github.com/gavinwahl/postgres-json-schema)
158−
159If you use it as well, please fork and send a pull request adding yourself to
138If you use it as well, please fork and send a pull request adding yourself to
160the list :).
139the list :).
161
140
@@ -165,5 +144,5 @@ Contributing
165If you see something missing or incorrect, a pull request is most welcome!
144If you see something missing or incorrect, a pull request is most welcome!
166
145
167There are some sanity checks in place for testing the test suite. You can run
146There are some sanity checks in place for testing the test suite. You can run
168−them with `bin/jsonschema_suite check && npm test` or `tox && npm test`. They will be run automatically by
147+them with `bin/jsonschema_suite check` or `tox`. They will be run automatically by
169[Travis CI](https://travis-ci.org/) as well.
148[Travis CI](https://travis-ci.org/) as well.
JSON-Schema-Test-Suite/bin/jsonschema_suite+27−2
@@ -52,8 +52,33 @@ REMOTES = {
52}
52}
53REMOTES_DIR = os.path.join(ROOT_DIR, "remotes")
53REMOTES_DIR = os.path.join(ROOT_DIR, "remotes")
54
54
55−with open(os.path.join(ROOT_DIR, "test-schema.json")) as schema:
56− TESTSUITE_SCHEMA = json.load(schema)
55+TESTSUITE_SCHEMA = {
56+ "$schema": "http://json-schema.org/draft-03/schema#",
57+ "type": "array",
58+ "items": {
59+ "type": "object",
60+ "properties": {
61+ "description": {"type": "string", "required": True},
62+ "schema": {"required": True},
63+ "tests": {
64+ "type": "array",
65+ "items": {
66+ "type": "object",
67+ "properties": {
68+ "description": {"type": "string", "required": True},
69+ "data": {"required": True},
70+ "valid": {"type": "boolean", "required": True}
71+ },
72+ "additionalProperties": False
73+ },
74+ "minItems": 1
75+ }
76+ },
77+ "additionalProperties": False,
78+ "minItems": 1
79+ }
80+}
81+
57
82
58def files(paths):
83def files(paths):
59 for path in paths:
84 for path in paths:
JSON-Schema-Test-Suite/index.jsdeleted−32
@@ -1,32 +0,0 @@
1−'use strict';
2−
3−var Ajv = require('ajv');
4−var jsonSchemaTest = require('json-schema-test');
5−var assert = require('assert');
6−
7−var refs = {
8− 'http://localhost:1234/integer.json': require('./remotes/integer.json'),
9− 'http://localhost:1234/subSchemas.json': require('./remotes/subSchemas.json'),
10− 'http://localhost:1234/folder/folderInteger.json': require('./remotes/folder/folderInteger.json'),
11− 'http://localhost:1234/name.json': require('./remotes/name.json')
12−};
13−
14−runTest(4);
15−runTest(6);
16−
17−function runTest(draft) {
18− var opts = {};
19− if (draft == 4) opts.meta = false;
20− var ajv = new Ajv(opts);
21− ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'));
22− if (draft == 4) ajv._opts.defaultMeta = 'http://json-schema.org/draft-04/schema#';
23− for (var uri in refs) ajv.addSchema(refs[uri], uri);
24−
25− jsonSchemaTest(ajv, {
26− description: 'Test suite draft-0' + draft,
27− suites: {tests: './tests/draft' + draft + '/{**/,}*.json'},
28− skip: draft == 4 ? ['optional/zeroTerminatedFloats'] : [],
29− cwd: __dirname,
30− hideFolder: 'tests/'
31− });
32−}
JSON-Schema-Test-Suite/package.jsondeleted−28
@@ -1,28 +0,0 @@
1−{
2− "name": "json-schema-test-suite",
3− "version": "0.1.0",
4− "description": "A language agnostic test suite for the JSON Schema specifications",
5− "main": "index.js",
6− "scripts": {
7− "test": "mocha index.js -R spec"
8− },
9− "repository": {
10− "type": "git",
11− "url": "git+https://github.com/json-schema-org/JSON-Schema-Test-Suite.git"
12− },
13− "keywords": [
14− "json-schema",
15− "tests"
16− ],
17− "author": "http://json-schema.org",
18− "license": "MIT",
19− "bugs": {
20− "url": "https://github.com/json-schema-org/JSON-Schema-Test-Suite/issues"
21− },
22− "homepage": "https://github.com/json-schema-org/JSON-Schema-Test-Suite#readme",
23− "devDependencies": {
24− "ajv": "^5.0.4-beta.0",
25− "json-schema-test": "^1.3.0",
26− "mocha": "^3.2.0"
27− }
28−}
JSON-Schema-Test-Suite/remotes/name.jsondeleted−11
@@ -1,11 +0,0 @@
1−{
2− "definitions": {
3− "orNull": {
4− "anyOf": [
5− {"type": "null"},
6− {"$ref": "#"}
7− ]
8− }
9− },
10− "type": "string"
11−}
JSON-Schema-Test-Suite/test-schema.jsondeleted−28
@@ -1,28 +0,0 @@
1−{
2− "$schema": "http://json-schema.org/draft-04/schema#",
3− "type": "array",
4− "items": {
5− "type": "object",
6− "required": ["description", "schema", "tests"],
7− "properties": {
8− "description": {"type": "string"},
9− "schema": {},
10− "tests": {
11− "type": "array",
12− "items": {
13− "type": "object",
14− "required": ["description", "data", "valid"],
15− "properties": {
16− "description": {"type": "string"},
17− "data": {},
18− "valid": {"type": "boolean"}
19− },
20− "additionalProperties": false
21− },
22− "minItems": 1
23− }
24− },
25− "additionalProperties": false,
26− "minItems": 1
27− }
28−}
JSON-Schema-Test-Suite/tests/draft3/optional/format.json+2−2
@@ -78,9 +78,9 @@
78 "valid": true
78 "valid": true
79 },
79 },
80 {
80 {
81− "description": "an invalid protocol-relative URI Reference",
81+ "description": "a valid protocol-relative URI",
82 "data": "//foo.bar/?baz=qux#quux",
82 "data": "//foo.bar/?baz=qux#quux",
83− "valid": false
83+ "valid": true
84 },
84 },
85 {
85 {
86 "description": "an invalid URI",
86 "description": "an invalid URI",
JSON-Schema-Test-Suite/tests/draft3/ref.json−33
@@ -140,39 +140,6 @@
140 }
140 }
141 ]
141 ]
142 },
142 },
143− {
144− "description": "ref overrides any sibling keywords",
145− "schema": {
146− "definitions": {
147− "reffed": {
148− "type": "array"
149− }
150− },
151− "properties": {
152− "foo": {
153− "$ref": "#/definitions/reffed",
154− "maxItems": 2
155− }
156− }
157− },
158− "tests": [
159− {
160− "description": "remote ref valid",
161− "data": { "foo": [] },
162− "valid": true
163− },
164− {
165− "description": "remote ref valid, maxItems ignored",
166− "data": { "foo": [ 1, 2, 3] },
167− "valid": true
168− },
169− {
170− "description": "ref invalid",
171− "data": { "foo": "string" },
172− "valid": false
173− }
174− ]
175− },
176 {
143 {
177 "description": "remote ref, containing refs itself",
144 "description": "remote ref, containing refs itself",
178 "schema": {"$ref": "http://json-schema.org/draft-03/schema#"},
145 "schema": {"$ref": "http://json-schema.org/draft-03/schema#"},
JSON-Schema-Test-Suite/tests/draft3/type.json−15
@@ -18,11 +18,6 @@
18 "data": "foo",
18 "data": "foo",
19 "valid": false
19 "valid": false
20 },
20 },
21− {
22− "description": "a string is still not an integer, even if it looks like one",
23− "data": "1",
24− "valid": false
25− },
26 {
21 {
27 "description": "an object is not an integer",
22 "description": "an object is not an integer",
28 "data": {},
23 "data": {},
@@ -64,11 +59,6 @@
64 "data": "foo",
59 "data": "foo",
65 "valid": false
60 "valid": false
66 },
61 },
67− {
68− "description": "a string is still not a number, even if it looks like one",
69− "data": "1",
70− "valid": false
71− },
72 {
62 {
73 "description": "an object is not a number",
63 "description": "an object is not a number",
74 "data": {},
64 "data": {},
@@ -110,11 +100,6 @@
110 "data": "foo",
100 "data": "foo",
111 "valid": true
101 "valid": true
112 },
102 },
113− {
114− "description": "a string is still a string, even if it looks like a number",
115− "data": "1",
116− "valid": true
117− },
118 {
103 {
119 "description": "an object is not a string",
104 "description": "an object is not a string",
120 "data": {},
105 "data": {},
JSON-Schema-Test-Suite/tests/draft4/additionalItems.json+1−6
@@ -40,12 +40,7 @@
40 },
40 },
41 "tests": [
41 "tests": [
42 {
42 {
43− "description": "fewer number of items present",
43+ "description": "no additional items present",
44− "data": [ 1, 2 ],
45− "valid": true
46− },
47− {
48− "description": "equal number of items present",
49 "data": [ 1, 2, 3 ],
44 "data": [ 1, 2, 3 ],
50 "valid": true
45 "valid": true
51 },
46 },
JSON-Schema-Test-Suite/tests/draft4/items.json−32
@@ -19,14 +19,6 @@
19 "description": "ignores non-arrays",
19 "description": "ignores non-arrays",
20 "data": {"foo" : "bar"},
20 "data": {"foo" : "bar"},
21 "valid": true
21 "valid": true
22− },
23− {
24− "description": "JavaScript pseudo-array is valid",
25− "data": {
26− "0": "invalid",
27− "length": 1
28− },
29− "valid": true
30 }
22 }
31 ]
23 ]
32 },
24 },
@@ -48,30 +40,6 @@
48 "description": "wrong types",
40 "description": "wrong types",
49 "data": [ "foo", 1 ],
41 "data": [ "foo", 1 ],
50 "valid": false
42 "valid": false
51− },
52− {
53− "description": "incomplete array of items",
54− "data": [ 1 ],
55− "valid": true
56− },
57− {
58− "description": "array with additional items",
59− "data": [ 1, "foo", true ],
60− "valid": true
61− },
62− {
63− "description": "empty array",
64− "data": [ ],
65− "valid": true
66− },
67− {
68− "description": "JavaScript pseudo-array is valid",
69− "data": {
70− "0": "invalid",
71− "1": "valid",
72− "length": 2
73− },
74− "valid": true
75 }
43 }
76 ]
44 ]
77 }
45 }
JSON-Schema-Test-Suite/tests/draft4/maximum.json−5
@@ -8,11 +8,6 @@
8 "data": 2.6,
8 "data": 2.6,
9 "valid": true
9 "valid": true
10 },
10 },
11− {
12− "description": "boundary point is valid",
13− "data": 3.0,
14− "valid": true
15− },
16 {
11 {
17 "description": "above the maximum is invalid",
12 "description": "above the maximum is invalid",
18 "data": 3.5,
13 "data": 3.5,
JSON-Schema-Test-Suite/tests/draft4/minimum.json−5
@@ -8,11 +8,6 @@
8 "data": 2.6,
8 "data": 2.6,
9 "valid": true
9 "valid": true
10 },
10 },
11− {
12− "description": "boundary point is valid",
13− "data": 1.1,
14− "valid": true
15− },
16 {
11 {
17 "description": "below the minimum is invalid",
12 "description": "below the minimum is invalid",
18 "data": 0.6,
13 "data": 0.6,
JSON-Schema-Test-Suite/tests/draft4/optional/ecmascript-regex.jsondeleted−13
@@ -1,13 +0,0 @@
1−[
2− {
3− "description": "ECMA 262 regex non-compliance",
4− "schema": { "format": "regex" },
5− "tests": [
6− {
7− "description": "ECMA 262 has no support for \\Z anchor from .NET",
8− "data": "^\\S(|(.|\\n)*\\S)\\Z",
9− "valid": false
10− }
11− ]
12− }
13−]
JSON-Schema-Test-Suite/tests/draft4/optional/format.json+2−2
@@ -30,9 +30,9 @@
30 "valid": true
30 "valid": true
31 },
31 },
32 {
32 {
33− "description": "an invalid protocol-relative URI Reference",
33+ "description": "a valid protocol-relative URI",
34 "data": "//foo.bar/?baz=qux#quux",
34 "data": "//foo.bar/?baz=qux#quux",
35− "valid": false
35+ "valid": true
36 },
36 },
37 {
37 {
38 "description": "an invalid URI",
38 "description": "an invalid URI",
JSON-Schema-Test-Suite/tests/draft4/ref.json−139
@@ -140,39 +140,6 @@
140 }
140 }
141 ]
141 ]
142 },
142 },
143− {
144− "description": "ref overrides any sibling keywords",
145− "schema": {
146− "definitions": {
147− "reffed": {
148− "type": "array"
149− }
150− },
151− "properties": {
152− "foo": {
153− "$ref": "#/definitions/reffed",
154− "maxItems": 2
155− }
156− }
157− },
158− "tests": [
159− {
160− "description": "ref valid",
161− "data": { "foo": [] },
162− "valid": true
163− },
164− {
165− "description": "ref valid, maxItems ignored",
166− "data": { "foo": [ 1, 2, 3] },
167− "valid": true
168− },
169− {
170− "description": "ref invalid",
171− "data": { "foo": "string" },
172− "valid": false
173− }
174− ]
175− },
176 {
143 {
177 "description": "remote ref, containing refs itself",
144 "description": "remote ref, containing refs itself",
178 "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
145 "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
@@ -188,111 +155,5 @@
188 "valid": false
155 "valid": false
189 }
156 }
190 ]
157 ]
191− },
192− {
193− "description": "property named $ref that is not a reference",
194− "schema": {
195− "properties": {
196− "$ref": {"type": "string"}
197− }
198− },
199− "tests": [
200− {
201− "description": "property named $ref valid",
202− "data": {"$ref": "a"},
203− "valid": true
204− },
205− {
206− "description": "property named $ref invalid",
207− "data": {"$ref": 2},
208− "valid": false
209− }
210− ]
211− },
212− {
213− "description": "Recursive references between schemas",
214− "schema": {
215− "description": "tree of nodes",
216− "type": "object",
217− "properties": {
218− "meta": {"type": "string"},
219− "nodes": {
220− "type": "array",
221− "items": {"$ref": "#/definitions/node"}
222− }
223− },
224− "required": ["meta", "nodes"],
225− "definitions": {
226− "node": {
227− "description": "node",
228− "type": "object",
229− "properties": {
230− "value": {"type": "number"},
231− "subtree": {"$ref": "#"}
232− },
233− "required": ["value"]
234− }
235− }
236− },
237− "tests": [
238− {
239− "description": "valid tree",
240− "data": {
241− "meta": "root",
242− "nodes": [
243− {
244− "value": 1,
245− "subtree": {
246− "meta": "child",
247− "nodes": [
248− {"value": 1.1},
249− {"value": 1.2}
250− ]
251− }
252− },
253− {
254− "value": 2,
255− "subtree": {
256− "meta": "child",
257− "nodes": [
258− {"value": 2.1},
259− {"value": 2.2}
260− ]
261− }
262− }
263− ]
264− },
265− "valid": true
266− },
267− {
268− "description": "invalid tree",
269− "data": {
270− "meta": "root",
271− "nodes": [
272− {
273− "value": 1,
274− "subtree": {
275− "meta": "child",
276− "nodes": [
277− {"value": "string is invalid"},
278− {"value": 1.2}
279− ]
280− }
281− },
282− {
283− "value": 2,
284− "subtree": {
285− "meta": "child",
286− "nodes": [
287− {"value": 2.1},
288− {"value": 2.2}
289− ]
290− }
291− }
292− ]
293− },
294− "valid": false
295− }
296− ]
297 }
158 }
298]
159]
JSON-Schema-Test-Suite/tests/draft4/refRemote.json+3−100
@@ -50,7 +50,7 @@
50 ]
50 ]
51 },
51 },
52 {
52 {
53− "description": "base URI change",
53+ "description": "change resolution scope",
54 "schema": {
54 "schema": {
55 "id": "http://localhost:1234/",
55 "id": "http://localhost:1234/",
56 "items": {
56 "items": {
@@ -60,112 +60,15 @@
60 },
60 },
61 "tests": [
61 "tests": [
62 {
62 {
63− "description": "base URI change ref valid",
63+ "description": "changed scope ref valid",
64 "data": [[1]],
64 "data": [[1]],
65 "valid": true
65 "valid": true
66 },
66 },
67 {
67 {
68− "description": "base URI change ref invalid",
68+ "description": "changed scope ref invalid",
69 "data": [["a"]],
69 "data": [["a"]],
70 "valid": false
70 "valid": false
71 }
71 }
72 ]
72 ]
73− },
74− {
75− "description": "base URI change - change folder",
76− "schema": {
77− "id": "http://localhost:1234/scope_change_defs1.json",
78− "type" : "object",
79− "properties": {
80− "list": {"$ref": "#/definitions/baz"}
81− },
82− "definitions": {
83− "baz": {
84− "id": "folder/",
85− "type": "array",
86− "items": {"$ref": "folderInteger.json"}
87− }
88− }
89− },
90− "tests": [
91− {
92− "description": "number is valid",
93− "data": {"list": [1]},
94− "valid": true
95− },
96− {
97− "description": "string is invalid",
98− "data": {"list": ["a"]},
99− "valid": false
100− }
101− ]
102− },
103− {
104− "description": "base URI change - change folder in subschema",
105− "schema": {
106− "id": "http://localhost:1234/scope_change_defs2.json",
107− "type" : "object",
108− "properties": {
109− "list": {"$ref": "#/definitions/baz/definitions/bar"}
110− },
111− "definitions": {
112− "baz": {
113− "id": "folder/",
114− "definitions": {
115− "bar": {
116− "type": "array",
117− "items": {"$ref": "folderInteger.json"}
118− }
119− }
120− }
121− }
122− },
123− "tests": [
124− {
125− "description": "number is valid",
126− "data": {"list": [1]},
127− "valid": true
128− },
129− {
130− "description": "string is invalid",
131− "data": {"list": ["a"]},
132− "valid": false
133− }
134− ]
135− },
136− {
137− "description": "root ref in remote ref",
138− "schema": {
139− "id": "http://localhost:1234/object",
140− "type": "object",
141− "properties": {
142− "name": {"$ref": "name.json#/definitions/orNull"}
143− }
144− },
145− "tests": [
146− {
147− "description": "string is valid",
148− "data": {
149− "name": "foo"
150− },
151− "valid": true
152− },
153− {
154− "description": "null is valid",
155− "data": {
156− "name": null
157− },
158− "valid": true
159− },
160− {
161− "description": "object is invalid",
162− "data": {
163− "name": {
164− "name": null
165− }
166− },
167− "valid": false
168− }
169− ]
170 }
73 }
171]
74]
JSON-Schema-Test-Suite/tests/draft4/required.json−5
@@ -18,11 +18,6 @@
18 "description": "non-present required property is invalid",
18 "description": "non-present required property is invalid",
19 "data": {"bar": 1},
19 "data": {"bar": 1},
20 "valid": false
20 "valid": false
21− },
22− {
23− "description": "ignores non-objects",
24− "data": 12,
25− "valid": true
26 }
21 }
27 ]
22 ]
28 },
23 },
JSON-Schema-Test-Suite/tests/draft4/type.json−15
@@ -18,11 +18,6 @@
18 "data": "foo",
18 "data": "foo",
19 "valid": false
19 "valid": false
20 },
20 },
21− {
22− "description": "a string is still not an integer, even if it looks like one",
23− "data": "1",
24− "valid": false
25− },
26 {
21 {
27 "description": "an object is not an integer",
22 "description": "an object is not an integer",
28 "data": {},
23 "data": {},
@@ -64,11 +59,6 @@
64 "data": "foo",
59 "data": "foo",
65 "valid": false
60 "valid": false
66 },
61 },
67− {
68− "description": "a string is still not a number, even if it looks like one",
69− "data": "1",
70− "valid": false
71− },
72 {
62 {
73 "description": "an object is not a number",
63 "description": "an object is not a number",
74 "data": {},
64 "data": {},
@@ -110,11 +100,6 @@
110 "data": "foo",
100 "data": "foo",
111 "valid": true
101 "valid": true
112 },
102 },
113− {
114− "description": "a string is still a string, even if it looks like a number",
115− "data": "1",
116− "valid": true
117− },
118 {
103 {
119 "description": "an object is not a string",
104 "description": "an object is not a string",
120 "data": {},
105 "data": {},
JSON-Schema-Test-Suite/tests/draft6/additionalItems.jsondeleted−87
@@ -1,87 +0,0 @@
1−[
2− {
3− "description": "additionalItems as schema",
4− "schema": {
5− "items": [{}],
6− "additionalItems": {"type": "integer"}
7− },
8− "tests": [
9− {
10− "description": "additional items match schema",
11− "data": [ null, 2, 3, 4 ],
12− "valid": true
13− },
14− {
15− "description": "additional items do not match schema",
16− "data": [ null, 2, 3, "foo" ],
17− "valid": false
18− }
19− ]
20− },
21− {
22− "description": "items is schema, no additionalItems",
23− "schema": {
24− "items": {},
25− "additionalItems": false
26− },
27− "tests": [
28− {
29− "description": "all items match schema",
30− "data": [ 1, 2, 3, 4, 5 ],
31− "valid": true
32− }
33− ]
34− },
35− {
36− "description": "array of items with no additionalItems",
37− "schema": {
38− "items": [{}, {}, {}],
39− "additionalItems": false
40− },
41− "tests": [
42− {
43− "description": "fewer number of items present",
44− "data": [ 1, 2 ],
45− "valid": true
46− },
47− {
48− "description": "equal number of items present",
49− "data": [ 1, 2, 3 ],
50− "valid": true
51− },
52− {
53− "description": "additional items are not permitted",
54− "data": [ 1, 2, 3, 4 ],
55− "valid": false
56− }
57− ]
58− },
59− {
60− "description": "additionalItems as false without items",
61− "schema": {"additionalItems": false},
62− "tests": [
63− {
64− "description":
65− "items defaults to empty schema so everything is valid",
66− "data": [ 1, 2, 3, 4, 5 ],
67− "valid": true
68− },
69− {
70− "description": "ignores non-arrays",
71− "data": {"foo" : "bar"},
72− "valid": true
73− }
74− ]
75− },
76− {
77− "description": "additionalItems are allowed by default",
78− "schema": {"items": [{"type": "integer"}]},
79− "tests": [
80− {
81− "description": "only the first item is validated",
82− "data": [1, "foo", false],
83− "valid": true
84− }
85− ]
86− }
87−]
JSON-Schema-Test-Suite/tests/draft6/additionalProperties.jsondeleted−88
@@ -1,88 +0,0 @@
1−[
2− {
3− "description":
4− "additionalProperties being false does not allow other properties",
5− "schema": {
6− "properties": {"foo": {}, "bar": {}},
7− "patternProperties": { "^v": {} },
8− "additionalProperties": false
9− },
10− "tests": [
11− {
12− "description": "no additional properties is valid",
13− "data": {"foo": 1},
14− "valid": true
15− },
16− {
17− "description": "an additional property is invalid",
18− "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
19− "valid": false
20− },
21− {
22− "description": "ignores non-objects",
23− "data": [1, 2, 3],
24− "valid": true
25− },
26− {
27− "description": "patternProperties are not additional properties",
28− "data": {"foo":1, "vroom": 2},
29− "valid": true
30− }
31− ]
32− },
33− {
34− "description":
35− "additionalProperties allows a schema which should validate",
36− "schema": {
37− "properties": {"foo": {}, "bar": {}},
38− "additionalProperties": {"type": "boolean"}
39− },
40− "tests": [
41− {
42− "description": "no additional properties is valid",
43− "data": {"foo": 1},
44− "valid": true
45− },
46− {
47− "description": "an additional valid property is valid",
48− "data": {"foo" : 1, "bar" : 2, "quux" : true},
49− "valid": true
50− },
51− {
52− "description": "an additional invalid property is invalid",
53− "data": {"foo" : 1, "bar" : 2, "quux" : 12},
54− "valid": false
55− }
56− ]
57− },
58− {
59− "description":
60− "additionalProperties can exist by itself",
61− "schema": {
62− "additionalProperties": {"type": "boolean"}
63− },
64− "tests": [
65− {
66− "description": "an additional valid property is valid",
67− "data": {"foo" : true},
68− "valid": true
69− },
70− {
71− "description": "an additional invalid property is invalid",
72− "data": {"foo" : 1},
73− "valid": false
74− }
75− ]
76− },
77− {
78− "description": "additionalProperties are allowed by default",
79− "schema": {"properties": {"foo": {}, "bar": {}}},
80− "tests": [
81− {
82− "description": "additional properties are allowed",
83− "data": {"foo": 1, "bar": 2, "quux": true},
84− "valid": true
85− }
86− ]
87− }
88−]
JSON-Schema-Test-Suite/tests/draft6/allOf.jsondeleted−145
@@ -1,145 +0,0 @@
1−[
2− {
3− "description": "allOf",
4− "schema": {
5− "allOf": [
6− {
7− "properties": {
8− "bar": {"type": "integer"}
9− },
10− "required": ["bar"]
11− },
12− {
13− "properties": {
14− "foo": {"type": "string"}
15− },
16− "required": ["foo"]
17− }
18− ]
19− },
20− "tests": [
21− {
22− "description": "allOf",
23− "data": {"foo": "baz", "bar": 2},
24− "valid": true
25− },
26− {
27− "description": "mismatch second",
28− "data": {"foo": "baz"},
29− "valid": false
30− },
31− {
32− "description": "mismatch first",
33− "data": {"bar": 2},
34− "valid": false
35− },
36− {
37− "description": "wrong type",
38− "data": {"foo": "baz", "bar": "quux"},
39− "valid": false
40− }
41− ]
42− },
43− {
44− "description": "allOf with base schema",
45− "schema": {
46− "properties": {"bar": {"type": "integer"}},
47− "required": ["bar"],
48− "allOf" : [
49− {
50− "properties": {
51− "foo": {"type": "string"}
52− },
53− "required": ["foo"]
54− },
55− {
56− "properties": {
57− "baz": {"type": "null"}
58− },
59− "required": ["baz"]
60− }
61− ]
62− },
63− "tests": [
64− {
65− "description": "valid",
66− "data": {"foo": "quux", "bar": 2, "baz": null},
67− "valid": true
68− },
69− {
70− "description": "mismatch base schema",
71− "data": {"foo": "quux", "baz": null},
72− "valid": false
73− },
74− {
75− "description": "mismatch first allOf",
76− "data": {"bar": 2, "baz": null},
77− "valid": false
78− },
79− {
80− "description": "mismatch second allOf",
81− "data": {"foo": "quux", "bar": 2},
82− "valid": false
83− },
84− {
85− "description": "mismatch both",
86− "data": {"bar": 2},
87− "valid": false
88− }
89− ]
90− },
91− {
92− "description": "allOf simple types",
93− "schema": {
94− "allOf": [
95− {"maximum": 30},
96− {"minimum": 20}
97− ]
98− },
99− "tests": [
100− {
101− "description": "valid",
102− "data": 25,
103− "valid": true
104− },
105− {
106− "description": "mismatch one",
107− "data": 35,
108− "valid": false
109− }
110− ]
111− },
112− {
113− "description": "allOf with boolean schemas, all true",
114− "schema": {"allOf": [true, true]},
115− "tests": [
116− {
117− "description": "any value is valid",
118− "data": "foo",
119− "valid": true
120− }
121− ]
122− },
123− {
124− "description": "allOf with boolean schemas, some false",
125− "schema": {"allOf": [true, false]},
126− "tests": [
127− {
128− "description": "any value is invalid",
129− "data": "foo",
130− "valid": false
131− }
132− ]
133− },
134− {
135− "description": "allOf with boolean schemas, all false",
136− "schema": {"allOf": [false, false]},
137− "tests": [
138− {
139− "description": "any value is invalid",
140− "data": "foo",
141− "valid": false
142− }
143− ]
144− }
145−]
JSON-Schema-Test-Suite/tests/draft6/anyOf.jsondeleted−101
@@ -1,101 +0,0 @@
1−[
2− {
3− "description": "anyOf",
4− "schema": {
5− "anyOf": [
6− {
7− "type": "integer"
8− },
9− {
10− "minimum": 2
11− }
12− ]
13− },
14− "tests": [
15− {
16− "description": "first anyOf valid",
17− "data": 1,
18− "valid": true
19− },
20− {
21− "description": "second anyOf valid",
22− "data": 2.5,
23− "valid": true
24− },
25− {
26− "description": "both anyOf valid",
27− "data": 3,
28− "valid": true
29− },
30− {
31− "description": "neither anyOf valid",
32− "data": 1.5,
33− "valid": false
34− }
35− ]
36− },
37− {
38− "description": "anyOf with base schema",
39− "schema": {
40− "type": "string",
41− "anyOf" : [
42− {
43− "maxLength": 2
44− },
45− {
46− "minLength": 4
47− }
48− ]
49− },
50− "tests": [
51− {
52− "description": "mismatch base schema",
53− "data": 3,
54− "valid": false
55− },
56− {
57− "description": "one anyOf valid",
58− "data": "foobar",
59− "valid": true
60− },
61− {
62− "description": "both anyOf invalid",
63− "data": "foo",
64− "valid": false
65− }
66− ]
67− },
68− {
69− "description": "anyOf with boolean schemas, all true",
70− "schema": {"anyOf": [true, true]},
71− "tests": [
72− {
73− "description": "any value is valid",
74− "data": "foo",
75− "valid": true
76− }
77− ]
78− },
79− {
80− "description": "anyOf with boolean schemas, some true",
81− "schema": {"anyOf": [true, false]},
82− "tests": [
83− {
84− "description": "any value is valid",
85− "data": "foo",
86− "valid": true
87− }
88− ]
89− },
90− {
91− "description": "anyOf with boolean schemas, all false",
92− "schema": {"anyOf": [false, false]},
93− "tests": [
94− {
95− "description": "any value is invalid",
96− "data": "foo",
97− "valid": false
98− }
99− ]
100− }
101−]
JSON-Schema-Test-Suite/tests/draft6/boolean_schema.jsondeleted−104
@@ -1,104 +0,0 @@
1−[
2− {
3− "description": "boolean schema 'true'",
4− "schema": true,
5− "tests": [
6− {
7− "description": "number is valid",
8− "data": 1,
9− "valid": true
10− },
11− {
12− "description": "string is valid",
13− "data": "foo",
14− "valid": true
15− },
16− {
17− "description": "boolean true is valid",
18− "data": true,
19− "valid": true
20− },
21− {
22− "description": "boolean false is valid",
23− "data": false,
24− "valid": true
25− },
26− {
27− "description": "null is valid",
28− "data": null,
29− "valid": true
30− },
31− {
32− "description": "object is valid",
33− "data": {"foo": "bar"},
34− "valid": true
35− },
36− {
37− "description": "empty object is valid",
38− "data": {},
39− "valid": true
40− },
41− {
42− "description": "array is valid",
43− "data": ["foo"],
44− "valid": true
45− },
46− {
47− "description": "empty array is valid",
48− "data": [],
49− "valid": true
50− }
51− ]
52− },
53− {
54− "description": "boolean schema 'false'",
55− "schema": false,
56− "tests": [
57− {
58− "description": "number is invalid",
59− "data": 1,
60− "valid": false
61− },
62− {
63− "description": "string is invalid",
64− "data": "foo",
65− "valid": false
66− },
67− {
68− "description": "boolean true is invalid",
69− "data": true,
70− "valid": false
71− },
72− {
73− "description": "boolean false is invalid",
74− "data": false,
75− "valid": false
76− },
77− {
78− "description": "null is invalid",
79− "data": null,
80− "valid": false
81− },
82− {
83− "description": "object is invalid",
84− "data": {"foo": "bar"},
85− "valid": false
86− },
87− {
88− "description": "empty object is invalid",
89− "data": {},
90− "valid": false
91− },
92− {
93− "description": "array is invalid",
94− "data": ["foo"],
95− "valid": false
96− },
97− {
98− "description": "empty array is invalid",
99− "data": [],
100− "valid": false
101− }
102− ]
103− }
104−]
JSON-Schema-Test-Suite/tests/draft6/const.jsondeleted−65
@@ -1,65 +0,0 @@
1−[
2− {
3− "description": "const validation",
4− "schema": {"const": 2},
5− "tests": [
6− {
7− "description": "same value is valid",
8− "data": 2,
9− "valid": true
10− },
11− {
12− "description": "another value is invalid",
13− "data": 5,
14− "valid": false
15− },
16− {
17− "description": "another type is invalid",
18− "data": "a",
19− "valid": false
20− }
21− ]
22− },
23− {
24− "description": "const with object",
25− "schema": {"const": {"foo": "bar", "baz": "bax"}},
26− "tests": [
27− {
28− "description": "same object is valid",
29− "data": {"foo": "bar", "baz": "bax"},
30− "valid": true
31− },
32− {
33− "description": "same object with different property order is valid",
34− "data": {"baz": "bax", "foo": "bar"},
35− "valid": true
36− },
37− {
38− "description": "another object is invalid",
39− "data": {"foo": "bar"},
40− "valid": false
41− },
42− {
43− "description": "another type is invalid",
44− "data": [1, 2],
45− "valid": false
46− }
47− ]
48− },
49− {
50− "description": "const with null",
51− "schema": {"const": null},
52− "tests": [
53− {
54− "description": "null is valid",
55− "data": null,
56− "valid": true
57− },
58− {
59− "description": "not null is invalid",
60− "data": 0,
61− "valid": false
62− }
63− ]
64− }
65−]
JSON-Schema-Test-Suite/tests/draft6/contains.jsondeleted−95
@@ -1,95 +0,0 @@
1−[
2− {
3− "description": "contains keyword validation",
4− "schema": {
5− "contains": {"minimum": 5}
6− },
7− "tests": [
8− {
9− "description": "array with item matching schema (5) is valid",
10− "data": [3, 4, 5],
11− "valid": true
12− },
13− {
14− "description": "array with item matching schema (6) is valid",
15− "data": [3, 4, 6],
16− "valid": true
17− },
18− {
19− "description": "array with two items matching schema (5, 6) is valid",
20− "data": [3, 4, 5, 6],
21− "valid": true
22− },
23− {
24− "description": "array without items matching schema is invalid",
25− "data": [2, 3, 4],
26− "valid": false
27− },
28− {
29− "description": "empty array is invalid",
30− "data": [],
31− "valid": false
32− },
33− {
34− "description": "not array is valid",
35− "data": {},
36− "valid": true
37− }
38− ]
39− },
40− {
41− "description": "contains keyword with const keyword",
42− "schema": {
43− "contains": { "const": 5 }
44− },
45− "tests": [
46− {
47− "description": "array with item 5 is valid",
48− "data": [3, 4, 5],
49− "valid": true
50− },
51− {
52− "description": "array with two items 5 is valid",
53− "data": [3, 4, 5, 5],
54− "valid": true
55− },
56− {
57− "description": "array without item 5 is invalid",
58− "data": [1, 2, 3, 4],
59− "valid": false
60− }
61− ]
62− },
63− {
64− "description": "contains keyword with boolean schema true",
65− "schema": {"contains": true},
66− "tests": [
67− {
68− "description": "any non-empty array is valid",
69− "data": ["foo"],
70− "valid": true
71− },
72− {
73− "description": "empty array is invalid",
74− "data": [],
75− "valid": false
76− }
77− ]
78− },
79− {
80− "description": "contains keyword with boolean schema false",
81− "schema": {"contains": false},
82− "tests": [
83− {
84− "description": "any non-empty array is invalid",
85− "data": ["foo"],
86− "valid": false
87− },
88− {
89− "description": "empty array is invalid",
90− "data": [],
91− "valid": false
92− }
93− ]
94− }
95−]
JSON-Schema-Test-Suite/tests/draft6/default.jsondeleted−49
@@ -1,49 +0,0 @@
1−[
2− {
3− "description": "invalid type for default",
4− "schema": {
5− "properties": {
6− "foo": {
7− "type": "integer",
8− "default": []
9− }
10− }
11− },
12− "tests": [
13− {
14− "description": "valid when property is specified",
15− "data": {"foo": 13},
16− "valid": true
17− },
18− {
19− "description": "still valid when the invalid default is used",
20− "data": {},
21− "valid": true
22− }
23− ]
24− },
25− {
26− "description": "invalid string value for default",
27− "schema": {
28− "properties": {
29− "bar": {
30− "type": "string",
31− "minLength": 4,
32− "default": "bad"
33− }
34− }
35− },
36− "tests": [
37− {
38− "description": "valid when property is specified",
39− "data": {"bar": "good"},
40− "valid": true
41− },
42− {
43− "description": "still valid when the invalid default is used",
44− "data": {},
45− "valid": true
46− }
47− ]
48− }
49−]
JSON-Schema-Test-Suite/tests/draft6/definitions.jsondeleted−32
@@ -1,32 +0,0 @@
1−[
2− {
3− "description": "valid definition",
4− "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
5− "tests": [
6− {
7− "description": "valid definition schema",
8− "data": {
9− "definitions": {
10− "foo": {"type": "integer"}
11− }
12− },
13− "valid": true
14− }
15− ]
16− },
17− {
18− "description": "invalid definition",
19− "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
20− "tests": [
21− {
22− "description": "invalid definition schema",
23− "data": {
24− "definitions": {
25− "foo": {"type": 1}
26− }
27− },
28− "valid": false
29− }
30− ]
31− }
32−]
JSON-Schema-Test-Suite/tests/draft6/dependencies.jsondeleted−162
@@ -1,162 +0,0 @@
1−[
2− {
3− "description": "dependencies",
4− "schema": {
5− "dependencies": {"bar": ["foo"]}
6− },
7− "tests": [
8− {
9− "description": "neither",
10− "data": {},
11− "valid": true
12− },
13− {
14− "description": "nondependant",
15− "data": {"foo": 1},
16− "valid": true
17− },
18− {
19− "description": "with dependency",
20− "data": {"foo": 1, "bar": 2},
21− "valid": true
22− },
23− {
24− "description": "missing dependency",
25− "data": {"bar": 2},
26− "valid": false
27− },
28− {
29− "description": "ignores non-objects",
30− "data": "foo",
31− "valid": true
32− }
33− ]
34− },
35− {
36− "description": "dependencies with empty array",
37− "schema": {
38− "dependencies": {"bar": []}
39− },
40− "tests": [
41− {
42− "description": "empty object",
43− "data": {},
44− "valid": true
45− },
46− {
47− "description": "object with one property",
48− "data": {"bar": 2},
49− "valid": true
50− }
51− ]
52− },
53− {
54− "description": "multiple dependencies",
55− "schema": {
56− "dependencies": {"quux": ["foo", "bar"]}
57− },
58− "tests": [
59− {
60− "description": "neither",
61− "data": {},
62− "valid": true
63− },
64− {
65− "description": "nondependants",
66− "data": {"foo": 1, "bar": 2},
67− "valid": true
68− },
69− {
70− "description": "with dependencies",
71− "data": {"foo": 1, "bar": 2, "quux": 3},
72− "valid": true
73− },
74− {
75− "description": "missing dependency",
76− "data": {"foo": 1, "quux": 2},
77− "valid": false
78− },
79− {
80− "description": "missing other dependency",
81− "data": {"bar": 1, "quux": 2},
82− "valid": false
83− },
84− {
85− "description": "missing both dependencies",
86− "data": {"quux": 1},
87− "valid": false
88− }
89− ]
90− },
91− {
92− "description": "multiple dependencies subschema",
93− "schema": {
94− "dependencies": {
95− "bar": {
96− "properties": {
97− "foo": {"type": "integer"},
98− "bar": {"type": "integer"}
99− }
100− }
101− }
102− },
103− "tests": [
104− {
105− "description": "valid",
106− "data": {"foo": 1, "bar": 2},
107− "valid": true
108− },
109− {
110− "description": "no dependency",
111− "data": {"foo": "quux"},
112− "valid": true
113− },
114− {
115− "description": "wrong type",
116− "data": {"foo": "quux", "bar": 2},
117− "valid": false
118− },
119− {
120− "description": "wrong type other",
121− "data": {"foo": 2, "bar": "quux"},
122− "valid": false
123− },
124− {
125− "description": "wrong type both",
126− "data": {"foo": "quux", "bar": "quux"},
127− "valid": false
128− }
129− ]
130− },
131− {
132− "description": "dependencies with boolean subschemas",
133− "schema": {
134− "dependencies": {
135− "foo": true,
136− "bar": false
137− }
138− },
139− "tests": [
140− {
141− "description": "object with property having schema true is valid",
142− "data": {"foo": 1},
143− "valid": true
144− },
145− {
146− "description": "object with property having schema false is invalid",
147− "data": {"bar": 2},
148− "valid": false
149− },
150− {
151− "description": "object with both properties is invalid",
152− "data": {"foo": 1, "bar": 2},
153− "valid": false
154− },
155− {
156− "description": "empty object is valid",
157− "data": {},
158− "valid": true
159− }
160− ]
161− }
162−]
JSON-Schema-Test-Suite/tests/draft6/enum.jsondeleted−72
@@ -1,72 +0,0 @@
1−[
2− {
3− "description": "simple enum validation",
4− "schema": {"enum": [1, 2, 3]},
5− "tests": [
6− {
7− "description": "one of the enum is valid",
8− "data": 1,
9− "valid": true
10− },
11− {
12− "description": "something else is invalid",
13− "data": 4,
14− "valid": false
15− }
16− ]
17− },
18− {
19− "description": "heterogeneous enum validation",
20− "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
21− "tests": [
22− {
23− "description": "one of the enum is valid",
24− "data": [],
25− "valid": true
26− },
27− {
28− "description": "something else is invalid",
29− "data": null,
30− "valid": false
31− },
32− {
33− "description": "objects are deep compared",
34− "data": {"foo": false},
35− "valid": false
36− }
37− ]
38− },
39− {
40− "description": "enums in properties",
41− "schema": {
42− "type":"object",
43− "properties": {
44− "foo": {"enum":["foo"]},
45− "bar": {"enum":["bar"]}
46− },
47− "required": ["bar"]
48− },
49− "tests": [
50− {
51− "description": "both properties are valid",
52− "data": {"foo":"foo", "bar":"bar"},
53− "valid": true
54− },
55− {
56− "description": "missing optional property is valid",
57− "data": {"bar":"bar"},
58− "valid": true
59− },
60− {
61− "description": "missing required property is invalid",
62− "data": {"foo":"foo"},
63− "valid": false
64− },
65− {
66− "description": "missing all properties is invalid",
67− "data": {},
68− "valid": false
69− }
70− ]
71− }
72−]
JSON-Schema-Test-Suite/tests/draft6/exclusiveMaximum.jsondeleted−30
@@ -1,30 +0,0 @@
1−[
2− {
3− "description": "exclusiveMaximum validation",
4− "schema": {
5− "exclusiveMaximum": 3.0
6− },
7− "tests": [
8− {
9− "description": "below the exclusiveMaximum is valid",
10− "data": 2.2,
11− "valid": true
12− },
13− {
14− "description": "boundary point is invalid",
15− "data": 3.0,
16− "valid": false
17− },
18− {
19− "description": "above the exclusiveMaximum is invalid",
20− "data": 3.5,
21− "valid": false
22− },
23− {
24− "description": "ignores non-numbers",
25− "data": "x",
26− "valid": true
27− }
28− ]
29− }
30−]
JSON-Schema-Test-Suite/tests/draft6/exclusiveMinimum.jsondeleted−30
@@ -1,30 +0,0 @@
1−[
2− {
3− "description": "exclusiveMinimum validation",
4− "schema": {
5− "exclusiveMinimum": 1.1
6− },
7− "tests": [
8− {
9− "description": "above the exclusiveMinimum is valid",
10− "data": 1.2,
11− "valid": true
12− },
13− {
14− "description": "boundary point is invalid",
15− "data": 1.1,
16− "valid": false
17− },
18− {
19− "description": "below the exclusiveMinimum is invalid",
20− "data": 0.6,
21− "valid": false
22− },
23− {
24− "description": "ignores non-numbers",
25− "data": "x",
26− "valid": true
27− }
28− ]
29− }
30−]
JSON-Schema-Test-Suite/tests/draft6/items.jsondeleted−133
@@ -1,133 +0,0 @@
1−[
2− {
3− "description": "a schema given for items",
4− "schema": {
5− "items": {"type": "integer"}
6− },
7− "tests": [
8− {
9− "description": "valid items",
10− "data": [ 1, 2, 3 ],
11− "valid": true
12− },
13− {
14− "description": "wrong type of items",
15− "data": [1, "x"],
16− "valid": false
17− },
18− {
19− "description": "ignores non-arrays",
20− "data": {"foo" : "bar"},
21− "valid": true
22− },
23− {
24− "description": "JavaScript pseudo-array is valid",
25− "data": {
26− "0": "invalid",
27− "length": 1
28− },
29− "valid": true
30− }
31− ]
32− },
33− {
34− "description": "an array of schemas for items",
35− "schema": {
36− "items": [
37− {"type": "integer"},
38− {"type": "string"}
39− ]
40− },
41− "tests": [
42− {
43− "description": "correct types",
44− "data": [ 1, "foo" ],
45− "valid": true
46− },
47− {
48− "description": "wrong types",
49− "data": [ "foo", 1 ],
50− "valid": false
51− },
52− {
53− "description": "incomplete array of items",
54− "data": [ 1 ],
55− "valid": true
56− },
57− {
58− "description": "array with additional items",
59− "data": [ 1, "foo", true ],
60− "valid": true
61− },
62− {
63− "description": "empty array",
64− "data": [ ],
65− "valid": true
66− },
67− {
68− "description": "JavaScript pseudo-array is valid",
69− "data": {
70− "0": "invalid",
71− "1": "valid",
72− "length": 2
73− },
74− "valid": true
75− }
76− ]
77− },
78− {
79− "description": "items with boolean schema (true)",
80− "schema": {"items": true},
81− "tests": [
82− {
83− "description": "any array is valid",
84− "data": [ 1, "foo", true ],
85− "valid": true
86− },
87− {
88− "description": "empty array is valid",
89− "data": [],
90− "valid": true
91− }
92− ]
93− },
94− {
95− "description": "items with boolean schema (false)",
96− "schema": {"items": false},
97− "tests": [
98− {
99− "description": "any non-empty array is invalid",
100− "data": [ 1, "foo", true ],
101− "valid": false
102− },
103− {
104− "description": "empty array is valid",
105− "data": [],
106− "valid": true
107− }
108− ]
109− },
110− {
111− "description": "items with boolean schemas",
112− "schema": {
113− "items": [true, false]
114− },
115− "tests": [
116− {
117− "description": "array with one item is valid",
118− "data": [ 1 ],
119− "valid": true
120− },
121− {
122− "description": "array with two items is invalid",
123− "data": [ 1, "foo" ],
124− "valid": false
125− },
126− {
127− "description": "empty array is valid",
128− "data": [],
129− "valid": true
130− }
131− ]
132− }
133−]
JSON-Schema-Test-Suite/tests/draft6/maxItems.jsondeleted−28
@@ -1,28 +0,0 @@
1−[
2− {
3− "description": "maxItems validation",
4− "schema": {"maxItems": 2},
5− "tests": [
6− {
7− "description": "shorter is valid",
8− "data": [1],
9− "valid": true
10− },
11− {
12− "description": "exact length is valid",
13− "data": [1, 2],
14− "valid": true
15− },
16− {
17− "description": "too long is invalid",
18− "data": [1, 2, 3],
19− "valid": false
20− },
21− {
22− "description": "ignores non-arrays",
23− "data": "foobar",
24− "valid": true
25− }
26− ]
27− }
28−]
JSON-Schema-Test-Suite/tests/draft6/maxLength.jsondeleted−33
@@ -1,33 +0,0 @@
1−[
2− {
3− "description": "maxLength validation",
4− "schema": {"maxLength": 2},
5− "tests": [
6− {
7− "description": "shorter is valid",
8− "data": "f",
9− "valid": true
10− },
11− {
12− "description": "exact length is valid",
13− "data": "fo",
14− "valid": true
15− },
16− {
17− "description": "too long is invalid",
18− "data": "foo",
19− "valid": false
20− },
21− {
22− "description": "ignores non-strings",
23− "data": 100,
24− "valid": true
25− },
26− {
27− "description": "two supplementary Unicode code points is long enough",
28− "data": "\uD83D\uDCA9\uD83D\uDCA9",
29− "valid": true
30− }
31− ]
32− }
33−]
JSON-Schema-Test-Suite/tests/draft6/maxProperties.jsondeleted−28
@@ -1,28 +0,0 @@
1−[
2− {
3− "description": "maxProperties validation",
4− "schema": {"maxProperties": 2},
5− "tests": [
6− {
7− "description": "shorter is valid",
8− "data": {"foo": 1},
9− "valid": true
10− },
11− {
12− "description": "exact length is valid",
13− "data": {"foo": 1, "bar": 2},
14− "valid": true
15− },
16− {
17− "description": "too long is invalid",
18− "data": {"foo": 1, "bar": 2, "baz": 3},
19− "valid": false
20− },
21− {
22− "description": "ignores non-objects",
23− "data": "foobar",
24− "valid": true
25− }
26− ]
27− }
28−]
JSON-Schema-Test-Suite/tests/draft6/maximum.jsondeleted−28
@@ -1,28 +0,0 @@
1−[
2− {
3− "description": "maximum validation",
4− "schema": {"maximum": 3.0},
5− "tests": [
6− {
7− "description": "below the maximum is valid",
8− "data": 2.6,
9− "valid": true
10− },
11− {
12− "description": "boundary point is valid",
13− "data": 3.0,
14− "valid": true
15− },
16− {
17− "description": "above the maximum is invalid",
18− "data": 3.5,
19− "valid": false
20− },
21− {
22− "description": "ignores non-numbers",
23− "data": "x",
24− "valid": true
25− }
26− ]
27− }
28−]
JSON-Schema-Test-Suite/tests/draft6/minItems.jsondeleted−28
@@ -1,28 +0,0 @@
1−[
2− {
3− "description": "minItems validation",
4− "schema": {"minItems": 1},
5− "tests": [
6− {
7− "description": "longer is valid",
8− "data": [1, 2],
9− "valid": true
10− },
11− {
12− "description": "exact length is valid",
13− "data": [1],
14− "valid": true
15− },
16− {
17− "description": "too short is invalid",
18− "data": [],
19− "valid": false
20− },
21− {
22− "description": "ignores non-arrays",
23− "data": "",
24− "valid": true
25− }
26− ]
27− }
28−]
JSON-Schema-Test-Suite/tests/draft6/minLength.jsondeleted−33
@@ -1,33 +0,0 @@
1−[
2− {
3− "description": "minLength validation",
4− "schema": {"minLength": 2},
5− "tests": [
6− {
7− "description": "longer is valid",
8− "data": "foo",
9− "valid": true
10− },
11− {
12− "description": "exact length is valid",
13− "data": "fo",
14− "valid": true
15− },
16− {
17− "description": "too short is invalid",
18− "data": "f",
19− "valid": false
20− },
21− {
22− "description": "ignores non-strings",
23− "data": 1,
24− "valid": true
25− },
26− {
27− "description": "one supplementary Unicode code point is not long enough",
28− "data": "\uD83D\uDCA9",
29− "valid": false
30− }
31− ]
32− }
33−]
JSON-Schema-Test-Suite/tests/draft6/minProperties.jsondeleted−28
@@ -1,28 +0,0 @@
1−[
2− {
3− "description": "minProperties validation",
4− "schema": {"minProperties": 1},
5− "tests": [
6− {
7− "description": "longer is valid",
8− "data": {"foo": 1, "bar": 2},
9− "valid": true
10− },
11− {
12− "description": "exact length is valid",
13− "data": {"foo": 1},
14− "valid": true
15− },
16− {
17− "description": "too short is invalid",
18− "data": {},
19− "valid": false
20− },
21− {
22− "description": "ignores non-objects",
23− "data": "",
24− "valid": true
25− }
26− ]
27− }
28−]
JSON-Schema-Test-Suite/tests/draft6/minimum.jsondeleted−28
@@ -1,28 +0,0 @@
1−[
2− {
3− "description": "minimum validation",
4− "schema": {"minimum": 1.1},
5− "tests": [
6− {
7− "description": "above the minimum is valid",
8− "data": 2.6,
9− "valid": true
10− },
11− {
12− "description": "boundary point is valid",
13− "data": 1.1,
14− "valid": true
15− },
16− {
17− "description": "below the minimum is invalid",
18− "data": 0.6,
19− "valid": false
20− },
21− {
22− "description": "ignores non-numbers",
23− "data": "x",
24− "valid": true
25− }
26− ]
27− }
28−]
JSON-Schema-Test-Suite/tests/draft6/multipleOf.jsondeleted−60
@@ -1,60 +0,0 @@
1−[
2− {
3− "description": "by int",
4− "schema": {"multipleOf": 2},
5− "tests": [
6− {
7− "description": "int by int",
8− "data": 10,
9− "valid": true
10− },
11− {
12− "description": "int by int fail",
13− "data": 7,
14− "valid": false
15− },
16− {
17− "description": "ignores non-numbers",
18− "data": "foo",
19− "valid": true
20− }
21− ]
22− },
23− {
24− "description": "by number",
25− "schema": {"multipleOf": 1.5},
26− "tests": [
27− {
28− "description": "zero is multiple of anything",
29− "data": 0,
30− "valid": true
31− },
32− {
33− "description": "4.5 is multiple of 1.5",
34− "data": 4.5,
35− "valid": true
36− },
37− {
38− "description": "35 is not multiple of 1.5",
39− "data": 35,
40− "valid": false
41− }
42− ]
43− },
44− {
45− "description": "by small number",
46− "schema": {"multipleOf": 0.0001},
47− "tests": [
48− {
49− "description": "0.0075 is multiple of 0.0001",
50− "data": 0.0075,
51− "valid": true
52− },
53− {
54− "description": "0.00751 is not multiple of 0.0001",
55− "data": 0.00751,
56− "valid": false
57− }
58− ]
59− }
60−]
JSON-Schema-Test-Suite/tests/draft6/not.jsondeleted−117
@@ -1,117 +0,0 @@
1−[
2− {
3− "description": "not",
4− "schema": {
5− "not": {"type": "integer"}
6− },
7− "tests": [
8− {
9− "description": "allowed",
10− "data": "foo",
11− "valid": true
12− },
13− {
14− "description": "disallowed",
15− "data": 1,
16− "valid": false
17− }
18− ]
19− },
20− {
21− "description": "not multiple types",
22− "schema": {
23− "not": {"type": ["integer", "boolean"]}
24− },
25− "tests": [
26− {
27− "description": "valid",
28− "data": "foo",
29− "valid": true
30− },
31− {
32− "description": "mismatch",
33− "data": 1,
34− "valid": false
35− },
36− {
37− "description": "other mismatch",
38− "data": true,
39− "valid": false
40− }
41− ]
42− },
43− {
44− "description": "not more complex schema",
45− "schema": {
46− "not": {
47− "type": "object",
48− "properties": {
49− "foo": {
50− "type": "string"
51− }
52− }
53− }
54− },
55− "tests": [
56− {
57− "description": "match",
58− "data": 1,
59− "valid": true
60− },
61− {
62− "description": "other match",
63− "data": {"foo": 1},
64− "valid": true
65− },
66− {
67− "description": "mismatch",
68− "data": {"foo": "bar"},
69− "valid": false
70− }
71− ]
72− },
73− {
74− "description": "forbidden property",
75− "schema": {
76− "properties": {
77− "foo": {
78− "not": {}
79− }
80− }
81− },
82− "tests": [
83− {
84− "description": "property present",
85− "data": {"foo": 1, "bar": 2},
86− "valid": false
87− },
88− {
89− "description": "property absent",
90− "data": {"bar": 1, "baz": 2},
91− "valid": true
92− }
93− ]
94− },
95− {
96− "description": "not with boolean schema true",
97− "schema": {"not": true},
98− "tests": [
99− {
100− "description": "any value is invalid",
101− "data": "foo",
102− "valid": false
103− }
104− ]
105− },
106− {
107− "description": "not with boolean schema false",
108− "schema": {"not": false},
109− "tests": [
110− {
111− "description": "any value is valid",
112− "data": "foo",
113− "valid": true
114− }
115− ]
116− }
117−]
JSON-Schema-Test-Suite/tests/draft6/oneOf.jsondeleted−112
@@ -1,112 +0,0 @@
1−[
2− {
3− "description": "oneOf",
4− "schema": {
5− "oneOf": [
6− {
7− "type": "integer"
8− },
9− {
10− "minimum": 2
11− }
12− ]
13− },
14− "tests": [
15− {
16− "description": "first oneOf valid",
17− "data": 1,
18− "valid": true
19− },
20− {
21− "description": "second oneOf valid",
22− "data": 2.5,
23− "valid": true
24− },
25− {
26− "description": "both oneOf valid",
27− "data": 3,
28− "valid": false
29− },
30− {
31− "description": "neither oneOf valid",
32− "data": 1.5,
33− "valid": false
34− }
35− ]
36− },
37− {
38− "description": "oneOf with base schema",
39− "schema": {
40− "type": "string",
41− "oneOf" : [
42− {
43− "minLength": 2
44− },
45− {
46− "maxLength": 4
47− }
48− ]
49− },
50− "tests": [
51− {
52− "description": "mismatch base schema",
53− "data": 3,
54− "valid": false
55− },
56− {
57− "description": "one oneOf valid",
58− "data": "foobar",
59− "valid": true
60− },
61− {
62− "description": "both oneOf valid",
63− "data": "foo",
64− "valid": false
65− }
66− ]
67− },
68− {
69− "description": "oneOf with boolean schemas, all true",
70− "schema": {"oneOf": [true, true, true]},
71− "tests": [
72− {
73− "description": "any value is invalid",
74− "data": "foo",
75− "valid": false
76− }
77− ]
78− },
79− {
80− "description": "oneOf with boolean schemas, one true",
81− "schema": {"oneOf": [true, false, false]},
82− "tests": [
83− {
84− "description": "any value is valid",
85− "data": "foo",
86− "valid": true
87− }
88− ]
89− },
90− {
91− "description": "oneOf with boolean schemas, more than one true",
92− "schema": {"oneOf": [true, true, false]},
93− "tests": [
94− {
95− "description": "any value is invalid",
96− "data": "foo",
97− "valid": false
98− }
99− ]
100− },
101− {
102− "description": "oneOf with boolean schemas, all false",
103− "schema": {"oneOf": [false, false, false]},
104− "tests": [
105− {
106− "description": "any value is invalid",
107− "data": "foo",
108− "valid": false
109− }
110− ]
111− }
112−]
JSON-Schema-Test-Suite/tests/draft6/optional/bignum.jsondeleted−105
@@ -1,105 +0,0 @@
1−[
2− {
3− "description": "integer",
4− "schema": {"type": "integer"},
5− "tests": [
6− {
7− "description": "a bignum is an integer",
8− "data": 12345678910111213141516171819202122232425262728293031,
9− "valid": true
10− }
11− ]
12− },
13− {
14− "description": "number",
15− "schema": {"type": "number"},
16− "tests": [
17− {
18− "description": "a bignum is a number",
19− "data": 98249283749234923498293171823948729348710298301928331,
20− "valid": true
21− }
22− ]
23− },
24− {
25− "description": "integer",
26− "schema": {"type": "integer"},
27− "tests": [
28− {
29− "description": "a negative bignum is an integer",
30− "data": -12345678910111213141516171819202122232425262728293031,
31− "valid": true
32− }
33− ]
34− },
35− {
36− "description": "number",
37− "schema": {"type": "number"},
38− "tests": [
39− {
40− "description": "a negative bignum is a number",
41− "data": -98249283749234923498293171823948729348710298301928331,
42− "valid": true
43− }
44− ]
45− },
46− {
47− "description": "string",
48− "schema": {"type": "string"},
49− "tests": [
50− {
51− "description": "a bignum is not a string",
52− "data": 98249283749234923498293171823948729348710298301928331,
53− "valid": false
54− }
55− ]
56− },
57− {
58− "description": "integer comparison",
59− "schema": {"maximum": 18446744073709551615},
60− "tests": [
61− {
62− "description": "comparison works for high numbers",
63− "data": 18446744073709551600,
64− "valid": true
65− }
66− ]
67− },
68− {
69− "description": "float comparison with high precision",
70− "schema": {
71− "exclusiveMaximum": 972783798187987123879878123.18878137
72− },
73− "tests": [
74− {
75− "description": "comparison works for high numbers",
76− "data": 972783798187987123879878123.188781371,
77− "valid": false
78− }
79− ]
80− },
81− {
82− "description": "integer comparison",
83− "schema": {"minimum": -18446744073709551615},
84− "tests": [
85− {
86− "description": "comparison works for very negative numbers",
87− "data": -18446744073709551600,
88− "valid": true
89− }
90− ]
91− },
92− {
93− "description": "float comparison with high precision on negative numbers",
94− "schema": {
95− "exclusiveMinimum": -972783798187987123879878123.18878137
96− },
97− "tests": [
98− {
99− "description": "comparison works for very negative numbers",
100− "data": -972783798187987123879878123.188781371,
101− "valid": false
102− }
103− ]
104− }
105−]
JSON-Schema-Test-Suite/tests/draft6/optional/ecmascript-regex.jsondeleted−13
@@ -1,13 +0,0 @@
1−[
2− {
3− "description": "ECMA 262 regex non-compliance",
4− "schema": { "format": "regex" },
5− "tests": [
6− {
7− "description": "ECMA 262 has no support for \\Z anchor from .NET",
8− "data": "^\\S(|(.|\\n)*\\S)\\Z",
9− "valid": false
10− }
11− ]
12− }
13−]
JSON-Schema-Test-Suite/tests/draft6/optional/format.jsondeleted−148
@@ -1,148 +0,0 @@
1−[
2− {
3− "description": "validation of date-time strings",
4− "schema": {"format": "date-time"},
5− "tests": [
6− {
7− "description": "a valid date-time string",
8− "data": "1963-06-19T08:30:06.283185Z",
9− "valid": true
10− },
11− {
12− "description": "an invalid date-time string",
13− "data": "06/19/1963 08:30:06 PST",
14− "valid": false
15− },
16− {
17− "description": "only RFC3339 not all of ISO 8601 are valid",
18− "data": "2013-350T01:01:01",
19− "valid": false
20− }
21− ]
22− },
23− {
24− "description": "validation of URIs",
25− "schema": {"format": "uri"},
26− "tests": [
27− {
28− "description": "a valid URI",
29− "data": "http://foo.bar/?baz=qux#quux",
30− "valid": true
31− },
32− {
33− "description": "an invalid protocol-relative URI Reference",
34− "data": "//foo.bar/?baz=qux#quux",
35− "valid": false
36− },
37− {
38− "description": "an invalid URI",
39− "data": "\\\\WINDOWS\\fileshare",
40− "valid": false
41− },
42− {
43− "description": "an invalid URI though valid URI reference",
44− "data": "abc",
45− "valid": false
46− }
47− ]
48− },
49− {
50− "description": "validation of e-mail addresses",
51− "schema": {"format": "email"},
52− "tests": [
53− {
54− "description": "a valid e-mail address",
55− "data": "joe.bloggs@example.com",
56− "valid": true
57− },
58− {
59− "description": "an invalid e-mail address",
60− "data": "2962",
61− "valid": false
62− }
63− ]
64− },
65− {
66− "description": "validation of IP addresses",
67− "schema": {"format": "ipv4"},
68− "tests": [
69− {
70− "description": "a valid IP address",
71− "data": "192.168.0.1",
72− "valid": true
73− },
74− {
75− "description": "an IP address with too many components",
76− "data": "127.0.0.0.1",
77− "valid": false
78− },
79− {
80− "description": "an IP address with out-of-range values",
81− "data": "256.256.256.256",
82− "valid": false
83− },
84− {
85− "description": "an IP address without 4 components",
86− "data": "127.0",
87− "valid": false
88− },
89− {
90− "description": "an IP address as an integer",
91− "data": "0x7f000001",
92− "valid": false
93− }
94− ]
95− },
96− {
97− "description": "validation of IPv6 addresses",
98− "schema": {"format": "ipv6"},
99− "tests": [
100− {
101− "description": "a valid IPv6 address",
102− "data": "::1",
103− "valid": true
104− },
105− {
106− "description": "an IPv6 address with out-of-range values",
107− "data": "12345::",
108− "valid": false
109− },
110− {
111− "description": "an IPv6 address with too many components",
112− "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
113− "valid": false
114− },
115− {
116− "description": "an IPv6 address containing illegal characters",
117− "data": "::laptop",
118− "valid": false
119− }
120− ]
121− },
122− {
123− "description": "validation of host names",
124− "schema": {"format": "hostname"},
125− "tests": [
126− {
127− "description": "a valid host name",
128− "data": "www.example.com",
129− "valid": true
130− },
131− {
132− "description": "a host name starting with an illegal character",
133− "data": "-a-host-name-that-starts-with--",
134− "valid": false
135− },
136− {
137− "description": "a host name containing illegal characters",
138− "data": "not_a_valid_host_name",
139− "valid": false
140− },
141− {
142− "description": "a host name with a component too long",
143− "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
144− "valid": false
145− }
146− ]
147− }
148−]
JSON-Schema-Test-Suite/tests/draft6/optional/zeroTerminatedFloats.jsondeleted−15
@@ -1,15 +0,0 @@
1−[
2− {
3− "description": "some languages do not distinguish between different types of numeric value",
4− "schema": {
5− "type": "integer"
6− },
7− "tests": [
8− {
9− "description": "a float is not an integer even without fractional part",
10− "data": 1.0,
11− "valid": true
12− }
13− ]
14− }
15−]
JSON-Schema-Test-Suite/tests/draft6/pattern.jsondeleted−34
@@ -1,34 +0,0 @@
1−[
2− {
3− "description": "pattern validation",
4− "schema": {"pattern": "^a*$"},
5− "tests": [
6− {
7− "description": "a matching pattern is valid",
8− "data": "aaa",
9− "valid": true
10− },
11− {
12− "description": "a non-matching pattern is invalid",
13− "data": "abc",
14− "valid": false
15− },
16− {
17− "description": "ignores non-strings",
18− "data": true,
19− "valid": true
20− }
21− ]
22− },
23− {
24− "description": "pattern is not anchored",
25− "schema": {"pattern": "a+"},
26− "tests": [
27− {
28− "description": "matches a substring",
29− "data": "xxaayy",
30− "valid": true
31− }
32− ]
33− }
34−]
JSON-Schema-Test-Suite/tests/draft6/patternProperties.jsondeleted−141
@@ -1,141 +0,0 @@
1−[
2− {
3− "description":
4− "patternProperties validates properties matching a regex",
5− "schema": {
6− "patternProperties": {
7− "f.*o": {"type": "integer"}
8− }
9− },
10− "tests": [
11− {
12− "description": "a single valid match is valid",
13− "data": {"foo": 1},
14− "valid": true
15− },
16− {
17− "description": "multiple valid matches is valid",
18− "data": {"foo": 1, "foooooo" : 2},
19− "valid": true
20− },
21− {
22− "description": "a single invalid match is invalid",
23− "data": {"foo": "bar", "fooooo": 2},
24− "valid": false
25− },
26− {
27− "description": "multiple invalid matches is invalid",
28− "data": {"foo": "bar", "foooooo" : "baz"},
29− "valid": false
30− },
31− {
32− "description": "ignores non-objects",
33− "data": 12,
34− "valid": true
35− }
36− ]
37− },
38− {
39− "description": "multiple simultaneous patternProperties are validated",
40− "schema": {
41− "patternProperties": {
42− "a*": {"type": "integer"},
43− "aaa*": {"maximum": 20}
44− }
45− },
46− "tests": [
47− {
48− "description": "a single valid match is valid",
49− "data": {"a": 21},
50− "valid": true
51− },
52− {
53− "description": "a simultaneous match is valid",
54− "data": {"aaaa": 18},
55− "valid": true
56− },
57− {
58− "description": "multiple matches is valid",
59− "data": {"a": 21, "aaaa": 18},
60− "valid": true
61− },
62− {
63− "description": "an invalid due to one is invalid",
64− "data": {"a": "bar"},
65− "valid": false
66− },
67− {
68− "description": "an invalid due to the other is invalid",
69− "data": {"aaaa": 31},
70− "valid": false
71− },
72− {
73− "description": "an invalid due to both is invalid",
74− "data": {"aaa": "foo", "aaaa": 31},
75− "valid": false
76− }
77− ]
78− },
79− {
80− "description": "regexes are not anchored by default and are case sensitive",
81− "schema": {
82− "patternProperties": {
83− "[0-9]{2,}": { "type": "boolean" },
84− "X_": { "type": "string" }
85− }
86− },
87− "tests": [
88− {
89− "description": "non recognized members are ignored",
90− "data": { "answer 1": "42" },
91− "valid": true
92− },
93− {
94− "description": "recognized members are accounted for",
95− "data": { "a31b": null },
96− "valid": false
97− },
98− {
99− "description": "regexes are case sensitive",
100− "data": { "a_x_3": 3 },
101− "valid": true
102− },
103− {
104− "description": "regexes are case sensitive, 2",
105− "data": { "a_X_3": 3 },
106− "valid": false
107− }
108− ]
109− },
110− {
111− "description": "patternProperties with boolean schemas",
112− "schema": {
113− "patternProperties": {
114− "f.*": true,
115− "b.*": false
116− }
117− },
118− "tests": [
119− {
120− "description": "object with property matching schema true is valid",
121− "data": {"foo": 1},
122− "valid": true
123− },
124− {
125− "description": "object with property matching schema false is invalid",
126− "data": {"bar": 2},
127− "valid": false
128− },
129− {
130− "description": "object with both properties is invalid",
131− "data": {"foo": 1, "bar": 2},
132− "valid": false
133− },
134− {
135− "description": "empty object is valid",
136− "data": {},
137− "valid": true
138− }
139− ]
140− }
141−]
JSON-Schema-Test-Suite/tests/draft6/properties.jsondeleted−123
@@ -1,123 +0,0 @@
1−[
2− {
3− "description": "object properties validation",
4− "schema": {
5− "properties": {
6− "foo": {"type": "integer"},
7− "bar": {"type": "string"}
8− }
9− },
10− "tests": [
11− {
12− "description": "both properties present and valid is valid",
13− "data": {"foo": 1, "bar": "baz"},
14− "valid": true
15− },
16− {
17− "description": "one property invalid is invalid",
18− "data": {"foo": 1, "bar": {}},
19− "valid": false
20− },
21− {
22− "description": "both properties invalid is invalid",
23− "data": {"foo": [], "bar": {}},
24− "valid": false
25− },
26− {
27− "description": "doesn't invalidate other properties",
28− "data": {"quux": []},
29− "valid": true
30− },
31− {
32− "description": "ignores non-objects",
33− "data": [],
34− "valid": true
35− }
36− ]
37− },
38− {
39− "description":
40− "properties, patternProperties, additionalProperties interaction",
41− "schema": {
42− "properties": {
43− "foo": {"type": "array", "maxItems": 3},
44− "bar": {"type": "array"}
45− },
46− "patternProperties": {"f.o": {"minItems": 2}},
47− "additionalProperties": {"type": "integer"}
48− },
49− "tests": [
50− {
51− "description": "property validates property",
52− "data": {"foo": [1, 2]},
53− "valid": true
54− },
55− {
56− "description": "property invalidates property",
57− "data": {"foo": [1, 2, 3, 4]},
58− "valid": false
59− },
60− {
61− "description": "patternProperty invalidates property",
62− "data": {"foo": []},
63− "valid": false
64− },
65− {
66− "description": "patternProperty validates nonproperty",
67− "data": {"fxo": [1, 2]},
68− "valid": true
69− },
70− {
71− "description": "patternProperty invalidates nonproperty",
72− "data": {"fxo": []},
73− "valid": false
74− },
75− {
76− "description": "additionalProperty ignores property",
77− "data": {"bar": []},
78− "valid": true
79− },
80− {
81− "description": "additionalProperty validates others",
82− "data": {"quux": 3},
83− "valid": true
84− },
85− {
86− "description": "additionalProperty invalidates others",
87− "data": {"quux": "foo"},
88− "valid": false
89− }
90− ]
91− },
92− {
93− "description": "properties with boolean schema",
94− "schema": {
95− "properties": {
96− "foo": true,
97− "bar": false
98− }
99− },
100− "tests": [
101− {
102− "description": "no property present is valid",
103− "data": {},
104− "valid": true
105− },
106− {
107− "description": "only 'true' property present is valid",
108− "data": {"foo": 1},
109− "valid": true
110− },
111− {
112− "description": "only 'false' property present is invalid",
113− "data": {"bar": 2},
114− "valid": false
115− },
116− {
117− "description": "both properties present is invalid",
118− "data": {"foo": 1, "bar": 2},
119− "valid": false
120− }
121− ]
122− }
123−]
JSON-Schema-Test-Suite/tests/draft6/propertyNames.jsondeleted−68
@@ -1,68 +0,0 @@
1−[
2− {
3− "description": "propertyNames validation",
4− "schema": {
5− "propertyNames": {"maxLength": 3}
6− },
7− "tests": [
8− {
9− "description": "all property names valid",
10− "data": {
11− "f": {},
12− "foo": {}
13− },
14− "valid": true
15− },
16− {
17− "description": "some property names invalid",
18− "data": {
19− "foo": {},
20− "foobar": {}
21− },
22− "valid": false
23− },
24− {
25− "description": "object without properties is valid",
26− "data": {},
27− "valid": true
28− },
29− {
30− "description": "non-object is valid",
31− "data": [],
32− "valid": true
33− }
34− ]
35− },
36− {
37− "description": "propertyNames with boolean schema true",
38− "schema": {"propertyNames": true},
39− "tests": [
40− {
41− "description": "object with any properties is valid",
42− "data": {"foo": 1},
43− "valid": true
44− },
45− {
46− "description": "empty object is valid",
47− "data": {},
48− "valid": true
49− }
50− ]
51− },
52− {
53− "description": "propertyNames with boolean schema false",
54− "schema": {"propertyNames": false},
55− "tests": [
56− {
57− "description": "object with any properties is invalid",
58− "data": {"foo": 1},
59− "valid": false
60− },
61− {
62− "description": "empty object is valid",
63− "data": {},
64− "valid": true
65− }
66− ]
67− }
68−]
JSON-Schema-Test-Suite/tests/draft6/ref.jsondeleted−332
@@ -1,332 +0,0 @@
1−[
2− {
3− "description": "root pointer ref",
4− "schema": {
5− "properties": {
6− "foo": {"$ref": "#"}
7− },
8− "additionalProperties": false
9− },
10− "tests": [
11− {
12− "description": "match",
13− "data": {"foo": false},
14− "valid": true
15− },
16− {
17− "description": "recursive match",
18− "data": {"foo": {"foo": false}},
19− "valid": true
20− },
21− {
22− "description": "mismatch",
23− "data": {"bar": false},
24− "valid": false
25− },
26− {
27− "description": "recursive mismatch",
28− "data": {"foo": {"bar": false}},
29− "valid": false
30− }
31− ]
32− },
33− {
34− "description": "relative pointer ref to object",
35− "schema": {
36− "properties": {
37− "foo": {"type": "integer"},
38− "bar": {"$ref": "#/properties/foo"}
39− }
40− },
41− "tests": [
42− {
43− "description": "match",
44− "data": {"bar": 3},
45− "valid": true
46− },
47− {
48− "description": "mismatch",
49− "data": {"bar": true},
50− "valid": false
51− }
52− ]
53− },
54− {
55− "description": "relative pointer ref to array",
56− "schema": {
57− "items": [
58− {"type": "integer"},
59− {"$ref": "#/items/0"}
60− ]
61− },
62− "tests": [
63− {
64− "description": "match array",
65− "data": [1, 2],
66− "valid": true
67− },
68− {
69− "description": "mismatch array",
70− "data": [1, "foo"],
71− "valid": false
72− }
73− ]
74− },
75− {
76− "description": "escaped pointer ref",
77− "schema": {
78− "tilda~field": {"type": "integer"},
79− "slash/field": {"type": "integer"},
80− "percent%field": {"type": "integer"},
81− "properties": {
82− "tilda": {"$ref": "#/tilda~0field"},
83− "slash": {"$ref": "#/slash~1field"},
84− "percent": {"$ref": "#/percent%25field"}
85− }
86− },
87− "tests": [
88− {
89− "description": "slash invalid",
90− "data": {"slash": "aoeu"},
91− "valid": false
92− },
93− {
94− "description": "tilda invalid",
95− "data": {"tilda": "aoeu"},
96− "valid": false
97− },
98− {
99− "description": "percent invalid",
100− "data": {"percent": "aoeu"},
101− "valid": false
102− },
103− {
104− "description": "slash valid",
105− "data": {"slash": 123},
106− "valid": true
107− },
108− {
109− "description": "tilda valid",
110− "data": {"tilda": 123},
111− "valid": true
112− },
113− {
114− "description": "percent valid",
115− "data": {"percent": 123},
116− "valid": true
117− }
118− ]
119− },
120− {
121− "description": "nested refs",
122− "schema": {
123− "definitions": {
124− "a": {"type": "integer"},
125− "b": {"$ref": "#/definitions/a"},
126− "c": {"$ref": "#/definitions/b"}
127− },
128− "$ref": "#/definitions/c"
129− },
130− "tests": [
131− {
132− "description": "nested ref valid",
133− "data": 5,
134− "valid": true
135− },
136− {
137− "description": "nested ref invalid",
138− "data": "a",
139− "valid": false
140− }
141− ]
142− },
143− {
144− "description": "ref overrides any sibling keywords",
145− "schema": {
146− "definitions": {
147− "reffed": {
148− "type": "array"
149− }
150− },
151− "properties": {
152− "foo": {
153− "$ref": "#/definitions/reffed",
154− "maxItems": 2
155− }
156− }
157− },
158− "tests": [
159− {
160− "description": "ref valid",
161− "data": { "foo": [] },
162− "valid": true
163− },
164− {
165− "description": "ref valid, maxItems ignored",
166− "data": { "foo": [ 1, 2, 3] },
167− "valid": true
168− },
169− {
170− "description": "ref invalid",
171− "data": { "foo": "string" },
172− "valid": false
173− }
174− ]
175− },
176− {
177− "description": "remote ref, containing refs itself",
178− "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
179− "tests": [
180− {
181− "description": "remote ref valid",
182− "data": {"minLength": 1},
183− "valid": true
184− },
185− {
186− "description": "remote ref invalid",
187− "data": {"minLength": -1},
188− "valid": false
189− }
190− ]
191− },
192− {
193− "description": "property named $ref that is not a reference",
194− "schema": {
195− "properties": {
196− "$ref": {"type": "string"}
197− }
198− },
199− "tests": [
200− {
201− "description": "property named $ref valid",
202− "data": {"$ref": "a"},
203− "valid": true
204− },
205− {
206− "description": "property named $ref invalid",
207− "data": {"$ref": 2},
208− "valid": false
209− }
210− ]
211− },
212− {
213− "description": "$ref to boolean schema true",
214− "schema": {
215− "$ref": "#/definitions/bool",
216− "definitions": {
217− "bool": true
218− }
219− },
220− "tests": [
221− {
222− "description": "any value is valid",
223− "data": "foo",
224− "valid": true
225− }
226− ]
227− },
228− {
229− "description": "$ref to boolean schema false",
230− "schema": {
231− "$ref": "#/definitions/bool",
232− "definitions": {
233− "bool": false
234− }
235− },
236− "tests": [
237− {
238− "description": "any value is invalid",
239− "data": "foo",
240− "valid": false
241− }
242− ]
243− },
244− {
245− "description": "Recursive references between schemas",
246− "schema": {
247− "$id": "http://localhost:1234/tree",
248− "description": "tree of nodes",
249− "type": "object",
250− "properties": {
251− "meta": {"type": "string"},
252− "nodes": {
253− "type": "array",
254− "items": {"$ref": "node"}
255− }
256− },
257− "required": ["meta", "nodes"],
258− "definitions": {
259− "node": {
260− "$id": "http://localhost:1234/node",
261− "description": "node",
262− "type": "object",
263− "properties": {
264− "value": {"type": "number"},
265− "subtree": {"$ref": "tree"}
266− },
267− "required": ["value"]
268− }
269− }
270− },
271− "tests": [
272− {
273− "description": "valid tree",
274− "data": {
275− "meta": "root",
276− "nodes": [
277− {
278− "value": 1,
279− "subtree": {
280− "meta": "child",
281− "nodes": [
282− {"value": 1.1},
283− {"value": 1.2}
284− ]
285− }
286− },
287− {
288− "value": 2,
289− "subtree": {
290− "meta": "child",
291− "nodes": [
292− {"value": 2.1},
293− {"value": 2.2}
294− ]
295− }
296− }
297− ]
298− },
299− "valid": true
300− },
301− {
302− "description": "invalid tree",
303− "data": {
304− "meta": "root",
305− "nodes": [
306− {
307− "value": 1,
308− "subtree": {
309− "meta": "child",
310− "nodes": [
311− {"value": "string is invalid"},
312− {"value": 1.2}
313− ]
314− }
315− },
316− {
317− "value": 2,
318− "subtree": {
319− "meta": "child",
320− "nodes": [
321− {"value": 2.1},
322− {"value": 2.2}
323− ]
324− }
325− }
326− ]
327− },
328− "valid": false
329− }
330− ]
331− }
332−]
JSON-Schema-Test-Suite/tests/draft6/refRemote.jsondeleted−171
@@ -1,171 +0,0 @@
1−[
2− {
3− "description": "remote ref",
4− "schema": {"$ref": "http://localhost:1234/integer.json"},
5− "tests": [
6− {
7− "description": "remote ref valid",
8− "data": 1,
9− "valid": true
10− },
11− {
12− "description": "remote ref invalid",
13− "data": "a",
14− "valid": false
15− }
16− ]
17− },
18− {
19− "description": "fragment within remote ref",
20− "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
21− "tests": [
22− {
23− "description": "remote fragment valid",
24− "data": 1,
25− "valid": true
26− },
27− {
28− "description": "remote fragment invalid",
29− "data": "a",
30− "valid": false
31− }
32− ]
33− },
34− {
35− "description": "ref within remote ref",
36− "schema": {
37− "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
38− },
39− "tests": [
40− {
41− "description": "ref within ref valid",
42− "data": 1,
43− "valid": true
44− },
45− {
46− "description": "ref within ref invalid",
47− "data": "a",
48− "valid": false
49− }
50− ]
51− },
52− {
53− "description": "base URI change",
54− "schema": {
55− "$id": "http://localhost:1234/",
56− "items": {
57− "$id": "folder/",
58− "items": {"$ref": "folderInteger.json"}
59− }
60− },
61− "tests": [
62− {
63− "description": "base URI change ref valid",
64− "data": [[1]],
65− "valid": true
66− },
67− {
68− "description": "base URI change ref invalid",
69− "data": [["a"]],
70− "valid": false
71− }
72− ]
73− },
74− {
75− "description": "base URI change - change folder",
76− "schema": {
77− "$id": "http://localhost:1234/scope_change_defs1.json",
78− "type" : "object",
79− "properties": {
80− "list": {"$ref": "#/definitions/baz"}
81− },
82− "definitions": {
83− "baz": {
84− "$id": "folder/",
85− "type": "array",
86− "items": {"$ref": "folderInteger.json"}
87− }
88− }
89− },
90− "tests": [
91− {
92− "description": "number is valid",
93− "data": {"list": [1]},
94− "valid": true
95− },
96− {
97− "description": "string is invalid",
98− "data": {"list": ["a"]},
99− "valid": false
100− }
101− ]
102− },
103− {
104− "description": "base URI change - change folder in subschema",
105− "schema": {
106− "$id": "http://localhost:1234/scope_change_defs2.json",
107− "type" : "object",
108− "properties": {
109− "list": {"$ref": "#/definitions/baz/definitions/bar"}
110− },
111− "definitions": {
112− "baz": {
113− "$id": "folder/",
114− "definitions": {
115− "bar": {
116− "type": "array",
117− "items": {"$ref": "folderInteger.json"}
118− }
119− }
120− }
121− }
122− },
123− "tests": [
124− {
125− "description": "number is valid",
126− "data": {"list": [1]},
127− "valid": true
128− },
129− {
130− "description": "string is invalid",
131− "data": {"list": ["a"]},
132− "valid": false
133− }
134− ]
135− },
136− {
137− "description": "root ref in remote ref",
138− "schema": {
139− "$id": "http://localhost:1234/object",
140− "type": "object",
141− "properties": {
142− "name": {"$ref": "name.json#/definitions/orNull"}
143− }
144− },
145− "tests": [
146− {
147− "description": "string is valid",
148− "data": {
149− "name": "foo"
150− },
151− "valid": true
152− },
153− {
154− "description": "null is valid",
155− "data": {
156− "name": null
157− },
158− "valid": true
159− },
160− {
161− "description": "object is invalid",
162− "data": {
163− "name": {
164− "name": null
165− }
166− },
167− "valid": false
168− }
169− ]
170− }
171−]
JSON-Schema-Test-Suite/tests/draft6/required.jsondeleted−60
@@ -1,60 +0,0 @@
1−[
2− {
3− "description": "required validation",
4− "schema": {
5− "properties": {
6− "foo": {},
7− "bar": {}
8− },
9− "required": ["foo"]
10− },
11− "tests": [
12− {
13− "description": "present required property is valid",
14− "data": {"foo": 1},
15− "valid": true
16− },
17− {
18− "description": "non-present required property is invalid",
19− "data": {"bar": 1},
20− "valid": false
21− },
22− {
23− "description": "ignores non-objects",
24− "data": 12,
25− "valid": true
26− }
27− ]
28− },
29− {
30− "description": "required default validation",
31− "schema": {
32− "properties": {
33− "foo": {}
34− }
35− },
36− "tests": [
37− {
38− "description": "not required by default",
39− "data": {},
40− "valid": true
41− }
42− ]
43− },
44− {
45− "description": "required with empty array",
46− "schema": {
47− "properties": {
48− "foo": {}
49− },
50− "required": []
51− },
52− "tests": [
53− {
54− "description": "property not required",
55− "data": {},
56− "valid": true
57− }
58− ]
59− }
60−]
JSON-Schema-Test-Suite/tests/draft6/type.jsondeleted−345
@@ -1,345 +0,0 @@
1−[
2− {
3− "description": "integer type matches integers",
4− "schema": {"type": "integer"},
5− "tests": [
6− {
7− "description": "an integer is an integer",
8− "data": 1,
9− "valid": true
10− },
11− {
12− "description": "a float is not an integer",
13− "data": 1.1,
14− "valid": false
15− },
16− {
17− "description": "a string is not an integer",
18− "data": "foo",
19− "valid": false
20− },
21− {
22− "description": "a string is still not an integer, even if it looks like one",
23− "data": "1",
24− "valid": false
25− },
26− {
27− "description": "an object is not an integer",
28− "data": {},
29− "valid": false
30− },
31− {
32− "description": "an array is not an integer",
33− "data": [],
34− "valid": false
35− },
36− {
37− "description": "a boolean is not an integer",
38− "data": true,
39− "valid": false
40− },
41− {
42− "description": "null is not an integer",
43− "data": null,
44− "valid": false
45− }
46− ]
47− },
48− {
49− "description": "number type matches numbers",
50− "schema": {"type": "number"},
51− "tests": [
52− {
53− "description": "an integer is a number",
54− "data": 1,
55− "valid": true
56− },
57− {
58− "description": "a float is a number",
59− "data": 1.1,
60− "valid": true
61− },
62− {
63− "description": "a string is not a number",
64− "data": "foo",
65− "valid": false
66− },
67− {
68− "description": "a string is still not a number, even if it looks like one",
69− "data": "1",
70− "valid": false
71− },
72− {
73− "description": "an object is not a number",
74− "data": {},
75− "valid": false
76− },
77− {
78− "description": "an array is not a number",
79− "data": [],
80− "valid": false
81− },
82− {
83− "description": "a boolean is not a number",
84− "data": true,
85− "valid": false
86− },
87− {
88− "description": "null is not a number",
89− "data": null,
90− "valid": false
91− }
92− ]
93− },
94− {
95− "description": "string type matches strings",
96− "schema": {"type": "string"},
97− "tests": [
98− {
99− "description": "1 is not a string",
100− "data": 1,
101− "valid": false
102− },
103− {
104− "description": "a float is not a string",
105− "data": 1.1,
106− "valid": false
107− },
108− {
109− "description": "a string is a string",
110− "data": "foo",
111− "valid": true
112− },
113− {
114− "description": "a string is still a string, even if it looks like a number",
115− "data": "1",
116− "valid": true
117− },
118− {
119− "description": "an object is not a string",
120− "data": {},
121− "valid": false
122− },
123− {
124− "description": "an array is not a string",
125− "data": [],
126− "valid": false
127− },
128− {
129− "description": "a boolean is not a string",
130− "data": true,
131− "valid": false
132− },
133− {
134− "description": "null is not a string",
135− "data": null,
136− "valid": false
137− }
138− ]
139− },
140− {
141− "description": "object type matches objects",
142− "schema": {"type": "object"},
143− "tests": [
144− {
145− "description": "an integer is not an object",
146− "data": 1,
147− "valid": false
148− },
149− {
150− "description": "a float is not an object",
151− "data": 1.1,
152− "valid": false
153− },
154− {
155− "description": "a string is not an object",
156− "data": "foo",
157− "valid": false
158− },
159− {
160− "description": "an object is an object",
161− "data": {},
162− "valid": true
163− },
164− {
165− "description": "an array is not an object",
166− "data": [],
167− "valid": false
168− },
169− {
170− "description": "a boolean is not an object",
171− "data": true,
172− "valid": false
173− },
174− {
175− "description": "null is not an object",
176− "data": null,
177− "valid": false
178− }
179− ]
180− },
181− {
182− "description": "array type matches arrays",
183− "schema": {"type": "array"},
184− "tests": [
185− {
186− "description": "an integer is not an array",
187− "data": 1,
188− "valid": false
189− },
190− {
191− "description": "a float is not an array",
192− "data": 1.1,
193− "valid": false
194− },
195− {
196− "description": "a string is not an array",
197− "data": "foo",
198− "valid": false
199− },
200− {
201− "description": "an object is not an array",
202− "data": {},
203− "valid": false
204− },
205− {
206− "description": "an array is an array",
207− "data": [],
208− "valid": true
209− },
210− {
211− "description": "a boolean is not an array",
212− "data": true,
213− "valid": false
214− },
215− {
216− "description": "null is not an array",
217− "data": null,
218− "valid": false
219− }
220− ]
221− },
222− {
223− "description": "boolean type matches booleans",
224− "schema": {"type": "boolean"},
225− "tests": [
226− {
227− "description": "an integer is not a boolean",
228− "data": 1,
229− "valid": false
230− },
231− {
232− "description": "a float is not a boolean",
233− "data": 1.1,
234− "valid": false
235− },
236− {
237− "description": "a string is not a boolean",
238− "data": "foo",
239− "valid": false
240− },
241− {
242− "description": "an object is not a boolean",
243− "data": {},
244− "valid": false
245− },
246− {
247− "description": "an array is not a boolean",
248− "data": [],
249− "valid": false
250− },
251− {
252− "description": "a boolean is a boolean",
253− "data": true,
254− "valid": true
255− },
256− {
257− "description": "null is not a boolean",
258− "data": null,
259− "valid": false
260− }
261− ]
262− },
263− {
264− "description": "null type matches only the null object",
265− "schema": {"type": "null"},
266− "tests": [
267− {
268− "description": "an integer is not null",
269− "data": 1,
270− "valid": false
271− },
272− {
273− "description": "a float is not null",
274− "data": 1.1,
275− "valid": false
276− },
277− {
278− "description": "a string is not null",
279− "data": "foo",
280− "valid": false
281− },
282− {
283− "description": "an object is not null",
284− "data": {},
285− "valid": false
286− },
287− {
288− "description": "an array is not null",
289− "data": [],
290− "valid": false
291− },
292− {
293− "description": "a boolean is not null",
294− "data": true,
295− "valid": false
296− },
297− {
298− "description": "null is null",
299− "data": null,
300− "valid": true
301− }
302− ]
303− },
304− {
305− "description": "multiple types can be specified in an array",
306− "schema": {"type": ["integer", "string"]},
307− "tests": [
308− {
309− "description": "an integer is valid",
310− "data": 1,
311− "valid": true
312− },
313− {
314− "description": "a string is valid",
315− "data": "foo",
316− "valid": true
317− },
318− {
319− "description": "a float is invalid",
320− "data": 1.1,
321− "valid": false
322− },
323− {
324− "description": "an object is invalid",
325− "data": {},
326− "valid": false
327− },
328− {
329− "description": "an array is invalid",
330− "data": [],
331− "valid": false
332− },
333− {
334− "description": "a boolean is invalid",
335− "data": true,
336− "valid": false
337− },
338− {
339− "description": "null is invalid",
340− "data": null,
341− "valid": false
342− }
343− ]
344− }
345−]
JSON-Schema-Test-Suite/tests/draft6/uniqueItems.jsondeleted−79
@@ -1,79 +0,0 @@
1−[
2− {
3− "description": "uniqueItems validation",
4− "schema": {"uniqueItems": true},
5− "tests": [
6− {
7− "description": "unique array of integers is valid",
8− "data": [1, 2],
9− "valid": true
10− },
11− {
12− "description": "non-unique array of integers is invalid",
13− "data": [1, 1],
14− "valid": false
15− },
16− {
17− "description": "numbers are unique if mathematically unequal",
18− "data": [1.0, 1.00, 1],
19− "valid": false
20− },
21− {
22− "description": "unique array of objects is valid",
23− "data": [{"foo": "bar"}, {"foo": "baz"}],
24− "valid": true
25− },
26− {
27− "description": "non-unique array of objects is invalid",
28− "data": [{"foo": "bar"}, {"foo": "bar"}],
29− "valid": false
30− },
31− {
32− "description": "unique array of nested objects is valid",
33− "data": [
34− {"foo": {"bar" : {"baz" : true}}},
35− {"foo": {"bar" : {"baz" : false}}}
36− ],
37− "valid": true
38− },
39− {
40− "description": "non-unique array of nested objects is invalid",
41− "data": [
42− {"foo": {"bar" : {"baz" : true}}},
43− {"foo": {"bar" : {"baz" : true}}}
44− ],
45− "valid": false
46− },
47− {
48− "description": "unique array of arrays is valid",
49− "data": [["foo"], ["bar"]],
50− "valid": true
51− },
52− {
53− "description": "non-unique array of arrays is invalid",
54− "data": [["foo"], ["foo"]],
55− "valid": false
56− },
57− {
58− "description": "1 and true are unique",
59− "data": [1, true],
60− "valid": true
61− },
62− {
63− "description": "0 and false are unique",
64− "data": [0, false],
65− "valid": true
66− },
67− {
68− "description": "unique heterogeneous types are valid",
69− "data": [{}, [1], true, null, 1],
70− "valid": true
71− },
72− {
73− "description": "non-unique heterogeneous types are invalid",
74− "data": [{}, [1], true, null, {}, 1],
75− "valid": false
76− }
77− ]
78− }
79−]
README.md+48−12
@@ -1,25 +1,61 @@
1−# DEPRECATION NOTICE
1+![hjsonschema logo](./logo.jpg)
2
2
3−`hjsonschema` was an attempt to build a very modular JSON Schema library. Validators have [a concrete type](src/JSONSchema/Validator/Types.hs) and can be mixed and matched into new [Specs](src/JSONSchema/Types.hs).
3+A Haskell implementation of the current [JSON Schema](http://json-schema.org/) specification (Draft 4).
4
4
5−However this flexibility came at the price of complicating the code. I don't think it was the right tradeoff, especially since situations where you'd want to change what validators make up a `Spec` at runtime seem rare.
5+[Hackage](https://hackage.haskell.org/package/hjsonschema) / [GitHub](https://github.com/seagreen/hjsonschema) / [Travis CI](https://travis-ci.org/seagreen/hjsonschema)
6−
7−Also, there are many parts of JSON Schema that `hjsonschema` doesn't implement properly (as you can see from the issue tracker). I'm hoping that a new JSON Schema library will come along that handles these correctly. In the meantime I'm happy to merge working fixes into here.
8
6
9−# Links
7+Requires [pcre](http://www.pcre.org/) (`pkgs.pcre` in Nixpkgs).
10
8
11−[Hackage](https://hackage.haskell.org/package/hjsonschema) / [GitHub](https://github.com/seagreen/hjsonschema) / [Travis CI](https://travis-ci.org/seagreen/hjsonschema)
9+NOTE: Schemas with circular references can cause infinite loops. hjsonschema does loop detection but it may not be solid yet -- please open an issue if you find a situation where it fails.
12
10
13# Example
11# Example
14
12
15See [here](https://github.com/seagreen/hjsonschema/blob/master/examples/Simple.hs).
13See [here](https://github.com/seagreen/hjsonschema/blob/master/examples/Simple.hs).
16
14
17−# System dependencies
15+# Tests
16+
17+Run all tests:
18+
19+`stack test`
20+
21+Run only local tests:
22+
23+`stack test hjsonschema:local`
24+
25+Run remote tests (makes GETs to json-schema.org, also temporarily starts an HTTP server on port 1234):
26+
27+`stack test hjsonschema:remote`
28+
29+# Details
30+
31+## Goals
32+
33++ Be a correct and fast implementation of the spec.
34+
35++ Be a useful reference for implementers in other languages. Haskell's high level nature, expressive type system and referential transparency suit this purpose well.
36+
37+## Good Parts
38+
39++ Passes all the required tests in the [language agnostic test suite](https://github.com/json-schema/JSON-Schema-Test-Suite).
40+
41++ Very modular, which should make it easy to support future versions of the specification.
42+
43+## Bad Parts
44+
45++ Uses the [pcre-heavy](https://hackage.haskell.org/package/pcre-heavy) regular expression library for the "pattern" validator. It should use a library based on the ECMA 262 regex dialect, which the [spec](http://json-schema.org/latest/json-schema-validation.html#anchor33) requires.
46+
47++ Currently doesn't support the optional `"format"` validators.
48+
49+## Notes
50+
51++ `JSON-Schema-Test-Suite` is vendored from commit # aabcb3427745ade7a0b4d49ff016ad7eda8b898b [here](https://github.com/json-schema-org/JSON-Schema-Test-Suite).
52+
53++ `src/draft4.json` is from commit # f3d5aeb5ffbe9d9a5a0ceb761dc47c7c4c2efa68 [here](https://github.com/json-schema/json-schema).
18
54
19−+ Requires [pcre](http://www.pcre.org/) (`pkgs.pcre` in Nixpkgs).
55+## Credits
20
56
21−## Vendoring
57+[TJ Weigel](http://tjweigel.com/) created the logo.
22
58
23−+ `JSON-Schema-Test-Suite` is vendored from commit # c1b12bf699f29a04b4286711c6e3bbfba66f21e5 [here](https://github.com/json-schema-org/JSON-Schema-Test-Suite).
59+[Tim Baumann](https://github.com/timjb) wrote [aeson-schema](https://hackage.haskell.org/package/aeson-schema), on which hjsonschema's test code and its implementation of `SchemaGraph` were based.
24
60
25−+ `src/draft4.json` is from commit # c1b12bf699f29a04b4286711c6e3bbfba66f21e5 [here](https://github.com/json-schema/json-schema). The [root ref in remote ref](./JSON-Schema-Test-Suite/tests/draft4/refRemote.json) test has been modified to fix [#175](https://github.com/json-schema-org/JSON-Schema-Test-Suite/issues/175).
61+[Julian Berman](https://github.com/Julian) maintains the fantastic [language agnostic test suite](https://github.com/json-schema/JSON-Schema-Test-Suite).
changelog.mddeleted−209
@@ -1,209 +0,0 @@
1−# 1.8.0
2−
3−+ Add GHC 8.4 support (thanks @4e6 !).
4−+ Drop GHC 7.10 support.
5−+ Fix JSON Pointer resolution error.
6−
7−# 1.8.0
8−
9−+ Allow HTTPS references (thanks @creichert!).
10−+ Use `safe-exceptions` to eliminate the risk of catching asynchronous exceptions.
11−
12−# 1.7.2
13−
14−+ Remove upper bounds.
15−
16−# 1.7.1
17−
18−+ Bump http-types.
19−
20−# 1.7.0
21−
22−+ Test with GHC 8.2. Drop GHC 7.8.
23−+ Rework `allUniqueValues` related utility functions.
24−
25−# 1.6.3
26−
27−+ Bump hjsonpointer and QuickCheck.
28−
29−# 1.6.2
30−
31−+ Bump aeson.
32−
33−# 1.6.1
34−
35−+ Fix Haddocks.
36−
37−# 1.6.0
38−
39−+ Fix defect where validators alongside "$ref" weren't ignored.
40−
41−+ Fix defect where local references would fail if an "id" key had set resolution scope to start from a different document.
42−
43−+ Vendor latest tests.
44−
45−+ Remove `ReferencedSchemas`.
46−
47−+ Create `Scope` to hold information that changes during validation.
48−
49−+ Use a sum type for "type" values. Thanks to Philip Weaver (GitHub @pheaver).
50−
51−# 1.5.0.1
52−
53−+ Raise test dep upper bounds.
54−
55−# 1.5.0.0
56−
57−+ Report details of `"required"` validator failure.
58−+ Bump `directory`.
59−
60−# 1.4.0.0
61−
62−+ Rename `Data.JsonSchema.*` modules to `JSONSchema.*`.
63−+ Rename `Data.Validator.*` modules to `JSONSchema.Validator.*`.
64−
65−# 1.3.0.1
66−
67−+ Bump `vector`.
68−
69−# 1.3.0.0
70−
71−+ Rewrite failure messages.
72−
73−We used to return a list of every failed validator, no matter how far it was buried within subschemas of the original schema.
74−
75−Now we return a tree of failure messages, so that if the first schema had only three validators, then no more than three top level failures will be returned.
76−
77−+ Move the code to parse each validator from JSON from `Data.Validator.Draft4` into the validators' modules themselves.
78−
79−+ Switch from Prelude to Protolude.
80−
81−+ Switch `Data.JsonSchema.Fetch` from lazy to strict bytestrings.
82−
83−# 1.2.0.2
84−
85−+ Bump hspec.
86−
87−# 1.2.0.1
88−
89−+ Switch to hspec for tests.
90−
91−# 1.2.0.0
92−
93−+ Return `AdditionalPropertiesObject` error correctly (was mistakenly returning `AdditionalItemsObject` instead.
94−+ Don't silence errors resulting from subschemas of "anyOf" or "oneOf".
95−
96−# 1.1.0.1
97−
98−+ Bump `aeson` and `hjsonpointer`.
99−
100−# 1.1.0.0
101−
102−+ Rename `schemaForSchemas` to `metaSchema` and `schemaForSchemasBytes` to `metaSchemaBytes`.
103−
104−# 1.0.0.0
105−
106−## Bug fixes:
107−
108−+ Fix JSON Pointer bug. Pointers were being built in reverse order and so were totally invalid.
109−+ Use `.:!` instead of `.:?` to parse the draft 4 schema. The only way to omit optional fields in JSON Schema Draft 4 is to omit them entirely, `"null"` can't be used for this.
110−
111−## API Changes:
112−
113−+ Add referenced schema loop detection.
114−+ Add a new `referencesValidity` function.
115−+ `checkSchema` now checks referenced schema's validity in addition to the starting schema's validity. This change bubbles up to the one-step validation functions as well.
116−+ Switch most of the fetching code to use `URISchemaMap` instead of `ReferencedSchemas`. It didn't need to know about the more complicated data type.
117−+ Rething failure related names. Change `Invalid` to `Failure`, add a new `Invalid` type alias which is only used for final results.
118−+ Failures now include the failing part of the data as well as a JSON Pointer to it, so you don't have to worry about resolving the pointer.
119−
120−## Fundamental Changes:
121−
122−+ Make `Fail` (previously `Failure`) an instance of `Functor`.
123−+ Add a `Validator` data type which is an instance of `Profunctor`.
124−+ Add a `Spec` data type for collections of `Validators`.
125−
126−## General:
127−
128−+ Switch from 2 to 4 space indentation.
129−+ Update the vendored JSON Schema Test Suite.
130−
131−# 0.10.0.3
132−
133−+ Bump http-client.
134−
135−# 0.10.0.2
136−
137−+ Enable GHC 8.
138−
139−# 0.10.0.1
140−
141−+ Fix .cabal file.
142−
143−# 0.10
144−
145−+ Rewrite fetching internals.
146−+ Fix reference resolution defects, add more tests.
147−+ Switch to a Perl style regex library, which is closer to ECMAScript regexes than the previous Posix style one.
148−+ Add one-step validation functions ('fetchFilesystemAndValidate' and 'fetchHTTPAndValidate').
149−+ Alias the validation failure type exported by 'Data.JsonSchema.Draft4' to
150−'Invalid', change its field names.
151−
152−# 0.9
153−
154−+ Partial rewrite. The API of the library has changed, see the examples
155−folder for how to use the new one.
156−
157−+ Users of the library can now write schemas in Haskell as well as JSON.
158−
159−# 0.8
160−
161−+ Improve scope updating and resolving.
162−+ Rename RawSchema's _rsObject field to _rsData.
163−+ Make RawSchema's _rsURI field a Maybe. This way schemas without a starting URI can say so explicitly with Nothing instead of with "".
164−+ Rename Graph to SchemaGraph. Declare it with data instead of type. Give it a field referencing the starting schema. This field is used to find the curent schema if no URI is in scope and a self-referencing $ref is found (e.g. "#").
165−+ Change the order of the last two arguments to fetchReferencedSchemas.
166−
167−# 0.7.1
168−
169−+ Support GHC 7.8 again.
170−
171−# 0.7
172−
173−Change error type from Text to ValidationFailure.
174−
175−Revert the 0.6 changes to validate. Also switch from Vector to list. Validate is now:
176− Schema err -> Value -> [ValidationFailure err]
177−
178−Add fetchReferencedSchemas', which lets the user provide their own MonadIO function to be used when fetching schemas. This lets them do things like only fetch schemas from particular domains.
179−
180−# 0.6
181−
182−Break the API so the library doesn't induce boolean blindness.
183−
184−Change validate
185− was: Schema -> Value -> Vector ValErr
186− now: Schema -> Value -> Either (Vector ValErr) Value
187−
188−Change Schema
189− was: type Schema = Vector Validator
190− now: newtype Schema = Schema { _unSchema :: [Validator] }
191−
192−# 0.5.3
193−
194−+ Switch from http-conduit to http-client.
195−
196−# 0.5.2
197−
198−+ Add convenience function for validating and compiling draft 4 schemas simultaneously.
199−
200−# 0.5.1
201−
202−+ Switch from wreq to http-conduit; drop lens dependency.
203−
204−# 0.5
205−
206−+ Start changelog.
207−+ Rename Utils.hs to Helpers.hs.
208−+ Move all non-ValidatorGen functions in Validators.hs to Helpers.hs.
209−+ Various touchups.
changelog.txtnew file+125
@@ -0,0 +1,125 @@
1++ Rename `schemaForSchemas` to `metaSchema`.
2+
3+# 1.0.0.0
4+
5+## Bug fixes:
6+
7++ Fix JSON Pointer bug. Pointers were being built in reverse order and so were
8+totally invalid.
9++ Use `.:!` instead of `.:?` to parse the draft 4 schema. The only way to omit
10+optional fields in JSON Schema Draft 4 is to omit them entirely, `"null"`
11+can't be used for this.
12+
13+## API Changes:
14+
15++ Add referenced schema loop detection.
16++ Add a new `referencesValidity` function.
17++ `checkSchema` now checks referenced schema's validity in addition to the
18+starting schema's validity. This change bubbles up to the one-step validation
19+functions as well.
20++ Switch most of the fetching code to use `URISchemaMap` instead of
21+`ReferencedSchemas`. It didn't need to know about the more complicated data
22+type.
23++ Rething failure related names. Change `Invalid` to `Failure`, add a new
24+`Invalid` type alias which is only used for final results.
25++ Failures now include the failing part of the data as well as a JSON Pointer to
26+it, so you don't have to worry about resolving the pointer.
27+
28+## Fundamental Changes:
29+
30++ Make `Fail` (previously `Failure`) an instance of `Functor`.
31++ Add a `Validator` data type which is an instance of `Profunctor`.
32++ Add a `Spec` data type for collections of `Validators`.
33+
34+## General:
35+
36++ Switch from 2 to 4 space indentation.
37++ Update the vendored JSON Schema Test Suite.
38+
39+# 0.10.0.3
40+
41++ Bump http-client.
42+
43+# 0.10.0.2
44+
45++ Enable GHC 8.
46+
47+# 0.10.0.1
48+
49++ Fix .cabal file.
50+
51+# 0.10
52+
53++ Rewrite fetching internals.
54++ Fix reference resolution defects, add more tests.
55++ Switch to a Perl style regex library, which is closer to ECMAScript regexes
56+than the previous Posix style one.
57++ Add one-step validation functions ('fetchFilesystemAndValidate' and 'fetchHTTPAndValidate').
58++ Alias the validation failure type exported by 'Data.JsonSchema.Draft4' to
59+'Invalid', change its field names.
60+
61+# 0.9
62+
63++ Partial rewrite. The API of the library has changed, see the examples
64+folder for how to use the new one.
65+
66++ Users of the library can now write schemas in Haskell as well as JSON.
67+
68+# 0.8
69+
70++ Improve scope updating and resolving.
71++ Rename RawSchema's _rsObject field to _rsData.
72++ Make RawSchema's _rsURI field a Maybe. This way schemas without a starting
73+URI can say so explicitly with Nothing instead of with "".
74++ Rename Graph to SchemaGraph. Declare it with data instead of type. Give it a
75+field referencing the starting schema. This field is used to find the curent
76+schema if no URI is in scope and a self-referencing $ref is found (e.g. "#").
77++ Change the order of the last two arguments to fetchReferencedSchemas.
78+
79+# 0.7.1
80+
81++ Support GHC 7.8 again.
82+
83+# 0.7
84+
85+Change error type from Text to ValidationFailure.
86+
87+Revert the 0.6 changes to validate. Also switch from Vector
88+to list. Validate is now:
89+ Schema err -> Value -> [ValidationFailure err]
90+
91+Add fetchReferencedSchemas', which lets the user provide their
92+own MonadIO function to be used when fetching schemas. This lets
93+them do things like only fetch schemas from particular domains.
94+
95+# 0.6
96+
97+Break the API so the library doesn't induce boolean blindness.
98+
99+Change validate
100+ was: Schema -> Value -> Vector ValErr
101+ now: Schema -> Value -> Either (Vector ValErr) Value
102+
103+Change Schema
104+ was: type Schema = Vector Validator
105+ now: newtype Schema = Schema { _unSchema :: [Validator] }
106+
107+# 0.5.3
108+
109++ Switch from http-conduit to http-client.
110+
111+# 0.5.2
112+
113++ Add convenience function for validating and compiling draft 4 schemas
114+simultaneously.
115+
116+# 0.5.1
117+
118++ Switch from wreq to http-conduit; drop lens dependency.
119+
120+# 0.5
121+
122++ Start changelog.
123++ Rename Utils.hs to Helpers.hs.
124++ Move all non-ValidatorGen functions in Validators.hs to Helpers.hs.
125++ Various touchups.
examples/AlternateSchema.hs+110−150
@@ -1,28 +1,38 @@
1-- | An implementation of JSON Schema Draft 4 based on 'HashMap Text Value'
1-- | An implementation of JSON Schema Draft 4 based on 'HashMap Text Value'
2−-- instead of a custom record type like 'JSONSchema.Draft4'.
2+-- instead of a custom record type like 'Data.JsonSchema.Draft4'.
3--
3--
4-- If you're writing code for a new schema specification you probably want
4-- If you're writing code for a new schema specification you probably want
5−-- to copy this module instead of the 'JSONSchema.Draft4'. While it's
5+-- to copy this module instead of the 'Data.JsonSchema.Draft4'. While it's
6-- less convenient to write schemas in Haskell without a record type, you
6-- less convenient to write schemas in Haskell without a record type, you
7-- can get the implementation finished with far fewer lines of code.
7-- can get the implementation finished with far fewer lines of code.
8−module AlternateSchema where
8+--
9−
9+-- Note that this module imports the the failure sum type and failure related
10−import Protolude
10+-- helper functions from the library. If you're implementing a custom schema
11+-- with different error types from JSON Schema Draft 4 you'll have to make
12+-- your own.
11
13
12−import Data.Aeson (FromJSON(..), Value(..), decodeStrict)
14+module AlternateSchema where
13−import qualified Data.Aeson as AE
14−import qualified Data.HashMap.Strict as HM
15−import Data.Maybe (fromMaybe)
16−import Data.Profunctor (Profunctor(..))
17
15
18−import JSONSchema.Draft4 (ValidatorFailure(..), metaSchemaBytes)
16+import Data.Aeson (FromJSON(..), Value(..),
17+ decode)
18+import qualified Data.Aeson as AE
19+import qualified Data.ByteString.Lazy as LBS
20+import qualified Data.HashMap.Strict as HM
21+import Data.Maybe (fromMaybe)
22+import Data.Monoid
23+import Data.Profunctor (Profunctor (..))
24+import Data.Text (Text)
25+
26+import Data.JsonSchema.Draft4 (metaSchemaBytes)
27+import Data.JsonSchema.Draft4.Failure
28+import Data.JsonSchema.Fetch (ReferencedSchemas(..),
19−import JSONSchema.Fetch (SchemaWithURI(..), URISchemaMap(..))
29+ SchemaWithURI(..))
20−import qualified JSONSchema.Fetch as FE
30+import qualified Data.JsonSchema.Fetch as FE
21−import JSONSchema.Types (Schema(..), Spec(..))
31+import Data.JsonSchema.Types (Schema(..), Spec(..))
22−import qualified JSONSchema.Types as JT
32+import qualified Data.JsonSchema.Types as JT
23−import qualified JSONSchema.Validator.Draft4 as D4
33+import qualified Data.Validator.Draft4 as D4
24−import JSONSchema.Validator.Reference (BaseURI(..), Scope(..),
34+import qualified Data.Validator.Draft4.Any as AN
25− updateResolutionScope)
35+import Data.Validator.Reference (updateResolutionScope)
26
36
27--------------------------------------------------
37--------------------------------------------------
28-- * Basic fetching tools
38-- * Basic fetching tools
@@ -34,167 +44,117 @@ referencesViaHTTP
34referencesViaHTTP = FE.referencesViaHTTP' draft4FetchInfo
44referencesViaHTTP = FE.referencesViaHTTP' draft4FetchInfo
35
45
36draft4FetchInfo :: FE.FetchInfo Schema
46draft4FetchInfo :: FE.FetchInfo Schema
37−draft4FetchInfo = FE.FetchInfo embedded (lookup "id") (lookup "$ref")
47+draft4FetchInfo = FE.FetchInfo embedded (get "id") (get "$ref")
38 where
48 where
39− lookup :: Text -> Schema -> Maybe Text
49+ get :: Text -> Schema -> Maybe Text
40− lookup k (Schema s) =
41− case HM.lookup k s of
50+ get k (Schema s) = case HM.lookup k s of
42− Just (String t) -> Just t
51+ Just (String t) -> Just t
43− _ -> Nothing
52+ _ -> Nothing
44
53
45−-- | An implementation of 'JT.embedded'.
46embedded :: Schema -> ([Schema], [Schema])
54embedded :: Schema -> ([Schema], [Schema])
47−embedded s =
48− JT.embedded (d4Spec mempty mempty (Scope s Nothing (BaseURI Nothing))) s
55+embedded s = JT.embedded (d4Spec (ReferencedSchemas s mempty) mempty Nothing) s
49
56
50--------------------------------------------------
57--------------------------------------------------
51-- * Main API
58-- * Main API
52--------------------------------------------------
59--------------------------------------------------
53
60
54validate
61validate
55− :: URISchemaMap Schema
62+ :: ReferencedSchemas Schema
63+ -> Maybe Text
56− -> SchemaWithURI Schema
64+ -> Schema
57 -> Value
65 -> Value
58− -> [ValidatorFailure]
66+ -> [Failure]
59−validate schemaMap sw =
67+validate rs = continueValidating rs (AN.VisitedSchemas [(Nothing, Nothing)])
60− JT.validate (d4Spec schemaMap visited scope) (_swSchema sw)
61− where
62− visited :: D4.VisitedSchemas
63− visited = D4.VisitedSchemas [(Nothing, Nothing)]
64−
65− schemaId :: Maybe Text
66− schemaId = FE._fiId draft4FetchInfo (_swSchema sw)
67−
68− scope :: Scope Schema
69− scope = Scope
70− { _topLevelDocument = _swSchema sw
71− , _documentURI = _swURI sw
72− , _currentBaseURI = updateResolutionScope (BaseURI (_swURI sw))
73− schemaId
74− }
75
68
76−-- | A schema for schemas themselves. Uses @src/draft4.json@ which is loaded
69+-- A schema for schemas themselves, using @src/draft4.json@ which is loaded
77-- at compile time.
70-- at compile time.
78metaSchema :: Schema
71metaSchema :: Schema
79metaSchema =
72metaSchema =
80− fromMaybe (panic "Schema decode failed (this should never happen)")
73+ fromMaybe (error "Schema decode failed (this should never happen)")
81− . decodeStrict
74+ . decode
75+ . LBS.fromStrict
82 $ metaSchemaBytes
76 $ metaSchemaBytes
83
77
84−checkSchema :: Schema -> [ValidatorFailure]
78+checkSchema :: Schema -> [Failure]
85−checkSchema = validate schemaMap (SchemaWithURI metaSchema Nothing)
79+checkSchema = validate referenced Nothing metaSchema . Object . _unSchema
86− . Object
87− . _unSchema
88 where
80 where
89− schemaMap :: URISchemaMap Schema
81+ referenced :: ReferencedSchemas Schema
90− schemaMap =
82+ referenced = ReferencedSchemas
83+ metaSchema
91− URISchemaMap (HM.singleton "http://json-schema.org/draft-04/schema"
84+ (HM.singleton "http://json-schema.org/draft-04/schema"
92− metaSchema)
85+ metaSchema)
93
86
94--------------------------------------------------
87--------------------------------------------------
95-- * Spec
88-- * Spec
96--------------------------------------------------
89--------------------------------------------------
97
90
98−validateSubschema
91+continueValidating
99− :: URISchemaMap Schema
92+ :: ReferencedSchemas Schema
100− -> D4.VisitedSchemas
93+ -> AN.VisitedSchemas
101− -> Scope Schema
94+ -> Maybe Text
102 -> Schema
95 -> Schema
103 -> Value
96 -> Value
104− -> [ValidatorFailure]
97+ -> [Failure]
105−validateSubschema schemaMap visited scope schema =
98+continueValidating referenced visited mURI sc =
106− JT.validate (d4Spec schemaMap visited newScope) schema
99+ JT.validate (d4Spec referenced visited newScope) sc
107 where
100 where
108 schemaId :: Maybe Text
101 schemaId :: Maybe Text
109− schemaId = FE._fiId draft4FetchInfo schema
102+ schemaId = case HM.lookup "id" (_unSchema sc) of
103+ Just (String t) -> Just t
104+ _ -> Nothing
110
105
111− newScope :: Scope Schema
106+ newScope :: Maybe Text
112− newScope = scope
113− { _currentBaseURI = updateResolutionScope (_currentBaseURI scope)
107+ newScope = updateResolutionScope mURI schemaId
114− schemaId
115− }
116
108
117d4Spec
109d4Spec
118− :: URISchemaMap Schema
110+ :: ReferencedSchemas Schema
119− -> D4.VisitedSchemas
111+ -> AN.VisitedSchemas
120− -> Scope Schema
112+ -> Maybe Text
121− -> Spec Schema ValidatorFailure
113+ -> Spec Schema ValidatorChain
122− -- ^ Here we reuses 'ValidatorFailure' from
123− -- 'JSONSchema.Draft4.Failure'. If your validators have different
124− -- failure possibilities you'll need to create your own validator
125− -- failure type.
126−d4Spec schemaMap visited scope =
114+d4Spec referenced visited scope =
127− Spec $
115+ Spec
128− [ dimap
116+ [ dimap f (const MultipleOf) D4.multipleOf
129− f
117+ , dimap f maxE D4.maximumVal
130− FailureRef
118+ , dimap f minE D4.minimumVal
131− (D4.refValidator (FE.getReference schemaMap) updateScope
119+
132− valRef visited scope)
120+ , dimap f (const MaxLength) D4.maxLength
133− ]
121+ , dimap f (const MinLength) D4.minLength
134−
122+ , dimap f (const PatternValidator) D4.patternVal
135− <> fmap (lmap disableIfRefPresent)
123+
136− [ dimap f FailureMultipleOf D4.multipleOfValidator
124+ , dimap f (const MaxItems) D4.maxItems
137− , dimap f FailureMaximum D4.maximumValidator
125+ , dimap f (const MinItems) D4.minItems
138− , dimap f FailureMinimum D4.minimumValidator
126+ , dimap f (const UniqueItems) D4.uniqueItems
139−
127+ , dimap f itemsE (D4.items descend)
140− , dimap f FailureMaxLength D4.maxLengthValidator
128+
141− , dimap f FailureMinLength D4.minLengthValidator
129+ , dimap f (const MaxProperties) D4.maxProperties
142− , dimap f FailurePattern D4.patternValidator
130+ , dimap f (const MinProperties) D4.minProperties
143−
131+ , dimap f (const Required) D4.required
144− , dimap f FailureMaxItems D4.maxItemsValidator
132+ , dimap f depsE (D4.dependencies descend)
145− , dimap f FailureMinItems D4.minItemsValidator
133+ , dimap f propE (D4.properties descend)
146− , dimap f FailureUniqueItems D4.uniqueItemsValidator
134+ , dimap f patPropE (D4.patternProperties descend)
147− , dimap
135+ , dimap f addPropE (D4.additionalProperties descend)
148− (fromMaybe D4.emptyItems . f)
136+
149− (\err -> case err of
137+ , dimap f refE (D4.ref visited scope (FE.getReference referenced) refVal)
150− D4.IRInvalidItems e -> FailureItems e
138+ , dimap f (const Enum) D4.enumVal
151− D4.IRInvalidAdditional e -> FailureAdditionalItems e)
139+ , dimap f (const TypeValidator) D4.typeVal
152− (D4.itemsRelatedValidator descend)
140+ , dimap f AllOf (D4.allOf lateral)
153− , lmap f D4.definitionsEmbedded
141+ , dimap f (const AnyOf) (D4.anyOf lateral)
154−
142+ , dimap f (const OneOf) (D4.oneOf lateral)
155− , dimap f FailureMaxProperties D4.maxPropertiesValidator
143+ , dimap f (const NotValidator) (D4.notVal lateral)
156− , dimap f FailureMinProperties D4.minPropertiesValidator
157− , dimap f FailureRequired D4.requiredValidator
158− , dimap f FailureDependencies (D4.dependenciesValidator descend)
159− , dimap
160− (fromMaybe D4.emptyProperties . f)
161− FailurePropertiesRelated
162− (D4.propertiesRelatedValidator descend)
163−
164− , dimap f FailureEnum D4.enumValidator
165− , dimap f FailureType D4.typeValidator
166− , dimap f FailureAllOf (D4.allOfValidator lateral)
167− , dimap f FailureAnyOf (D4.anyOfValidator lateral)
168− , dimap f FailureOneOf (D4.oneOfValidator lateral)
169− , dimap f FailureNot (D4.notValidator lateral)
170 ]
144 ]
171 where
145 where
172 f :: FromJSON a => Schema -> Maybe a
146 f :: FromJSON a => Schema -> Maybe a
173− f (Schema a) =
174− case AE.fromJSON (Object a) of
147+ f (Schema a) = case AE.fromJSON (Object a) of
175− AE.Error _ -> Nothing
148+ AE.Error _ -> Nothing
176− AE.Success b -> Just b
149+ AE.Success b -> Just b
177−
150+
178− disableIfRefPresent :: Schema -> Schema
151+ -- 'Maybe Text' is the URI the refernced schema is fetch from,
179− disableIfRefPresent schema =
152+ -- this probably needs a 'newtype' wrapper.
180− case FE._fiRef draft4FetchInfo schema of
181− Nothing -> schema
182− Just _ -> Schema mempty
183−
184− updateScope :: BaseURI -> Schema -> BaseURI
185− updateScope uri schema =
186− updateResolutionScope uri (FE._fiId draft4FetchInfo schema)
187−
188− valRef
189− :: D4.VisitedSchemas
153+ refVal :: AN.VisitedSchemas -> Maybe Text -> Schema -> Value -> [Failure]
190− -> Scope Schema
154+ refVal = continueValidating referenced
191− -> Schema
155+
192− -> Value
193− -> [ValidatorFailure]
194− valRef vis sc = JT.validate (d4Spec schemaMap vis sc)
195−
196− descend :: Schema -> Value -> [ValidatorFailure]
156+ descend :: Schema -> Value -> [Failure]
197− descend = validateSubschema schemaMap mempty scope
157+ descend = continueValidating referenced mempty scope
198−
158+
199− lateral :: Schema -> Value -> [ValidatorFailure]
159+ lateral :: Schema -> Value -> [Failure]
200− lateral = validateSubschema schemaMap visited scope
160+ lateral = continueValidating referenced visited scope
examples/Full.hsnew file+40
@@ -0,0 +1,40 @@
1+-- | Two differences from @examples/Simple.hs@:
2+--
3+-- * This shows how to write the starting schema in Haskell instead
4+-- of parsing it from JSON.
5+--
6+-- * Validation is done in two steps using 'D4.referencesViaFilesystem' and
7+-- 'D4.checkSchema' instead of 'D4.fetchFilesystemAndValidate'. This means
8+-- that the actual validation involves no IO.
9+
10+module Full where
11+
12+import Data.Aeson (Value (..), toJSON)
13+
14+import qualified Data.JsonSchema.Draft4 as D4
15+
16+schema :: D4.Schema
17+schema = D4.emptySchema { D4._schemaRef = Just "./unique.json" }
18+
19+schemaContext :: D4.SchemaWithURI D4.Schema
20+schemaContext = D4.SchemaWithURI
21+ { D4._swSchema = schema
22+ , D4._swURI = Just "./examples/json/start.json"
23+ }
24+
25+badData :: Value
26+badData = toJSON (["foo", "foo"] :: [String])
27+
28+example :: IO ()
29+example = do
30+ res <- D4.referencesViaFilesystem schemaContext
31+ let references = case res of
32+ Left _ -> error "Couldn't fetch referenced schemas."
33+ Right a -> a
34+ validate = case D4.checkSchema references schemaContext of
35+ Left _ -> error "Not a valid schema."
36+ Right f -> f
37+ case validate badData of
38+ [] -> error "We validated bad data."
39+ [D4.Failure (D4.Ref D4.UniqueItems) _ _ _] -> return () -- Success
40+ _ -> error "We got a different failure than expected."
examples/Simple.hs+26−26
@@ -1,37 +1,37 @@
1−module Simple where
1+-- | Demonstrate 'D4.fetchFilesystemAndValidate'.
2+--
3+-- To fetch schemas using HTTP instead of from the filesystem use
4+-- 'D4.fetchHTTPAndValidate'.
2
5
3−import Protolude
6+module Simple where
4
7
5−import Data.Aeson (Value(..), decodeStrict, toJSON)
8+import Data.Aeson (Value(..), decode, toJSON)
6−import qualified Data.ByteString as BS
9+import qualified Data.ByteString.Lazy as LBS
7−import qualified Data.List.NonEmpty as NE
10+import qualified Data.List.NonEmpty as NE
8−import Data.Maybe (fromMaybe)
11+import Data.Maybe (fromMaybe)
9
12
10−import qualified JSONSchema.Draft4 as D4
13+import qualified Data.JsonSchema.Draft4 as D4
11
14
12badData :: Value
15badData :: Value
13−badData = toJSON [True, True]
16+badData = toJSON (["foo", "foo"] :: [String])
14
17
15example :: IO ()
18example :: IO ()
16example = do
19example = do
17− bts <- BS.readFile "./examples/json/unique.json"
18− let schema = fromMaybe (panic "Invalid schema JSON.") (decodeStrict bts)
19− schemaWithURI = D4.SchemaWithURI
20− schema
21− Nothing -- This would be the URI of the schema
22− -- if it had one. It's used if the schema
23− -- has relative references to other
24− -- schemas.
25
20
21+ -- Get the starting schema.
22+ bts <- LBS.readFile "./examples/json/start.json"
23+ let schema = fromMaybe (error "Invalid schema JSON.") (decode bts)
24+ schemaWithURI = D4.SchemaWithURI schema (Just "./examples/json/start.json")
25+
26− -- Fetch any referenced schemas over HTTP, check that our original schema
26+ -- Fetch any referenced schemas, check that our original schema itself
27− -- itself is valid, and validate the data.
27+ -- is valid, and validate the data.
28− res <- D4.fetchHTTPAndValidate schemaWithURI badData
28+ res <- D4.fetchFilesystemAndValidate schemaWithURI badData
29 case res of
29 case res of
30− Right () -> panic "We validated bad data."
30+ Right () -> error "We validated bad data."
31− Left (D4.HVRequest _) -> panic ("Error fetching a referenced schema"
31+ Left (D4.FVRead _) -> error ("Error fetching a referenced schema"
32− <> " (either during IO or parsing).")
32+ ++ " (either during IO or parsing).")
33− Left (D4.HVSchema _) -> panic "Our 'schema' itself was invalid."
33+ Left (D4.FVSchema _) -> error "Our 'schema' itself was invalid."
34− Left (D4.HVData (D4.Invalid _ _ failures)) ->
34+ Left (D4.FVData failures) ->
35 case NE.toList failures of
35 case NE.toList failures of
36− [D4.FailureUniqueItems _] -> pure () -- Success.
36+ [D4.Failure (D4.Ref D4.UniqueItems) _ _ _] -> return () -- Success.
37− _ -> panic "We got a different failure than expected."
37+ _ -> error "Got more invalidation reasons than we expected."
examples/TwoStep.hsdeleted−48
@@ -1,48 +0,0 @@
1−-- | Differences from @examples/Simple.hs@:
2−--
3−-- * We demonstrate how to do the validation step without IO by first
4−-- getting the references with 'D4.referencesViaFilesystem'.
5−--
6−-- * This shows how to write the starting schema in Haskell instead
7−-- of parsing it from JSON.
8−--
9−-- * This schema references other schemas. Previously we used
10−-- 'fetchHTTPAndValidate' which fetched references over HTTP (though it
11−-- didn't matter because the last schema didn't actually have any).
12−-- This time we need to get them from the filesystem.
13−module TwoStep where
14−
15−import Protolude
16−
17−import Data.Aeson (Value(..), toJSON)
18−import qualified Data.List.NonEmpty as NE
19−
20−import qualified JSONSchema.Draft4 as D4
21−import qualified JSONSchema.Validator.Draft4 as VAL
22−
23−schema :: D4.Schema
24−schema = D4.emptySchema { D4._schemaRef = Just "./unique.json" }
25−
26−schemaContext :: D4.SchemaWithURI D4.Schema
27−schemaContext = D4.SchemaWithURI
28− { D4._swSchema = schema
29− , D4._swURI = Just "./examples/json/start.json"
30− }
31−
32−badData :: Value
33−badData = toJSON [True, True]
34−
35−example :: IO ()
36−example = do
37− res <- D4.referencesViaFilesystem schemaContext
38− let references = case res of
39− Left _ -> panic "Couldn't fetch referenced schemas."
40− Right a -> a
41− validate = case D4.checkSchema references schemaContext of
42− Left _ -> panic "Not a valid schema."
43− Right f -> f
44− case validate badData of
45− [] -> panic "We validated bad data."
46− [D4.FailureRef (VAL.RefInvalid _ _ (D4.FailureUniqueItems _ NE.:|[])) ] ->
47− pure () -- Success
48− _ -> panic "We got a different failure than expected."
hjsonschema.cabal+64−64
@@ -1,5 +1,5 @@
1name: hjsonschema
1name: hjsonschema
2−version: 1.9.0
2+version: 1.0.0.0
3synopsis: JSON Schema library
3synopsis: JSON Schema library
4homepage: https://github.com/seagreen/hjsonschema
4homepage: https://github.com/seagreen/hjsonschema
5license: MIT
5license: MIT
@@ -9,10 +9,9 @@ maintainer: ian@housejeffries.com
9category: Data
9category: Data
10build-type: Simple
10build-type: Simple
11cabal-version: >=1.10
11cabal-version: >=1.10
12−-- Rerun multi-ghc-travis (executable make-travis-yml-2) after changing:
12+tested-with: GHC == 7.8.4, GHC == 7.10.2
13−Tested-With: GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.1
14extra-source-files:
13extra-source-files:
15− changelog.md
14+ changelog.txt
16 JSON-Schema-Test-Suite/remotes/*.json
15 JSON-Schema-Test-Suite/remotes/*.json
17 JSON-Schema-Test-Suite/remotes/folder/*.json
16 JSON-Schema-Test-Suite/remotes/folder/*.json
18 JSON-Schema-Test-Suite/tests/draft4/*.json
17 JSON-Schema-Test-Suite/tests/draft4/*.json
@@ -28,74 +27,70 @@ library
28 src
27 src
29 default-language: Haskell2010
28 default-language: Haskell2010
30 default-extensions:
29 default-extensions:
31− DeriveFunctor
32− DeriveGeneric
33− GeneralizedNewtypeDeriving
34 NoImplicitPrelude
30 NoImplicitPrelude
35 OverloadedStrings
31 OverloadedStrings
36 ScopedTypeVariables
32 ScopedTypeVariables
37 if impl(ghc >= 8)
33 if impl(ghc >= 8)
38 default-extensions: StrictData
34 default-extensions: StrictData
35+ other-extensions:
36+ DeriveFunctor
37+ GeneralizedNewtypeDeriving
38+ TemplateHaskell
39 ghc-options:
39 ghc-options:
40 -Wall
40 -Wall
41 exposed-modules:
41 exposed-modules:
42− JSONSchema.Draft4
42+ Data.JsonSchema.Draft4
43− , JSONSchema.Draft4.Failure
43+ , Data.JsonSchema.Draft4.Failure
44− , JSONSchema.Draft4.Schema
44+ , Data.JsonSchema.Draft4.Schema
45− , JSONSchema.Draft4.Spec
45+ , Data.JsonSchema.Draft4.Spec
46− , JSONSchema.Fetch
46+ , Data.JsonSchema.Fetch
47− , JSONSchema.Types
47+ , Data.JsonSchema.Types
48− , JSONSchema.Validator.Draft4
48+ , Data.Validator.Draft4
49− , JSONSchema.Validator.Draft4.Any
49+ , Data.Validator.Draft4.Any
50− , JSONSchema.Validator.Draft4.Array
50+ , Data.Validator.Draft4.Array
51− , JSONSchema.Validator.Draft4.Number
51+ , Data.Validator.Draft4.Number
52− , JSONSchema.Validator.Draft4.Object
52+ , Data.Validator.Draft4.Object
53− , JSONSchema.Validator.Draft4.String
53+ , Data.Validator.Draft4.String
54+ , Data.Validator.Failure
54− , JSONSchema.Validator.Reference
55+ , Data.Validator.Reference
55− , JSONSchema.Validator.Types
56+ , Data.Validator.Types
56− , JSONSchema.Validator.Utils
57+ , Data.Validator.Utils
57 other-modules:
58 other-modules:
58− JSONSchema.Validator.Draft4.Object.Properties
59+ Data.Validator.Draft4.Object.Properties
59 , Import
60 , Import
60 build-depends:
61 build-depends:
61− base >= 4.7 && < 5
62− -- 0.11 is for `.:!`:
63− , aeson >= 0.11
62+ aeson >= 0.7 && < 0.12
63+ , base >= 4.7 && < 4.10
64− , bytestring >= 0.10
64+ , bytestring >= 0.10 && < 0.11
65− , containers >= 0.5
65+ , containers >= 0.5 && < 0.6
66− , file-embed >= 0.0.8
66+ , file-embed >= 0.0.8 && < 0.1
67− , filepath >= 1.3
67+ , filepath >= 1.3 && < 1.5
68− , hashable >= 1.2
69− , hjsonpointer >= 1.1
68+ , hjsonpointer >= 0.3 && < 0.4
70 -- 0.4.30 is for parseUrlThrow:
69 -- 0.4.30 is for parseUrlThrow:
71− , http-client >= 0.4.30
70+ , http-client >= 0.4.30 && < 0.6
72− , http-client-tls >= 0.3
73− , http-types >= 0.8
71+ , http-types >= 0.8 && < 0.10
74− , pcre-heavy >= 1.0
72+ , pcre-heavy >= 1.0 && < 1.1
75− , profunctors >= 5.0
73+ , profunctors >= 5.0 && < 5.3
76− , protolude >= 0.1.10
77− , QuickCheck >= 2.8
74+ , QuickCheck >= 2.8 && < 2.10
78− , safe-exceptions >= 0.1.6
79− , scientific >= 0.3
75+ , scientific >= 0.3 && < 0.4
76+ , semigroups >= 0.18 && < 0.19
80− , unordered-containers >= 0.2
77+ , unordered-containers >= 0.2 && < 0.3
81− , text >= 1.1
78+ , text >= 1.1 && < 1.3
82− , vector >= 0.10
79+ , vector >= 0.10 && < 0.12
83
80
84test-suite local
81test-suite local
85 hs-source-dirs:
82 hs-source-dirs:
86 test
83 test
87 examples
84 examples
88− main-is: Local.hs
89− type: exitcode-stdio-1.0
90 default-language: Haskell2010
85 default-language: Haskell2010
91 ghc-options:
86 ghc-options:
92 -Wall
87 -Wall
93 -fno-warn-orphans
88 -fno-warn-orphans
94 default-extensions:
89 default-extensions:
95− DeriveGeneric
96− NoImplicitPrelude
97 OverloadedStrings
90 OverloadedStrings
98 ScopedTypeVariables
91 ScopedTypeVariables
92+ type: exitcode-stdio-1.0
93+ main-is: Local.hs
99 other-modules:
94 other-modules:
100 Local.Failure
95 Local.Failure
101 , Local.Reference
96 , Local.Reference
@@ -103,60 +98,65 @@ test-suite local
103 , Shared
98 , Shared
104 -- from ./examples:
99 -- from ./examples:
105 , AlternateSchema
100 , AlternateSchema
101+ , Full
106 , Simple
102 , Simple
107− , TwoStep
108 build-depends:
103 build-depends:
109− base
104+ aeson
110− , aeson
105+ , base
111 , bytestring
106 , bytestring
112 , filepath
107 , filepath
113 , hjsonpointer
108 , hjsonpointer
114 , hjsonschema
109 , hjsonschema
115 , profunctors
110 , profunctors
116− , protolude
111+ , semigroups
117 , text
112 , text
118 , QuickCheck
113 , QuickCheck
119 , unordered-containers
114 , unordered-containers
120 , vector
115 , vector
121−
122 -- directory-1.2.5 required for `listDirectory`:
116 -- directory-1.2.5 required for `listDirectory`:
123− , directory >= 1.2.5
117+ , directory >= 1.2.5 && < 1.3
124− , hspec >= 2.2
118+ , HUnit >= 1.2 && < 1.4
119+ , tasty >= 0.11 && < 0.12
120+ , tasty-hunit >= 0.9 && < 0.10
121+ , tasty-quickcheck >= 0.8 && < 0.9
125
122
126test-suite remote
123test-suite remote
127 hs-source-dirs:
124 hs-source-dirs:
128 test
125 test
129 examples
126 examples
130− main-is: Remote.hs
131− type: exitcode-stdio-1.0
132 default-language: Haskell2010
127 default-language: Haskell2010
133 ghc-options:
128 ghc-options:
134 -Wall
129 -Wall
135 -fno-warn-orphans
130 -fno-warn-orphans
136 default-extensions:
131 default-extensions:
137− DeriveGeneric
138− NoImplicitPrelude
139 OverloadedStrings
132 OverloadedStrings
140 ScopedTypeVariables
133 ScopedTypeVariables
134+ type: exitcode-stdio-1.0
135+ main-is: Remote.hs
141 other-modules:
136 other-modules:
142 Shared
137 Shared
143 -- from ./examples:
138 -- from ./examples:
144 , AlternateSchema
139 , AlternateSchema
145 build-depends:
140 build-depends:
146− base
147− , aeson
141+ aeson
148 , async
142 , async
143+ , base
149 , bytestring
144 , bytestring
150 , filepath
145 , filepath
151 , hjsonpointer
146 , hjsonpointer
152 , hjsonschema
147 , hjsonschema
153 , profunctors
148 , profunctors
154− , protolude
149+ , semigroups
155 , text
150 , text
156 , unordered-containers
151 , unordered-containers
157 , vector
152 , vector
158−
159 , directory
153 , directory
160− , hspec
154+ , HUnit
155+ , tasty
156+ , tasty-hunit
161 , wai-app-static
157 , wai-app-static
162 , warp
158 , warp
159+
160+source-repository head
161+ type: git
162+ location: git://github.com/seagreen/hjsonschema.git
logo.jpgnew file

Binary file not shown.

shell.nix+4−7
@@ -1,15 +1,12 @@
1−{ghc}:
2with (import <nixpkgs> {});
1with (import <nixpkgs> {});
3
2
4haskell.lib.buildStackProject {
3haskell.lib.buildStackProject {
5 name = "none";
4 name = "none";
6− inherit ghc;
5+ ghc = haskell.compiler.ghc801;
7 buildInputs = [
6 buildInputs = [
8− pcre
7+ zlib.dev
9−
8+ zlib.out
10− git # for `git:` references in stack.yml.
11− ncurses # for intero
12 pkgconfig
9 pkgconfig
13− zlib
10+ pcre
14 ];
11 ];
15}
12}
src/Data/JsonSchema/Draft4.hsnew file+185
@@ -0,0 +1,185 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+
3+module Data.JsonSchema.Draft4
4+ ( -- * Draft 4 Schema
5+ SchemaWithURI(..)
6+ , Schema(..)
7+ , SC.emptySchema
8+
9+ -- * One-step validation (getting references over HTTP)
10+ , fetchHTTPAndValidate
11+ , HTTPValidationFailure(..)
12+ , FE.HTTPFailure(..)
13+ , InvalidSchema
14+
15+ -- * One-step validation (getting references from the filesystem)
16+ , fetchFilesystemAndValidate
17+ , FilesystemValidationFailure(..)
18+ , FE.FilesystemFailure(..)
19+
20+ -- * Validation failure
21+ , Invalid
22+ , Failure
23+ , FR.Fail(..)
24+ , ValidatorChain(..)
25+
26+ -- * Fetching tools
27+ , ReferencedSchemas(..)
28+ , referencesViaHTTP
29+ , referencesViaFilesystem
30+
31+ -- * Other Draft 4 things exported just in case
32+ , metaSchema
33+ , metaSchemaBytes
34+ , schemaValidity
35+ , referencesValidity
36+ , checkSchema
37+ , draft4FetchInfo
38+ ) where
39+
40+import Import
41+import Prelude
42+
43+import Control.Arrow (first, left)
44+import qualified Data.ByteString as BS
45+import qualified Data.ByteString.Lazy as LBS
46+import Data.FileEmbed (embedFile,
47+ makeRelativeToProject)
48+import qualified Data.HashMap.Strict as HM
49+import qualified Data.List.NonEmpty as NE
50+import Data.Maybe (fromMaybe)
51+
52+import Data.JsonSchema.Draft4.Failure (Failure, Invalid,
53+ InvalidSchema,
54+ ValidatorChain(..))
55+import Data.JsonSchema.Draft4.Schema (Schema)
56+import qualified Data.JsonSchema.Draft4.Schema as SC
57+import qualified Data.JsonSchema.Draft4.Spec as Spec
58+import Data.JsonSchema.Fetch (ReferencedSchemas(..),
59+ SchemaWithURI(..))
60+import qualified Data.JsonSchema.Fetch as FE
61+import qualified Data.Validator.Failure as FR
62+
63+data HTTPValidationFailure
64+ = HVRequest FE.HTTPFailure
65+ | HVSchema InvalidSchema
66+ | HVData Invalid
67+ deriving Show
68+
69+-- | Fetch recursively referenced schemas over HTTP, check that both the
70+-- original and referenced schemas are valid, and then validate data.
71+fetchHTTPAndValidate
72+ :: SchemaWithURI Schema
73+ -> Value
74+ -> IO (Either HTTPValidationFailure ())
75+fetchHTTPAndValidate sw v = do
76+ res <- referencesViaHTTP sw
77+ pure (g =<< f =<< left HVRequest res)
78+ where
79+ f :: FE.URISchemaMap Schema
80+ -> Either HTTPValidationFailure (Value -> [Failure])
81+ f references = left HVSchema (checkSchema references sw)
82+
83+ g :: (Value -> [Failure]) -> Either HTTPValidationFailure ()
84+ g validate = case NE.nonEmpty (validate v) of
85+ Nothing -> Right ()
86+ Just failures -> Left (HVData failures)
87+
88+data FilesystemValidationFailure
89+ = FVRead FE.FilesystemFailure
90+ | FVSchema InvalidSchema
91+ | FVData Invalid
92+ deriving (Show, Eq)
93+
94+-- | Fetch recursively referenced schemas from the filesystem, check that
95+-- both the original and referenced schemas are valid, and then
96+-- validate data.
97+fetchFilesystemAndValidate
98+ :: SchemaWithURI Schema
99+ -> Value
100+ -> IO (Either FilesystemValidationFailure ())
101+fetchFilesystemAndValidate sw v = do
102+ res <- referencesViaFilesystem sw
103+ pure (g =<< f =<< left FVRead res)
104+ where
105+ f :: FE.URISchemaMap Schema
106+ -> Either FilesystemValidationFailure (Value -> [Failure])
107+ f references = left FVSchema (checkSchema references sw)
108+
109+ g :: (Value -> [Failure]) -> Either FilesystemValidationFailure ()
110+ g validate = case NE.nonEmpty (validate v) of
111+ Nothing -> Right ()
112+ Just invalid -> Left (FVData invalid)
113+
114+-- | An instance of 'Data.JsonSchema.Fetch.FetchInfo' specialized for
115+-- JSON Schema Draft 4.
116+draft4FetchInfo :: FE.FetchInfo Schema
117+draft4FetchInfo = FE.FetchInfo Spec.embedded SC._schemaId SC._schemaRef
118+
119+-- | Fetch the schemas recursively referenced by a starting schema over HTTP.
120+referencesViaHTTP
121+ :: SchemaWithURI Schema
122+ -> IO (Either FE.HTTPFailure (FE.URISchemaMap Schema))
123+referencesViaHTTP = FE.referencesViaHTTP' draft4FetchInfo
124+
125+-- | Fetch the schemas recursively referenced by a starting schema from
126+-- the filesystem.
127+referencesViaFilesystem
128+ :: SchemaWithURI Schema
129+ -> IO (Either FE.FilesystemFailure (FE.URISchemaMap Schema))
130+referencesViaFilesystem = FE.referencesViaFilesystem' draft4FetchInfo
131+
132+-- | A helper function.
133+--
134+-- Checks if a schema and a set of referenced schemas are valid.
135+--
136+-- Return a function to validate data.
137+checkSchema
138+ :: FE.URISchemaMap Schema
139+ -> SchemaWithURI Schema
140+ -> Either InvalidSchema (Value -> [Failure])
141+checkSchema sm sw =
142+ case NE.nonEmpty failures of
143+ Nothing -> Right (Spec.validate (ReferencedSchemas (_swSchema sw) sm) sw)
144+ Just fs -> Left fs
145+ where
146+ failures :: [(Maybe Text, Failure)]
147+ failures = ((\v -> (Nothing, v)) <$> schemaValidity (_swSchema sw))
148+ <> (first Just <$> referencesValidity sm)
149+
150+metaSchema :: Schema
151+metaSchema =
152+ fromMaybe (error "Schema decode failed (this should never happen)")
153+ . decode
154+ . LBS.fromStrict
155+ $ metaSchemaBytes
156+
157+metaSchemaBytes :: BS.ByteString
158+metaSchemaBytes =
159+ $(makeRelativeToProject "src/draft4.json" >>= embedFile)
160+
161+-- | Check that a schema itself is valid
162+-- (if so the returned list will be empty).
163+schemaValidity :: Schema -> [Failure]
164+schemaValidity =
165+ Spec.validate referenced (SchemaWithURI metaSchema Nothing) . toJSON
166+ where
167+ referenced :: ReferencedSchemas Schema
168+ referenced = ReferencedSchemas
169+ metaSchema
170+ (HM.singleton "http://json-schema.org/draft-04/schema"
171+ metaSchema)
172+
173+-- | Check that a set of referenced schemas are valid
174+-- (if so the returned list will be empty).
175+referencesValidity
176+ :: FE.URISchemaMap Schema
177+ -> [(Text, Failure)]
178+ -- ^ The first value in the tuple is the URI of a referenced schema.
179+referencesValidity = HM.foldlWithKey' f mempty
180+ where
181+ f :: [(Text, Failure)]
182+ -> Text
183+ -> Schema
184+ -> [(Text, Failure)]
185+ f acc k v = ((\a -> (k,a)) <$> schemaValidity v) <> acc
src/Data/JsonSchema/Draft4/Failure.hsnew file+118
@@ -0,0 +1,118 @@
1+
2+module Data.JsonSchema.Draft4.Failure where
3+
4+import Prelude
5+
6+import Data.List.NonEmpty (NonEmpty)
7+import Data.Text (Text)
8+
9+import qualified Data.Validator.Draft4.Any as AN
10+import qualified Data.Validator.Draft4.Array as AR
11+import qualified Data.Validator.Draft4.Number as NU
12+import qualified Data.Validator.Draft4.Object as OB
13+import qualified Data.Validator.Failure as FR
14+
15+-- | A description of why a schema (or one of its reference) is itself
16+-- invalid.
17+--
18+-- 'Nothing' indicates the starting schema. 'Just' indicates a referenced
19+-- schema -- the contents of the 'Just' is the schema's URI.
20+--
21+-- NOTE: 'HashMap (Maybe Text) Invalid' would be a nicer way of defining
22+-- this, but then we lose the guarantee that there's at least one key.
23+type InvalidSchema = NonEmpty (Maybe Text, Failure)
24+
25+type Invalid = NonEmpty Failure
26+
27+type Failure = FR.Fail ValidatorChain
28+
29+-- | Distinguish all the different possible causes of failure for
30+-- Draft 4 validation.
31+data ValidatorChain
32+ = MultipleOf
33+ | Maximum
34+ | ExclusiveMaximum
35+ | Minimum
36+ | ExclusiveMinimum
37+
38+ | MaxLength
39+ | MinLength
40+ | PatternValidator
41+
42+ | MaxItems
43+ | MinItems
44+ | UniqueItems
45+ | Items ValidatorChain
46+ | AdditionalItemsBool
47+ | AdditionalItemsObject ValidatorChain
48+
49+ | MaxProperties
50+ | MinProperties
51+ | Required
52+ | SchemaDependency ValidatorChain
53+ | PropertyDependency
54+ | Properties ValidatorChain
55+ | PatternProperties ValidatorChain
56+ | AdditionalPropertiesBool
57+ | AdditionalPropertiesObject ValidatorChain
58+
59+ | RefResolution
60+ -- ^ Indicates a reference that failed to resolve.
61+ --
62+ -- NOTE: The language agnostic test suite doesn't specify if this should
63+ -- cause a validation error or should allow data to pass. We choose to
64+ -- return a validation error.
65+ --
66+ -- Also note that ideally we would enforce in the type system that any
67+ -- failing references be dealt with before valididation. Then this could
68+ -- be removed entirely.
69+ | RefLoop
70+ | Ref ValidatorChain
71+ | Enum
72+ | TypeValidator
73+ | AllOf ValidatorChain
74+ | AnyOf
75+ | OneOf
76+ | NotValidator
77+ deriving (Eq, Show)
78+
79+maxE :: NU.MaximumInvalid -> ValidatorChain
80+maxE NU.Maximum = Maximum
81+maxE NU.ExclusiveMaximum = ExclusiveMaximum
82+
83+minE :: NU.MinimumInvalid -> ValidatorChain
84+minE NU.Minimum = Minimum
85+minE NU.ExclusiveMinimum = ExclusiveMinimum
86+
87+itemsE :: AR.ItemsInvalid ValidatorChain -> ValidatorChain
88+itemsE (AR.Items err) = Items err
89+itemsE AR.AdditionalItemsBoolInvalid = AdditionalItemsBool
90+itemsE (AR.AdditionalItemsObjectInvalid err) = AdditionalItemsObject err
91+
92+depsE :: OB.DependencyInvalid ValidatorChain -> ValidatorChain
93+depsE (OB.SchemaDependencyInvalid err) = SchemaDependency err
94+depsE OB.PropertyDependencyInvalid = PropertyDependency
95+
96+propE :: OB.PropertiesInvalid ValidatorChain -> ValidatorChain
97+propE (OB.PropertiesInvalid err) = Properties err
98+propE (OB.PropPatternInvalid err) = PatternProperties err
99+propE (OB.PropAdditionalInvalid a) =
100+ case a of
101+ OB.APBoolInvalid -> AdditionalPropertiesBool
102+ OB.APObjectInvalid err -> AdditionalPropertiesObject err
103+
104+patPropE :: OB.PatternPropertiesInvalid ValidatorChain -> ValidatorChain
105+patPropE (OB.PPInvalid err) = PatternProperties err
106+patPropE (OB.PPAdditionalPropertiesInvalid a) =
107+ case a of
108+ OB.APBoolInvalid -> AdditionalPropertiesBool
109+ OB.APObjectInvalid err -> AdditionalPropertiesObject err
110+
111+addPropE :: OB.AdditionalPropertiesInvalid ValidatorChain -> ValidatorChain
112+addPropE OB.APBoolInvalid = AdditionalPropertiesBool
113+addPropE (OB.APObjectInvalid err) = AdditionalItemsObject err
114+
115+refE :: AN.RefInvalid ValidatorChain -> ValidatorChain
116+refE AN.RefResolution = RefResolution
117+refE AN.RefLoop = RefLoop
118+refE (AN.RefInvalid err) = Ref err
src/JSONSchema/Draft4/Schema.hs → src/Data/JsonSchema/Draft4/Schema.hsrenamed+23−26
@@ -1,16 +1,18 @@
1−module JSONSchema.Draft4.Schema where
2
1
3−import Import hiding (mapMaybe)
2+module Data.JsonSchema.Draft4.Schema where
4
3
4+import Import
5+import Prelude
6+
5−import qualified Data.HashMap.Strict as HM
7+import qualified Data.HashMap.Strict as HM
6−import Data.List.NonEmpty (NonEmpty)
8+import Data.List.NonEmpty (NonEmpty)
7−import Data.Maybe (fromJust, isJust)
9+import Data.Maybe (fromJust, isJust)
8import Data.Scientific
10import Data.Scientific
9−import qualified Data.Set as Set
10−import qualified Data.Text as T
11
11
12−import qualified JSONSchema.Validator.Draft4 as D4
12+import qualified Data.Validator.Draft4.Any as AN
13+import qualified Data.Validator.Draft4.Array as AR
14+import qualified Data.Validator.Draft4.Object as OB
13−import JSONSchema.Validator.Utils
15+import Data.Validator.Utils
14
16
15data Schema = Schema
17data Schema = Schema
16 { _schemaVersion :: Maybe Text
18 { _schemaVersion :: Maybe Text
@@ -24,10 +26,6 @@ data Schema = Schema
24 -- contain schemas anywhere (not just in "definitions" or any
26 -- contain schemas anywhere (not just in "definitions" or any
25 -- of the other official keys) we save any leftover key/value
27 -- of the other official keys) we save any leftover key/value
26 -- pairs not covered by them here.
28 -- pairs not covered by them here.
27− --
28− -- TODO: This field is the source of most of the complication in this
29− -- module and needs to be removed. It should be doable, though it will
30− -- involve some modification to the fetching code.
31
29
32 , _schemaMultipleOf :: Maybe Scientific
30 , _schemaMultipleOf :: Maybe Scientific
33 , _schemaMaximum :: Maybe Scientific
31 , _schemaMaximum :: Maybe Scientific
@@ -42,22 +40,22 @@ data Schema = Schema
42 , _schemaMaxItems :: Maybe Int
40 , _schemaMaxItems :: Maybe Int
43 , _schemaMinItems :: Maybe Int
41 , _schemaMinItems :: Maybe Int
44 , _schemaUniqueItems :: Maybe Bool
42 , _schemaUniqueItems :: Maybe Bool
45− , _schemaItems :: Maybe (D4.Items Schema)
43+ , _schemaItems :: Maybe (AR.Items Schema)
46 -- Note that '_schemaAdditionalItems' is left out of 'runValidate'
44 -- Note that '_schemaAdditionalItems' is left out of 'runValidate'
47 -- because its functionality is handled by '_schemaItems'. It always
45 -- because its functionality is handled by '_schemaItems'. It always
48 -- validates data unless 'Items' is present.
46 -- validates data unless 'Items' is present.
49− , _schemaAdditionalItems :: Maybe (D4.AdditionalItems Schema)
47+ , _schemaAdditionalItems :: Maybe (AR.AdditionalItems Schema)
50
48
51 , _schemaMaxProperties :: Maybe Int
49 , _schemaMaxProperties :: Maybe Int
52 , _schemaMinProperties :: Maybe Int
50 , _schemaMinProperties :: Maybe Int
53− , _schemaRequired :: Maybe (Set Text)
51+ , _schemaRequired :: Maybe OB.Required
54− , _schemaDependencies :: Maybe (HashMap Text (D4.Dependency Schema))
52+ , _schemaDependencies :: Maybe (HashMap Text (OB.Dependency Schema))
55 , _schemaProperties :: Maybe (HashMap Text Schema)
53 , _schemaProperties :: Maybe (HashMap Text Schema)
56 , _schemaPatternProperties :: Maybe (HashMap Text Schema)
54 , _schemaPatternProperties :: Maybe (HashMap Text Schema)
57− , _schemaAdditionalProperties :: Maybe (D4.AdditionalProperties Schema)
55+ , _schemaAdditionalProperties :: Maybe (OB.AdditionalProperties Schema)
58
56
59− , _schemaEnum :: Maybe (NonEmpty Value)
57+ , _schemaEnum :: Maybe AN.EnumVal
60− , _schemaType :: Maybe D4.TypeValidator
58+ , _schemaType :: Maybe AN.TypeVal
61 , _schemaAllOf :: Maybe (NonEmpty Schema)
59 , _schemaAllOf :: Maybe (NonEmpty Schema)
62 , _schemaAnyOf :: Maybe (NonEmpty Schema)
60 , _schemaAnyOf :: Maybe (NonEmpty Schema)
63 , _schemaOneOf :: Maybe (NonEmpty Schema)
61 , _schemaOneOf :: Maybe (NonEmpty Schema)
@@ -264,9 +262,9 @@ instance Arbitrary Schema where
264 a <- maybeGen arbitraryText
262 a <- maybeGen arbitraryText
265 b <- maybeGen arbitraryText
263 b <- maybeGen arbitraryText
266 c <- maybeGen arbitraryText
264 c <- maybeGen arbitraryText
267− -- NOTE: The next two fields are empty to generate cleaner
265+ -- NOTE: The next two fields are empty to generate cleaner schemas,
268− -- schemas, but note that this means we don't test the
266+ -- but note that this means we don't test e.g. the invertability
269− -- invertability of these fields.
267+ -- of these fields.
270 d <- pure Nothing -- _schemaDefinitions
268 d <- pure Nothing -- _schemaDefinitions
271 e <- pure mempty -- _otherPairs
269 e <- pure mempty -- _otherPairs
272
270
@@ -288,14 +286,13 @@ instance Arbitrary Schema where
288
286
289 s <- maybeGen (getPositive <$> arbitrary)
287 s <- maybeGen (getPositive <$> arbitrary)
290 t <- maybeGen (getPositive <$> arbitrary)
288 t <- maybeGen (getPositive <$> arbitrary)
291− u <- maybeGen (Set.map T.pack <$> arbitrary)
289+ u <- arbitrary
292 v <- maybeRecurse n arbitraryHashMap
290 v <- maybeRecurse n arbitraryHashMap
293 w <- maybeRecurse n arbitraryHashMap
291 w <- maybeRecurse n arbitraryHashMap
294 x <- maybeRecurse n arbitraryHashMap
292 x <- maybeRecurse n arbitraryHashMap
295 y <- maybeRecurse n arbitrary
293 y <- maybeRecurse n arbitrary
296
294
297− z <- maybeRecurse n ( fmap _unArbitraryValue . _unNonEmpty'
298− <$> arbitrary)
295+ z <- arbitrary
299 a2 <- arbitrary
296 a2 <- arbitrary
300 b2 <- maybeRecurse n (_unNonEmpty' <$> arbitrary)
297 b2 <- maybeRecurse n (_unNonEmpty' <$> arbitrary)
301 c2 <- maybeRecurse n (_unNonEmpty' <$> arbitrary)
298 c2 <- maybeRecurse n (_unNonEmpty' <$> arbitrary)
src/Data/JsonSchema/Draft4/Spec.hsnew file+130
@@ -0,0 +1,130 @@
1+
2+module Data.JsonSchema.Draft4.Spec where
3+
4+import Import
5+-- Hiding is for GHCs before 7.10:
6+import Prelude hiding (concat)
7+
8+import Data.Maybe (fromMaybe, isNothing)
9+import Data.Profunctor (Profunctor(..))
10+
11+import Data.JsonSchema.Draft4.Failure
12+import Data.JsonSchema.Draft4.Schema (Schema(..))
13+import Data.JsonSchema.Fetch (ReferencedSchemas(..),
14+ SchemaWithURI(..))
15+import qualified Data.JsonSchema.Fetch as FE
16+import Data.JsonSchema.Types (Spec(..))
17+import qualified Data.JsonSchema.Types as JT
18+import qualified Data.Validator.Draft4 as D4
19+import qualified Data.Validator.Draft4.Any as AN
20+import Data.Validator.Reference (updateResolutionScope)
21+
22+embedded :: Schema -> ([Schema], [Schema])
23+embedded s = JT.embedded (d4Spec (ReferencedSchemas s mempty) mempty Nothing) s
24+
25+validate
26+ :: ReferencedSchemas Schema
27+ -> SchemaWithURI Schema
28+ -> Value
29+ -> [Failure]
30+validate rs = continueValidating rs (AN.VisitedSchemas [(Nothing, Nothing)])
31+
32+continueValidating
33+ :: ReferencedSchemas Schema
34+ -> AN.VisitedSchemas
35+ -> SchemaWithURI Schema
36+ -> Value
37+ -> [Failure]
38+continueValidating referenced visited sw =
39+ JT.validate (d4Spec referenced visited currentScope)
40+ (_swSchema sw)
41+ where
42+ currentScope :: Maybe Text
43+ currentScope = updateResolutionScope
44+ (_swURI sw)
45+ (_schemaId (_swSchema sw))
46+
47+d4Spec
48+ :: ReferencedSchemas Schema
49+ -> AN.VisitedSchemas
50+ -> Maybe Text
51+ -> Spec Schema ValidatorChain
52+d4Spec referenced visited scope = Spec
53+ [ dimap
54+ (fmap D4.MultipleOf . _schemaMultipleOf)
55+ (const MultipleOf)
56+ D4.multipleOf
57+ , dimap
58+ (\s -> D4.MaximumContext (fromMaybe False (_schemaExclusiveMaximum s))
59+ <$> _schemaMaximum s)
60+ maxE
61+ D4.maximumVal
62+ , dimap
63+ (\s -> D4.MinimumContext (fromMaybe False (_schemaExclusiveMinimum s))
64+ <$> _schemaMinimum s)
65+ minE
66+ D4.minimumVal
67+
68+ , dimap (fmap D4.MaxLength . _schemaMaxLength) (const MaxLength) D4.maxLength
69+ , dimap (fmap D4.MinLength . _schemaMinLength) (const MinLength) D4.minLength
70+ , dimap (fmap D4.PatternVal . _schemaPattern) (const PatternValidator) D4.patternVal
71+
72+ , dimap (fmap D4.MaxItems . _schemaMaxItems) (const MaxItems) D4.maxItems
73+ , dimap (fmap D4.MinItems . _schemaMinItems) (const MinItems) D4.minItems
74+ , dimap (fmap D4.UniqueItems . _schemaUniqueItems) (const UniqueItems) D4.uniqueItems
75+ , dimap
76+ (\s -> D4.ItemsContext (_schemaAdditionalItems s) <$> _schemaItems s)
77+ itemsE
78+ (D4.items descend)
79+ , lmap (fmap D4.AdditionalItemsContext . _schemaAdditionalItems) D4.additionalItemsEmbedded
80+ , lmap (fmap D4.Definitions . _schemaDefinitions) D4.definitionsEmbedded
81+
82+ , dimap (fmap D4.MaxProperties . _schemaMaxProperties) (const MaxProperties) D4.maxProperties
83+ , dimap (fmap D4.MinProperties . _schemaMinProperties) (const MinProperties) D4.minProperties
84+ , dimap (fmap D4.RequiredContext . _schemaRequired) (const Required) D4.required
85+ , dimap (fmap D4.DependenciesContext . _schemaDependencies) depsE (D4.dependencies descend)
86+ , dimap
87+ (\s -> D4.PropertiesContext
88+ (_schemaPatternProperties s)
89+ (_schemaAdditionalProperties s)
90+ <$> _schemaProperties s)
91+ propE
92+ (D4.properties descend)
93+ , dimap
94+ (\s -> D4.PatternPropertiesContext
95+ (isNothing (_schemaProperties s))
96+ (_schemaAdditionalProperties s)
97+ <$> _schemaPatternProperties s)
98+ patPropE
99+ (D4.patternProperties descend)
100+ , dimap
101+ (\s -> D4.AdditionalPropertiesContext
102+ (isNothing (_schemaProperties s)
103+ && isNothing (_schemaPatternProperties s))
104+ <$> _schemaAdditionalProperties s)
105+ addPropE
106+ (D4.additionalProperties descend)
107+
108+ , dimap
109+ (\s -> D4.Ref <$> _schemaRef s)
110+ refE
111+ (D4.ref visited scope (FE.getReference referenced) refVal)
112+ , dimap (fmap D4.EnumContext . _schemaEnum) (const Enum) D4.enumVal
113+ , dimap (fmap D4.TypeContext . _schemaType) (const TypeValidator) D4.typeVal
114+ , dimap (fmap D4.AllOf . _schemaAllOf) AllOf (D4.allOf lateral)
115+ , dimap (fmap D4.AnyOf . _schemaAnyOf) (const AnyOf) (D4.anyOf lateral)
116+ , dimap (fmap D4.OneOf . _schemaOneOf) (const OneOf) (D4.oneOf lateral)
117+ , dimap (fmap D4.NotVal . _schemaNot) (const NotValidator) (D4.notVal lateral)
118+ ]
119+ where
120+ refVal :: AN.VisitedSchemas -> Maybe Text -> Schema -> Value -> [Failure]
121+ refVal newVisited newScope schema =
122+ continueValidating referenced newVisited (SchemaWithURI schema newScope)
123+
124+ descend :: Schema -> Value -> [Failure]
125+ descend schema =
126+ continueValidating referenced mempty (SchemaWithURI schema scope)
127+
128+ lateral :: Schema -> Value -> [Failure]
129+ lateral schema =
130+ continueValidating referenced visited (SchemaWithURI schema scope)
src/Data/JsonSchema/Fetch.hsnew file+194
@@ -0,0 +1,194 @@
1+
2+module Data.JsonSchema.Fetch where
3+
4+import Import
5+-- Hiding is for GHCs before 7.10:
6+import Prelude hiding (concat, sequence)
7+
8+import Control.Arrow (left)
9+import Control.Exception (catch)
10+import Control.Monad (foldM)
11+import qualified Data.ByteString.Lazy as LBS
12+import qualified Data.ByteString as BS
13+import qualified Data.HashMap.Strict as H
14+import qualified Data.Text as T
15+import qualified Network.HTTP.Client as NC
16+
17+import Data.Validator.Reference (resolveReference,
18+ updateResolutionScope)
19+
20+--------------------------------------------------
21+-- * Types
22+--------------------------------------------------
23+
24+-- | This is all the fetching functions need to know about a particular
25+-- JSON Schema draft, e.g. JSON Schema Draft 4.
26+data FetchInfo schema = FetchInfo
27+ { _fiEmbedded :: schema -> ([schema], [schema])
28+ , _fiId :: schema -> Maybe Text
29+ , _fiRef :: schema -> Maybe Text
30+ }
31+
32+data ReferencedSchemas schema = ReferencedSchemas
33+ { _rsStarting :: !schema
34+ -- ^ Used to resolve relative references when we don't know what the scope
35+ -- of the current schema is. This only happens with starting schemas
36+ -- because if we're using a remote schema we had to know its URI in order
37+ -- to fetch it.
38+ --
39+ -- Tracking the starting schema (instead of just resolving the reference to
40+ -- the current schema being used for validation) is necessary for cases
41+ -- where schemas are embedded inside one another. For instance in this
42+ -- case not distinguishing the starting and "foo" schemas sends the code
43+ -- into an infinite loop:
44+ --
45+ -- {
46+ -- "additionalProperties": false,
47+ -- "properties": {
48+ -- "foo": {
49+ -- "$ref": "#"
50+ -- }
51+ -- }
52+ -- }
53+ , _rsSchemaMap :: !(URISchemaMap schema)
54+ } deriving (Eq, Show)
55+
56+-- | Keys are URIs (without URI fragments).
57+type URISchemaMap schema = HashMap Text schema
58+
59+data SchemaWithURI schema = SchemaWithURI
60+ { _swSchema :: !schema
61+ , _swURI :: !(Maybe Text)
62+ -- ^ This is the URI identifying the document containing the schema.
63+ -- It's different than the schema's "id" field, which controls scope
64+ -- when resolving references contained in the schema.
65+
66+ -- TODO: Make the no URI fragment requirement unnecessary.
67+ } deriving (Eq, Show)
68+
69+getReference :: ReferencedSchemas schema -> Maybe Text -> Maybe schema
70+getReference referenced Nothing = Just (_rsStarting referenced)
71+getReference referenced (Just t) = H.lookup t (_rsSchemaMap referenced)
72+
73+--------------------------------------------------
74+-- * Fetch via HTTP
75+--------------------------------------------------
76+
77+data HTTPFailure
78+ = HTTPParseFailure Text
79+ | HTTPRequestFailure NC.HttpException
80+ deriving Show
81+
82+-- | Take a schema. Retrieve every document either it or its subschemas
83+-- include via the "$ref" keyword.
84+referencesViaHTTP'
85+ :: forall schema. FromJSON schema
86+ => FetchInfo schema
87+ -> SchemaWithURI schema
88+ -> IO (Either HTTPFailure (URISchemaMap schema))
89+referencesViaHTTP' info sw = do
90+ manager <- NC.newManager NC.defaultManagerSettings
91+ let f = referencesMethodAgnostic (get manager) info sw
92+ catch (left HTTPParseFailure <$> f) handler
93+ where
94+ get :: NC.Manager -> Text -> IO LBS.ByteString
95+ get man url = do
96+ request <- NC.parseUrlThrow (T.unpack url)
97+ NC.responseBody <$> NC.httpLbs request man
98+
99+ handler
100+ :: NC.HttpException
101+ -> IO (Either HTTPFailure (URISchemaMap schema))
102+ handler = pure . Left . HTTPRequestFailure
103+
104+--------------------------------------------------
105+-- * Fetch via Filesystem
106+--------------------------------------------------
107+
108+data FilesystemFailure
109+ = FSParseFailure Text
110+ | FSReadFailure IOError
111+ deriving (Show, Eq)
112+
113+referencesViaFilesystem'
114+ :: forall schema. FromJSON schema
115+ => FetchInfo schema
116+ -> SchemaWithURI schema
117+ -> IO (Either FilesystemFailure (URISchemaMap schema))
118+referencesViaFilesystem' info sw = catch (left FSParseFailure <$> f) handler
119+ where
120+ f :: IO (Either Text (URISchemaMap schema))
121+ f = referencesMethodAgnostic readFile' info sw
122+
123+ readFile' :: Text -> IO LBS.ByteString
124+ readFile' = fmap LBS.fromStrict . BS.readFile . T.unpack
125+
126+ handler
127+ :: IOError
128+ -> IO (Either FilesystemFailure (URISchemaMap schema))
129+ handler = pure . Left . FSReadFailure
130+
131+--------------------------------------------------
132+-- * Method Agnostic Fetching Tools
133+--------------------------------------------------
134+
135+-- | A version of 'fetchReferencedSchema's where the function to fetch
136+-- schemas is provided by the user. This allows restrictions to be added,
137+-- e.g. rejecting non-local URIs.
138+referencesMethodAgnostic
139+ :: forall schema. FromJSON schema
140+ => (Text -> IO LBS.ByteString)
141+ -> FetchInfo schema
142+ -> SchemaWithURI schema
143+ -> IO (Either Text (URISchemaMap schema))
144+referencesMethodAgnostic fetchRef info =
145+ getRecursiveReferences fetchRef info mempty
146+
147+getRecursiveReferences
148+ :: forall schema. FromJSON schema
149+ => (Text -> IO LBS.ByteString)
150+ -> FetchInfo schema
151+ -> URISchemaMap schema
152+ -> SchemaWithURI schema
153+ -> IO (Either Text (URISchemaMap schema))
154+getRecursiveReferences fetchRef info referenced sw =
155+ foldM f (Right referenced) (includeSubschemas info sw)
156+ where
157+ f :: Either Text (URISchemaMap schema)
158+ -> SchemaWithURI schema
159+ -> IO (Either Text (URISchemaMap schema))
160+ f (Left e) _ = pure (Left e)
161+ f (Right g) (SchemaWithURI schema mUri) =
162+ case newRef of
163+ Nothing -> pure (Right g)
164+ Just uri -> do
165+ bts <- fetchRef uri
166+ case eitherDecode bts of
167+ Left e -> pure . Left . T.pack $ e
168+ Right schm -> getRecursiveReferences
169+ fetchRef info (H.insert uri schm g)
170+ (SchemaWithURI schm (Just uri))
171+ where
172+ newRef :: Maybe Text
173+ newRef
174+ | Just (Just uri,_) <- resolveReference mUri <$> _fiRef info schema
175+ = case H.lookup uri g of
176+ Nothing -> Just uri
177+ Just _ -> Nothing
178+ | otherwise = Nothing
179+
180+-- | Return the schema passed in as an argument, as well as every
181+-- subschema contained within it.
182+includeSubschemas
183+ :: forall schema.
184+ FetchInfo schema
185+ -> SchemaWithURI schema
186+ -> [SchemaWithURI schema]
187+includeSubschemas info (SchemaWithURI schema mUri) =
188+ SchemaWithURI schema mUri
189+ : (includeSubschemas info =<< subSchemas)
190+ where
191+ subSchemas :: [SchemaWithURI schema]
192+ subSchemas =
193+ (\a -> SchemaWithURI a (updateResolutionScope mUri (_fiId info schema)))
194+ <$> uncurry (<>) (_fiEmbedded info schema)
src/JSONSchema/Types.hs → src/Data/JsonSchema/Types.hsrenamed+11−8
@@ -1,8 +1,12 @@
1−module JSONSchema.Types where
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2
2
3+module Data.JsonSchema.Types where
4+
5+import Prelude
3import Import
6import Import
4
7
5−import JSONSchema.Validator.Types (Validator(..))
8+import Data.Validator.Failure (Fail)
9+import Data.Validator.Types (Validator(..))
6
10
7newtype Spec schema err
11newtype Spec schema err
8 = Spec { _unSpec :: [Validator schema schema err] }
12 = Spec { _unSpec :: [Validator schema schema err] }
@@ -11,7 +15,7 @@ newtype Spec schema err
11--
15--
12-- The first list is subschemas validating the same level of the document,
16-- The first list is subschemas validating the same level of the document,
13-- the second list is subschemas validating lower levels (see
17-- the second list is subschemas validating lower levels (see
14−-- 'JSONSchema.Validator.Types.Fail' for a full explanation).
18+-- 'Data.Validator.Types.Fail' for a full explanation).
15embedded :: Spec schema a -> schema -> ([schema], [schema])
19embedded :: Spec schema a -> schema -> ([schema], [schema])
16embedded spec schema =
20embedded spec schema =
17 let embeds = (\val -> _embedded val schema) <$> _unSpec spec
21 let embeds = (\val -> _embedded val schema) <$> _unSpec spec
@@ -21,14 +25,13 @@ validate
21 :: Spec schema err
25 :: Spec schema err
22 -> schema
26 -> schema
23 -> Value
27 -> Value
24− -> [err]
28+ -> [Fail err]
25−validate spec schema v =
29+validate spec schema v = _unSpec spec >>= (\val -> _validate val schema v)
26− (\val -> _validate val schema v) =<< _unSpec spec
27
30
28-- | A basic schema type that doesn't impose much structure.
31-- | A basic schema type that doesn't impose much structure.
29--
32--
30−-- 'JSONSchema.Draft4' doesn't use this, but instead uses the record based
33+-- 'Data.JsonSchema.Draft4' doesn't use this, but instead uses the one
31−-- one defined in 'JSONSchema.Draft4.Schema' to make it easier to write
34+-- defined in 'Data.JsonSchema.Draft4.Schema' to make it easier to write
32-- draft 4 schemas in Haskell.
35-- draft 4 schemas in Haskell.
33newtype Schema
36newtype Schema
34 = Schema { _unSchema :: HashMap Text Value }
37 = Schema { _unSchema :: HashMap Text Value }
src/Data/Validator/Draft4.hsnew file+551
@@ -0,0 +1,551 @@
1+-- | Turn the validation functions into actual 'Validator's.
2+--
3+-- From this point on they know how to parse themselves from JSON
4+-- and also know how to extract subschemas embedded within themselves.
5+
6+module Data.Validator.Draft4 where
7+
8+import Prelude
9+import Import
10+
11+import Data.Aeson.Types (Parser)
12+import qualified Data.HashMap.Strict as HM
13+import Data.List.NonEmpty (NonEmpty)
14+import qualified Data.List.NonEmpty as NE
15+import Data.Maybe (catMaybes, isNothing,
16+ maybe, maybeToList)
17+import Data.Scientific (Scientific)
18+import Data.Text (Text)
19+
20+import qualified Data.Validator.Draft4.Any as AN
21+import qualified Data.Validator.Draft4.Array as AR
22+import qualified Data.Validator.Draft4.Number as NU
23+import qualified Data.Validator.Draft4.Object as OB
24+import qualified Data.Validator.Draft4.String as ST
25+import Data.Validator.Failure (Fail(..))
26+import Data.Validator.Types (Validator(..))
27+import Data.Validator.Utils (fromJSONEither)
28+
29+-- | For internal use.
30+run :: FromJSON b => (a -> b -> [c]) -> Maybe a -> Value -> [c]
31+run _ Nothing _ = mempty
32+run f (Just a) b =
33+ case fromJSONEither b of
34+ Left _ -> mempty
35+ Right c -> f a c
36+
37+-- | For internal use.
38+noEmbedded :: a -> ([b], [b])
39+noEmbedded = const (mempty, mempty)
40+
41+--------------------------------------------------
42+-- * Numbers
43+--------------------------------------------------
44+
45+newtype MultipleOf
46+ = MultipleOf { _unMultipleOf :: Scientific }
47+ deriving (Eq, Show)
48+
49+instance FromJSON MultipleOf where
50+ parseJSON = withObject "MultipleOf" $ \o ->
51+ MultipleOf <$> o .: "multipleOf"
52+
53+multipleOf :: Validator a (Maybe MultipleOf) ()
54+multipleOf =
55+ Validator
56+ noEmbedded
57+ (run (fmap maybeToList . NU.multipleOf . _unMultipleOf))
58+
59+data MaximumContext
60+ = MaximumContext Bool Scientific
61+ deriving (Eq, Show)
62+
63+instance FromJSON MaximumContext where
64+ parseJSON = withObject "MaximumContext" $ \o -> MaximumContext
65+ <$> o .:! "exclusiveMaximum" .!= False
66+ <*> o .: "maximum"
67+
68+maximumVal :: Validator a (Maybe MaximumContext) NU.MaximumInvalid
69+maximumVal =
70+ Validator
71+ noEmbedded
72+ (run (\(MaximumContext a b) -> maybeToList . NU.maximumVal a b))
73+
74+data MinimumContext
75+ = MinimumContext Bool Scientific
76+ deriving (Eq, Show)
77+
78+instance FromJSON MinimumContext where
79+ parseJSON = withObject "MinimumContext" $ \o -> MinimumContext
80+ <$> o .:! "exclusiveMinimum" .!= False
81+ <*> o .: "minimum"
82+
83+minimumVal :: Validator a (Maybe MinimumContext) NU.MinimumInvalid
84+minimumVal =
85+ Validator
86+ noEmbedded
87+ (run (\(MinimumContext a b) -> maybeToList . NU.minimumVal a b))
88+
89+--------------------------------------------------
90+-- * Strings
91+--------------------------------------------------
92+
93+newtype MaxLength
94+ = MaxLength { _unMaxLength :: Int }
95+ deriving (Eq, Show)
96+
97+instance FromJSON MaxLength where
98+ parseJSON = withObject "MaxLength" $ \o ->
99+ MaxLength <$> o .: "maxLength"
100+
101+maxLength :: Validator a (Maybe MaxLength) ()
102+maxLength =
103+ Validator
104+ noEmbedded
105+ (run (fmap maybeToList . ST.maxLength . _unMaxLength))
106+
107+newtype MinLength
108+ = MinLength { _unMinLength :: Int }
109+ deriving (Eq, Show)
110+
111+instance FromJSON MinLength where
112+ parseJSON = withObject "MinLength" $ \o ->
113+ MinLength <$> o .: "minLength"
114+
115+minLength :: Validator a (Maybe MinLength) ()
116+minLength =
117+ Validator
118+ noEmbedded
119+ (run (fmap maybeToList . ST.minLength . _unMinLength))
120+
121+newtype PatternVal
122+ = PatternVal { _unPatternVal :: Text }
123+ deriving (Eq, Show)
124+
125+instance FromJSON PatternVal where
126+ parseJSON = withObject "PatternVal" $ \o ->
127+ PatternVal <$> o .: "pattern"
128+
129+patternVal :: Validator a (Maybe PatternVal) ()
130+patternVal =
131+ Validator
132+ noEmbedded
133+ (run (fmap maybeToList . ST.patternVal . _unPatternVal))
134+
135+--------------------------------------------------
136+-- * Arrays
137+--------------------------------------------------
138+
139+newtype MaxItems
140+ = MaxItems { _unMaxItems :: Int }
141+ deriving (Eq, Show)
142+
143+instance FromJSON MaxItems where
144+ parseJSON = withObject "MaxItems" $ \o ->
145+ MaxItems <$> o .: "maxItems"
146+
147+maxItems :: Validator a (Maybe MaxItems) ()
148+maxItems =
149+ Validator
150+ noEmbedded
151+ (run (fmap maybeToList . AR.maxItems . _unMaxItems))
152+
153+newtype MinItems
154+ = MinItems { _unMinItems :: Int }
155+ deriving (Eq, Show)
156+
157+instance FromJSON MinItems where
158+ parseJSON = withObject "MinItems" $ \o ->
159+ MinItems <$> o .: "minItems"
160+
161+minItems :: Validator a (Maybe MinItems) ()
162+minItems =
163+ Validator
164+ noEmbedded
165+ (run (fmap maybeToList . AR.minItems . _unMinItems))
166+
167+newtype UniqueItems
168+ = UniqueItems { _unUniqueItems :: Bool }
169+ deriving (Eq, Show)
170+
171+instance FromJSON UniqueItems where
172+ parseJSON = withObject "UniqueItems" $ \o ->
173+ UniqueItems <$> o .: "uniqueItems"
174+
175+uniqueItems :: Validator a (Maybe UniqueItems) ()
176+uniqueItems =
177+ Validator
178+ noEmbedded
179+ (run (fmap maybeToList . AR.uniqueItems . _unUniqueItems))
180+
181+data ItemsContext schema =
182+ ItemsContext
183+ (Maybe (AR.AdditionalItems schema))
184+ (AR.Items schema)
185+ deriving (Eq, Show)
186+
187+instance FromJSON schema => FromJSON (ItemsContext schema) where
188+ parseJSON = withObject "ItemsContext" $ \o -> ItemsContext
189+ <$> o .:! "additionalItems"
190+ <*> o .: "items"
191+
192+items
193+ :: (schema -> Value -> [Fail err])
194+ -> Validator schema (Maybe (ItemsContext schema)) (AR.ItemsInvalid err)
195+items f =
196+ Validator
197+ (\a -> case a of
198+ Nothing -> mempty
199+ Just (ItemsContext _ b) ->
200+ case b of
201+ AR.ItemsObject c -> (mempty, pure c)
202+ AR.ItemsArray cs -> (mempty, cs))
203+ (run (\(ItemsContext a b) -> AR.items f a b))
204+
205+newtype AdditionalItemsContext schema
206+ = AdditionalItemsContext
207+ { _unAdditionalItemsContext :: AR.AdditionalItems schema }
208+ deriving (Eq, Show)
209+
210+instance FromJSON schema => FromJSON (AdditionalItemsContext schema) where
211+ parseJSON = withObject "AdditionalItemsContext" $ \o ->
212+ AdditionalItemsContext <$> o .: "additionalItems"
213+
214+-- | Since 'items' will always take care of validating 'additionalItems'
215+-- as well, the actual validation side of 'additionalItemsEmbedded' is
216+-- disabled.
217+additionalItemsEmbedded
218+ :: Validator
219+ schema
220+ (Maybe (AdditionalItemsContext schema))
221+ err
222+additionalItemsEmbedded=
223+ Validator
224+ (\a -> case a of
225+ Just (AdditionalItemsContext (AR.AdditionalObject b)) ->
226+ (mempty, pure b)
227+ _ -> (mempty, mempty))
228+ (const (const mempty))
229+
230+newtype Definitions schema
231+ = Definitions { _unDefinitions :: HashMap Text schema }
232+ deriving (Eq, Show)
233+
234+instance FromJSON schema => FromJSON (Definitions schema) where
235+ parseJSON = withObject "Definitions" $ \o ->
236+ Definitions <$> o .: "definitions"
237+
238+-- | Placing this here since it's similar to @"additionalItems"@.
239+-- in that its validator doesn't run.
240+--
241+-- TODO: Add tests to the language agnostic test suite for both
242+-- @"additionalItems"@ and this.
243+definitionsEmbedded
244+ :: Validator
245+ schema
246+ (Maybe (Definitions schema))
247+ err
248+definitionsEmbedded =
249+ Validator
250+ (\a -> case a of
251+ Just (Definitions b) -> (mempty, HM.elems b)
252+ Nothing -> (mempty, mempty))
253+ (const (const mempty))
254+
255+--------------------------------------------------
256+-- * Objects
257+--------------------------------------------------
258+
259+newtype MaxProperties
260+ = MaxProperties { _unMaxProperties :: Int }
261+ deriving (Eq, Show)
262+
263+instance FromJSON MaxProperties where
264+ parseJSON = withObject "MaxProperties" $ \o ->
265+ MaxProperties <$> o .: "maxProperties"
266+
267+maxProperties :: Validator a (Maybe MaxProperties) ()
268+maxProperties =
269+ Validator
270+ noEmbedded
271+ (run (fmap maybeToList . OB.maxProperties . _unMaxProperties))
272+
273+newtype MinProperties
274+ = MinProperties { _unMinProperties :: Int }
275+ deriving (Eq, Show)
276+
277+instance FromJSON MinProperties where
278+ parseJSON = withObject "MinProperties" $ \o ->
279+ MinProperties <$> o .: "minProperties"
280+
281+minProperties :: Validator a (Maybe MinProperties) ()
282+minProperties =
283+ Validator
284+ noEmbedded
285+ (run (fmap maybeToList . OB.minProperties . _unMinProperties))
286+
287+newtype RequiredContext
288+ = RequiredContext { _unRequiredContext :: OB.Required }
289+ deriving (Eq, Show)
290+
291+instance FromJSON RequiredContext where
292+ parseJSON = withObject "RequiredContext" $ \o ->
293+ RequiredContext <$> o .: "required"
294+
295+required :: Validator a (Maybe RequiredContext) ()
296+required =
297+ Validator
298+ noEmbedded
299+ (run (fmap maybeToList . OB.required . _unRequiredContext))
300+
301+newtype DependenciesContext schema
302+ = DependenciesContext
303+ { _unDependenciesContext :: HashMap Text (OB.Dependency schema) }
304+ deriving (Eq, Show)
305+
306+instance FromJSON schema => FromJSON (DependenciesContext schema) where
307+ parseJSON = withObject "DependenciesContext" $ \o ->
308+ DependenciesContext <$> o .: "dependencies"
309+
310+dependencies
311+ :: (schema -> Value -> [Fail err])
312+ -> Validator
313+ schema
314+ (Maybe (DependenciesContext schema))
315+ (OB.DependencyInvalid err)
316+dependencies f =
317+ Validator
318+ (maybe mempty ( (\a -> (mempty, a))
319+ . catMaybes . fmap checkDependency
320+ . HM.elems . _unDependenciesContext
321+ ))
322+ (run (OB.dependencies f . _unDependenciesContext))
323+ where
324+ checkDependency :: OB.Dependency schema -> Maybe schema
325+ checkDependency (OB.PropertyDependency _) = Nothing
326+ checkDependency (OB.SchemaDependency s) = Just s
327+
328+data PropertiesContext schema
329+ = PropertiesContext
330+ (Maybe (HashMap Text schema))
331+ (Maybe (OB.AdditionalProperties schema))
332+ (HashMap Text schema)
333+ deriving (Eq, Show)
334+
335+instance FromJSON schema => FromJSON (PropertiesContext schema) where
336+ parseJSON = withObject "PropertiesContext" $ \o -> PropertiesContext
337+ <$> o .:! "patternProperties"
338+ <*> o .:! "additionalProperties"
339+ <*> o .: "properties"
340+
341+properties
342+ :: (schema -> Value -> [Fail err])
343+ -> Validator
344+ schema
345+ (Maybe (PropertiesContext schema))
346+ (OB.PropertiesInvalid err)
347+properties f =
348+ Validator
349+ (\a -> case a of
350+ Just (PropertiesContext _ _ b) -> (mempty, HM.elems b)
351+ Nothing -> (mempty, mempty))
352+ (run (\(PropertiesContext a b c) -> OB.properties f a b c))
353+
354+-- | The first argument is whether the validator should be run.
355+-- If @"properties"@ exists it will be parsed to 'False'.
356+data PatternPropertiesContext schema
357+ = PatternPropertiesContext
358+ Bool
359+ (Maybe (OB.AdditionalProperties schema))
360+ (HashMap Text schema)
361+ deriving (Eq, Show)
362+
363+instance FromJSON schema => FromJSON (PatternPropertiesContext schema) where
364+ parseJSON = withObject "PatternPropertiesContext" $ \o ->
365+ PatternPropertiesContext
366+ <$> shouldRun o
367+ <*> o .:! "additionalProperties"
368+ <*> o .: "patternProperties"
369+ where
370+ shouldRun :: HashMap Text Value -> Parser Bool
371+ shouldRun o = do
372+ a <- o .:! "properties"
373+ pure $ isNothing (a :: Maybe (HashMap Text schema))
374+
375+patternProperties
376+ :: (schema -> Value -> [Fail err])
377+ -> Validator
378+ schema
379+ (Maybe (PatternPropertiesContext schema))
380+ (OB.PatternPropertiesInvalid err)
381+patternProperties f =
382+ Validator
383+ (\a -> case a of
384+ Just (PatternPropertiesContext _ _ b) -> (mempty, HM.elems b)
385+ Nothing -> (mempty, mempty))
386+ (run (\(PatternPropertiesContext a b c) -> OB.patternProperties f a b c))
387+
388+-- | The first argument is whether the validator should be run.
389+-- If @"properties"@ or @"patternProperties"@ exist it will be parsed
390+-- to 'False'.
391+data AdditionalPropertiesContext schema
392+ = AdditionalPropertiesContext
393+ Bool
394+ (OB.AdditionalProperties schema)
395+ deriving (Eq, Show)
396+
397+instance FromJSON schema => FromJSON (AdditionalPropertiesContext schema) where
398+ parseJSON = withObject "AdditionalPropertiesContext" $ \o ->
399+ AdditionalPropertiesContext
400+ <$> shouldRun o
401+ <*> o .: "additionalProperties"
402+ where
403+ shouldRun :: HashMap Text Value -> Parser Bool
404+ shouldRun o = do
405+ a <- o .:! "properties"
406+ b <- o .:! "patternProperties"
407+ pure $ isNothing (a :: Maybe (HashMap Text schema))
408+ && isNothing (b :: Maybe (HashMap Text schema))
409+
410+additionalProperties
411+ :: (schema -> Value -> [Fail err])
412+ -> Validator
413+ schema
414+ (Maybe (AdditionalPropertiesContext schema))
415+ (OB.AdditionalPropertiesInvalid err)
416+additionalProperties f =
417+ Validator
418+ (\a -> case a of
419+ Nothing -> mempty
420+ Just (AdditionalPropertiesContext _ b) ->
421+ case b of
422+ OB.AdditionalPropertiesBool _ -> (mempty, mempty)
423+ OB.AdditionalPropertiesObject c -> (mempty, pure c))
424+ (run (\(AdditionalPropertiesContext a b) -> OB.additionalProperties f a b))
425+
426+--------------------------------------------------
427+-- * Any
428+--------------------------------------------------
429+
430+newtype Ref
431+ = Ref { _unRef :: Text }
432+ deriving (Eq, Show)
433+
434+instance FromJSON Ref where
435+ parseJSON = withObject "Ref" $ \o ->
436+ Ref <$> o .: "$ref"
437+
438+ref
439+ :: (FromJSON schema, ToJSON schema)
440+ => AN.VisitedSchemas
441+ -> Maybe Text
442+ -> (Maybe Text -> Maybe schema)
443+ -> (AN.VisitedSchemas -> Maybe Text -> schema -> Value -> [Fail err])
444+ -> Validator a (Maybe Ref) (AN.RefInvalid err)
445+ref visited scope getRef f =
446+ Validator
447+ noEmbedded
448+ (run (AN.ref visited scope getRef f . _unRef))
449+
450+newtype EnumContext
451+ = EnumContext { _unEnumContext :: AN.EnumVal }
452+ deriving (Eq, Show)
453+
454+instance FromJSON EnumContext where
455+ parseJSON = withObject "EnumContext" $ \o ->
456+ EnumContext <$> o .: "enum"
457+
458+enumVal :: Validator a (Maybe EnumContext) ()
459+enumVal =
460+ Validator
461+ noEmbedded
462+ (run (fmap maybeToList . AN.enumVal . _unEnumContext))
463+
464+newtype TypeContext
465+ = TypeContext { _unTypeContext :: AN.TypeVal }
466+ deriving (Eq, Show)
467+
468+instance FromJSON TypeContext where
469+ parseJSON = withObject "TypeContext" $ \o ->
470+ TypeContext <$> o .: "type"
471+
472+typeVal :: Validator a (Maybe TypeContext) ()
473+typeVal =
474+ Validator
475+ noEmbedded
476+ (run (fmap maybeToList . AN.typeVal . _unTypeContext))
477+
478+newtype AllOf schema
479+ = AllOf { _unAllOf :: NonEmpty schema }
480+ deriving (Eq, Show)
481+
482+instance FromJSON schema => FromJSON (AllOf schema) where
483+ parseJSON = withObject "AllOf" $ \o ->
484+ AllOf <$> o .: "allOf"
485+
486+allOf
487+ :: (schema -> Value -> [Fail err])
488+ -> Validator schema (Maybe (AllOf schema)) err
489+allOf f =
490+ Validator
491+ (\a -> case a of
492+ Just (AllOf b) -> (NE.toList b, mempty)
493+ Nothing -> (mempty, mempty))
494+ (run (AN.allOf f . _unAllOf))
495+
496+newtype AnyOf schema
497+ = AnyOf { _unAnyOf :: NonEmpty schema }
498+ deriving (Eq, Show)
499+
500+instance FromJSON schema => FromJSON (AnyOf schema) where
501+ parseJSON = withObject "AnyOf" $ \o ->
502+ AnyOf <$> o .: "anyOf"
503+
504+anyOf
505+ :: ToJSON schema
506+ => (schema -> Value -> [Fail err])
507+ -> Validator schema (Maybe (AnyOf schema)) ()
508+anyOf f =
509+ Validator
510+ (\a -> case a of
511+ Just (AnyOf b) -> (NE.toList b, mempty)
512+ Nothing -> (mempty, mempty))
513+ (run (fmap maybeToList . AN.anyOf f . _unAnyOf))
514+
515+newtype OneOf schema
516+ = OneOf { _unOneOf :: NonEmpty schema }
517+ deriving (Eq, Show)
518+
519+instance FromJSON schema => FromJSON (OneOf schema) where
520+ parseJSON = withObject "OneOf" $ \o ->
521+ OneOf <$> o .: "oneOf"
522+
523+oneOf
524+ :: ToJSON schema
525+ => (schema -> Value -> [Fail err])
526+ -> Validator schema (Maybe (OneOf schema)) ()
527+oneOf f =
528+ Validator
529+ (\a -> case a of
530+ Just (OneOf b) -> (NE.toList b, mempty)
531+ Nothing -> (mempty, mempty))
532+ (run (fmap maybeToList . AN.oneOf f . _unOneOf))
533+
534+newtype NotVal schema
535+ = NotVal { _unNotVal :: schema }
536+ deriving (Eq, Show)
537+
538+instance FromJSON schema => FromJSON (NotVal schema) where
539+ parseJSON = withObject "NotVal" $ \o ->
540+ NotVal <$> o .: "not"
541+
542+notVal
543+ :: ToJSON schema
544+ => (schema -> Value -> [Fail err])
545+ -> Validator schema (Maybe (NotVal schema)) ()
546+notVal f =
547+ Validator
548+ (\a -> case a of
549+ Just (NotVal b) -> (pure b, mempty)
550+ Nothing -> (mempty, mempty))
551+ (run (fmap maybeToList . AN.notVal f . _unNotVal))
src/Data/Validator/Draft4/Any.hsnew file+219
@@ -0,0 +1,219 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+
3+module Data.Validator.Draft4.Any where
4+
5+import Import
6+-- Hiding is for GHCs before 7.10:
7+import Prelude hiding (any, elem)
8+
9+import Control.Monad
10+import Data.Aeson.Types (Parser)
11+import Data.List.NonEmpty (NonEmpty)
12+import qualified Data.List.NonEmpty as NE
13+import Data.Maybe
14+import qualified Data.Scientific as SCI
15+import Data.Set (Set)
16+import qualified Data.Set as S
17+
18+import Data.Validator.Failure (Fail(..))
19+import qualified Data.Validator.Utils as UT
20+import Data.Validator.Reference (URIBaseAndFragment,
21+ resolveFragment, resolveReference)
22+
23+--------------------------------------------------
24+-- * $ref
25+--------------------------------------------------
26+
27+data RefInvalid err
28+ = RefResolution
29+ | RefLoop
30+ | RefInvalid err
31+ deriving (Eq, Show)
32+
33+newtype VisitedSchemas
34+ = VisitedSchemas { _unVisited :: [URIBaseAndFragment] }
35+ deriving (Eq, Show, Monoid)
36+
37+ref
38+ :: forall err schema. (FromJSON schema, ToJSON schema)
39+ => VisitedSchemas
40+ -> Maybe Text
41+ -> (Maybe Text -> Maybe schema)
42+ -> (VisitedSchemas -> Maybe Text -> schema -> Value -> [Fail err])
43+ -> Text
44+ -> Value
45+ -> [Fail (RefInvalid err)]
46+ref visited scope getRef f reference x
47+ | (mUri, mFragment) `elem` _unVisited visited =
48+ pure $ Failure RefLoop (toJSON reference) mempty x
49+ | otherwise =
50+ case getRef mUri of
51+ Nothing -> pure failureDNE
52+ Just schema ->
53+ case resolveFragment mFragment schema of
54+ Nothing -> pure failureDNE
55+ Just s ->
56+ let newVisited = (VisitedSchemas [(mUri, mFragment)]
57+ <> visited)
58+ in fmap RefInvalid <$> f newVisited mUri s x
59+ where
60+ (mUri, mFragment) = resolveReference scope reference
61+
62+ failureDNE :: Fail (RefInvalid err)
63+ failureDNE = Failure RefResolution (toJSON reference) mempty x
64+
65+--------------------------------------------------
66+-- * enum
67+--------------------------------------------------
68+
69+-- | From the spec:
70+-- <http://json-schema.org/latest/json-schema-validation.html#anchor76>
71+--
72+-- > The value of this keyword MUST be an array.
73+-- > This array MUST have at least one element.
74+-- > Elements in the array MUST be unique.
75+--
76+-- NOTE: We don't enforce the uniqueness constraint in the haskell code,
77+-- but we do in the 'FromJSON' instance.
78+newtype EnumVal
79+ = EnumVal { _unEnumVal :: NonEmpty Value }
80+ -- Given a choice, we'd prefer to enforce uniqueness through the type
81+ -- system over having at least one element. To use a 'Set' though we'd
82+ -- have to use 'OrdValue' here (there's no 'Ord' instance for plain Values)
83+ -- and we'd rather not make users mess with 'OrdValue'.
84+ deriving (Eq, Show)
85+
86+instance FromJSON EnumVal where
87+ parseJSON v = checkUnique . UT._unNonEmpty' =<< parseJSON v
88+ where
89+ checkUnique :: NonEmpty Value -> Parser EnumVal
90+ checkUnique a
91+ | UT.allUniqueValues' a = pure (EnumVal a)
92+ | otherwise = fail "All elements of the Enum validator must be unique."
93+
94+instance ToJSON EnumVal where
95+ toJSON = toJSON . UT.NonEmpty' . _unEnumVal
96+
97+instance Arbitrary EnumVal where
98+ arbitrary = do
99+ xs <- traverse (const UT.arbitraryValue) =<< (arbitrary :: Gen [()])
100+ case NE.nonEmpty (toUnique xs) of
101+ Nothing -> EnumVal . pure <$> UT.arbitraryValue
102+ Just ne -> pure (EnumVal ne)
103+ where
104+ toUnique :: [Value] -> [Value]
105+ toUnique = fmap UT._unOrdValue . S.toList . S.fromList . fmap UT.OrdValue
106+
107+enumVal :: EnumVal -> Value -> Maybe (Fail ())
108+enumVal (EnumVal vs) x
109+ | not (UT.allUniqueValues' vs) = Nothing
110+ | x `elem` vs = Nothing
111+ | otherwise = Just $ Failure () (toJSON (UT.NonEmpty' vs))
112+ mempty x
113+
114+--------------------------------------------------
115+-- * type
116+--------------------------------------------------
117+
118+data TypeVal
119+ = TypeValString Text
120+ | TypeValArray (Set Text)
121+ deriving (Eq, Show)
122+
123+instance FromJSON TypeVal where
124+ parseJSON v = fmap TypeValString (parseJSON v)
125+ <|> fmap TypeValArray (parseJSON v)
126+
127+instance ToJSON TypeVal where
128+ toJSON (TypeValString t) = toJSON t
129+ toJSON (TypeValArray ts) = toJSON ts
130+
131+instance Arbitrary TypeVal where
132+ arbitrary = oneof [ TypeValString <$> UT.arbitraryText
133+ , TypeValArray <$> UT.arbitrarySetOfText
134+ ]
135+
136+setFromTypeVal :: TypeVal -> Set Text
137+setFromTypeVal (TypeValString t) = S.singleton t
138+setFromTypeVal (TypeValArray ts) = ts
139+
140+typeVal :: TypeVal -> Value -> Maybe (Fail ())
141+typeVal tv x
142+ | S.null matches = Just (Failure () (toJSON tv) mempty x)
143+ | otherwise = Nothing
144+ where
145+ -- There can be more than one match because a 'Value' can be both a
146+ -- @"number"@ and an @"integer"@.
147+ matches :: Set Text
148+ matches = S.intersection okTypes (setFromTypeVal tv)
149+
150+ okTypes :: Set Text
151+ okTypes =
152+ case x of
153+ Null -> S.singleton "null"
154+ (Array _) -> S.singleton "array"
155+ (Bool _) -> S.singleton "boolean"
156+ (Object _) -> S.singleton "object"
157+ (String _) -> S.singleton "string"
158+ (Number y) ->
159+ if SCI.isInteger y
160+ then S.fromList ["number", "integer"]
161+ else S.singleton "number"
162+
163+--------------------------------------------------
164+-- * allOf
165+--------------------------------------------------
166+
167+allOf
168+ :: (schema -> Value -> [Fail err])
169+ -> NonEmpty schema
170+ -> Value
171+ -> [Fail err]
172+allOf f subSchemas x = NE.toList subSchemas >>= flip f x
173+
174+--------------------------------------------------
175+-- * anyOf
176+--------------------------------------------------
177+
178+anyOf
179+ :: ToJSON schema
180+ => (schema -> Value -> [Fail err])
181+ -> NonEmpty schema
182+ -> Value
183+ -> Maybe (Fail ())
184+anyOf f subSchemas x
185+ | any null (flip f x <$> subSchemas) = Nothing
186+ | otherwise = Just $ Failure () (toJSON (UT.NonEmpty' subSchemas))
187+ mempty x
188+
189+--------------------------------------------------
190+-- * oneOf
191+--------------------------------------------------
192+
193+oneOf
194+ :: forall err schema. ToJSON schema
195+ => (schema -> Value -> [Fail err])
196+ -> NonEmpty schema
197+ -> Value
198+ -> Maybe (Fail ())
199+oneOf f subSchemas x
200+ | length successes == 1 = Nothing
201+ | otherwise = Just $ Failure () (toJSON (UT.NonEmpty' subSchemas)) mempty x
202+ where
203+ successes :: [[Fail err]]
204+ successes = filter null $ flip f x <$> NE.toList subSchemas
205+
206+--------------------------------------------------
207+-- * not
208+--------------------------------------------------
209+
210+notVal
211+ :: ToJSON schema
212+ => (schema -> Value -> [Fail err])
213+ -> schema
214+ -> Value
215+ -> Maybe (Fail ())
216+notVal f schema x =
217+ case f schema x of
218+ [] -> Just (Failure () (toJSON schema) mempty x)
219+ _ -> Nothing
src/Data/Validator/Draft4/Array.hsnew file+184
@@ -0,0 +1,184 @@
1+
2+module Data.Validator.Draft4.Array where
3+
4+import Import
5+import Prelude
6+
7+import Control.Monad
8+import qualified Data.Aeson.Pointer as AP
9+import qualified Data.Text as T
10+import qualified Data.Vector as V
11+import Text.Read (readMaybe)
12+
13+import Data.Validator.Failure (Fail(..), prependToPath)
14+import Data.Validator.Utils (allUniqueValues)
15+
16+--------------------------------------------------
17+-- * maxItems
18+--------------------------------------------------
19+
20+-- | The spec requires @"maxItems"@ to be non-negative.
21+maxItems :: Int -> Vector Value -> Maybe (Fail ())
22+maxItems n xs
23+ | n < 0 = Nothing
24+ | V.length xs > n = Just (Failure () (toJSON n) mempty (Array xs))
25+ | otherwise = Nothing
26+
27+--------------------------------------------------
28+-- * minItems
29+--------------------------------------------------
30+
31+-- | The spec requires @"minItems"@ to be non-negative.
32+minItems :: Int -> Vector Value -> Maybe (Fail ())
33+minItems n xs
34+ | n < 0 = Nothing
35+ | V.length xs < n = Just (Failure () (toJSON n) mempty (Array xs))
36+ | otherwise = Nothing
37+
38+--------------------------------------------------
39+-- * uniqueItems
40+--------------------------------------------------
41+
42+uniqueItems :: Bool -> Vector Value -> Maybe (Fail ())
43+uniqueItems True xs
44+ | allUniqueValues xs = Nothing
45+ | otherwise = Just (Failure () (Bool True) mempty (Array xs))
46+uniqueItems False _ = Nothing
47+
48+--------------------------------------------------
49+-- * items
50+--------------------------------------------------
51+
52+data Items schema
53+ = ItemsObject schema
54+ | ItemsArray [schema]
55+ deriving (Eq, Show)
56+
57+instance FromJSON schema => FromJSON (Items schema) where
58+ parseJSON v = fmap ItemsObject (parseJSON v)
59+ <|> fmap ItemsArray (parseJSON v)
60+
61+instance ToJSON schema => ToJSON (Items schema) where
62+ toJSON (ItemsObject hm) = toJSON hm
63+ toJSON (ItemsArray schemas) = toJSON schemas
64+
65+instance Arbitrary schema => Arbitrary (Items schema) where
66+ arbitrary = oneof [ ItemsObject <$> arbitrary
67+ , ItemsArray <$> arbitrary
68+ ]
69+
70+data ItemsInvalid err
71+ = Items err
72+ | AdditionalItemsBoolInvalid
73+ | AdditionalItemsObjectInvalid err
74+ deriving (Eq, Show)
75+
76+items
77+ :: forall err schema.
78+ (schema -> Value -> [Fail err])
79+ -> Maybe (AdditionalItems schema)
80+ -> Items schema
81+ -> Vector Value
82+ -> [Fail (ItemsInvalid err)]
83+items f _ (ItemsObject subSchema) xs =
84+ zip [0..] (V.toList xs) >>= g
85+ where
86+ g :: (Int, Value) -> [Fail (ItemsInvalid err)]
87+ g (index,x) = fmap Items
88+ . prependToPath (AP.Token (T.pack (show index)))
89+ <$> f subSchema x
90+
91+items f mAdditional (ItemsArray subSchemas) xs =
92+ itemFailures <> additionalItemFailures
93+ where
94+ indexedValues :: [(Int, Value)]
95+ indexedValues = zip [0..] (V.toList xs)
96+
97+ itemFailures :: [Fail (ItemsInvalid err)]
98+ itemFailures = join (zipWith g subSchemas indexedValues)
99+ where
100+ g :: schema -> (Int, Value) -> [Fail (ItemsInvalid err)]
101+ g schema (index,x) = fmap Items
102+ . prependToPath (AP.Token (T.pack (show index)))
103+ <$> f schema x
104+
105+ additionalItemFailures :: [Fail (ItemsInvalid err)]
106+ additionalItemFailures =
107+ case mAdditional of
108+ Nothing -> mempty
109+ Just adi -> fmap correctName
110+ . correctIndexes
111+ <$> additionalItems f adi extras
112+ where
113+ -- It's not great that we convert back to Vector again.
114+ extras :: Vector Value
115+ extras = V.fromList . fmap snd
116+ . drop (length subSchemas) $ indexedValues
117+
118+ -- Since 'additionalItems' only sees part of the array, but starts
119+ -- indexing from zero, we need to modify the paths it reports to
120+ -- represent invalid data so they actually represent the correct
121+ -- offsets.
122+ correctIndexes
123+ :: Fail (AdditionalItemsInvalid err)
124+ -> Fail (AdditionalItemsInvalid err)
125+ correctIndexes (Failure a b c d) = Failure a b (fixIndex c) d
126+ where
127+ fixIndex :: AP.Pointer -> AP.Pointer
128+ fixIndex (AP.Pointer (tok:toks)) =
129+ case readMaybe . T.unpack . AP._unToken $ tok of
130+ Nothing -> AP.Pointer $ tok:toks
131+ Just n -> AP.Pointer $
132+ (AP.Token . T.pack . show $ n + length subSchemas):toks
133+ fixIndex (AP.Pointer []) = AP.Pointer []
134+
135+ correctName :: AdditionalItemsInvalid err -> ItemsInvalid err
136+ correctName AdditionalBoolInvalid = AdditionalItemsBoolInvalid
137+ correctName (AdditionalObjectInvalid err) =
138+ AdditionalItemsObjectInvalid err
139+
140+--------------------------------------------------
141+-- * additionalItems
142+--------------------------------------------------
143+
144+data AdditionalItems schema
145+ = AdditionalBool Bool
146+ | AdditionalObject schema
147+ deriving (Eq, Show)
148+
149+instance FromJSON schema => FromJSON (AdditionalItems schema) where
150+ parseJSON v = fmap AdditionalBool (parseJSON v)
151+ <|> fmap AdditionalObject (parseJSON v)
152+
153+instance ToJSON schema => ToJSON (AdditionalItems schema) where
154+ toJSON (AdditionalBool b) = toJSON b
155+ toJSON (AdditionalObject hm) = toJSON hm
156+
157+instance Arbitrary schema => Arbitrary (AdditionalItems schema) where
158+ arbitrary = oneof [ AdditionalBool <$> arbitrary
159+ , AdditionalObject <$> arbitrary
160+ ]
161+
162+data AdditionalItemsInvalid err
163+ = AdditionalBoolInvalid
164+ | AdditionalObjectInvalid err
165+ deriving (Eq, Show)
166+
167+additionalItems
168+ :: forall err schema.
169+ (schema -> Value -> [Fail err])
170+ -> AdditionalItems schema
171+ -> Vector Value
172+ -> [Fail (AdditionalItemsInvalid err)]
173+additionalItems _ (AdditionalBool b) xs
174+ | b = mempty
175+ | V.length xs > 0 = pure (Failure AdditionalBoolInvalid (Bool b)
176+ mempty (toJSON xs))
177+ | otherwise = mempty
178+additionalItems f (AdditionalObject subSchema) xs =
179+ zip [0..] (V.toList xs) >>= g
180+ where
181+ g :: (Int, Value) -> [Fail (AdditionalItemsInvalid err)]
182+ g (index,x) = fmap AdditionalObjectInvalid
183+ . prependToPath (AP.Token (T.pack (show index)))
184+ <$> f subSchema x
src/Data/Validator/Draft4/Number.hsnew file+65
@@ -0,0 +1,65 @@
1+
2+module Data.Validator.Draft4.Number where
3+
4+import Import
5+import Prelude
6+
7+import Data.Fixed (mod')
8+import Data.Scientific (Scientific)
9+
10+import Data.Validator.Failure (Fail(..))
11+
12+--------------------------------------------------
13+-- * multipleOf
14+--------------------------------------------------
15+
16+-- | The spec requires @"multipleOf"@ to be positive.
17+multipleOf :: Scientific -> Scientific -> Maybe (Fail ())
18+multipleOf n x
19+ | n <= 0 = Nothing
20+ | x `mod'` n /= 0 = Just (Failure () (toJSON n) mempty (Number x))
21+ | otherwise = Nothing
22+
23+--------------------------------------------------
24+-- * maximum
25+--------------------------------------------------
26+
27+data MaximumInvalid
28+ = Maximum
29+ | ExclusiveMaximum
30+ deriving (Eq, Show)
31+
32+maximumVal
33+ :: Bool
34+ -> Scientific
35+ -> Scientific
36+ -> Maybe (Fail MaximumInvalid)
37+maximumVal exclusive n x
38+ | x `greaterThan` n = Just (Failure err (toJSON n) mempty (Number x))
39+ | otherwise = Nothing
40+ where
41+ (greaterThan, err) = if exclusive
42+ then ((>=), ExclusiveMaximum)
43+ else ((>), Maximum)
44+
45+--------------------------------------------------
46+-- * minimum
47+--------------------------------------------------
48+
49+data MinimumInvalid
50+ = Minimum
51+ | ExclusiveMinimum
52+ deriving (Eq, Show)
53+
54+minimumVal
55+ :: Bool
56+ -> Scientific
57+ -> Scientific
58+ -> Maybe (Fail MinimumInvalid)
59+minimumVal exclusive n x
60+ | x `lessThan` n = Just (Failure err (toJSON n) mempty (Number x))
61+ | otherwise = Nothing
62+ where
63+ (lessThan, err) = if exclusive
64+ then ((<=), ExclusiveMinimum)
65+ else ((<), Minimum)
src/Data/Validator/Draft4/Object.hsnew file+157
@@ -0,0 +1,157 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+
3+module Data.Validator.Draft4.Object
4+ ( module Data.Validator.Draft4.Object
5+ , module Data.Validator.Draft4.Object.Properties
6+ ) where
7+
8+import Import
9+-- Hiding is for GHCs before 7.10:
10+import Prelude hiding (all, concat,
11+ foldl)
12+
13+import Data.Aeson.Types (Parser)
14+import qualified Data.HashMap.Strict as H
15+import Data.Set (Set)
16+import qualified Data.Set as S
17+import qualified Data.Text as T
18+
19+import Data.Validator.Draft4.Object.Properties
20+import Data.Validator.Failure (Fail(..))
21+import Data.Validator.Utils
22+
23+--------------------------------------------------
24+-- * maxProperties
25+--------------------------------------------------
26+
27+-- | The spec requires @"maxProperties"@ to be non-negative.
28+maxProperties :: Int -> HashMap Text Value -> Maybe (Fail ())
29+maxProperties n x
30+ | n < 0 = Nothing
31+ | H.size x > n = Just (Failure () (toJSON n) mempty (Object x))
32+ | otherwise = Nothing
33+
34+--------------------------------------------------
35+-- * minProperties
36+--------------------------------------------------
37+
38+-- | The spec requires @"minProperties"@ to be non-negative.
39+minProperties :: Int -> HashMap Text Value -> Maybe (Fail ())
40+minProperties n x
41+ | n < 0 = Nothing
42+ | H.size x < n = Just (Failure () (toJSON n) mempty (Object x))
43+ | otherwise = Nothing
44+
45+--------------------------------------------------
46+-- * required
47+--------------------------------------------------
48+
49+-- | From the spec:
50+--
51+-- > The value of this keyword MUST be an array.
52+-- > This array MUST have at least one element.
53+-- > Elements of this array MUST be strings, and MUST be unique.
54+--
55+-- We don't enfore that 'Required' has at least one element in the
56+-- haskell code, but we do in the 'FromJSON' instance.
57+newtype Required
58+ = Required { _unRequired :: Set Text }
59+ deriving (Eq, Show, ToJSON)
60+
61+instance FromJSON Required where
62+ parseJSON v = checkUnique =<< checkSize =<< parseJSON v
63+ where
64+ checkSize :: [Text] -> Parser [Text]
65+ checkSize a
66+ | null a = fail "Required validator must not be empty."
67+ | otherwise = pure a
68+
69+ checkUnique :: [Text] -> Parser Required
70+ checkUnique a =
71+ let b = S.fromList a
72+ -- NOTE: Can use length instead of S.size in GHC 7.10 or later.
73+ in if length a == S.size b
74+ then pure (Required b)
75+ else fail "All elements of the Required validator must be unique."
76+
77+instance Arbitrary Required where
78+ arbitrary = do
79+ x <- arbitraryText -- Guarantee at least one element.
80+ xs <- (fmap.fmap) T.pack arbitrary
81+ pure . Required . S.fromList $ x:xs
82+
83+required :: Required -> HashMap Text Value -> Maybe (Fail ())
84+required (Required ts) x
85+ -- NOTE: When we no longer need to support GHCs before 7.10
86+ -- we can use null from Prelude throughout the library
87+ -- instead of specialized versions.
88+ | S.null ts = Nothing
89+ | H.null (H.difference hm x) = Nothing
90+ | otherwise = Just (Failure () (toJSON ts)
91+ mempty (Object x))
92+ where
93+ hm :: HashMap Text Bool
94+ hm = foldl (\b a -> H.insert a True b) mempty ts
95+
96+--------------------------------------------------
97+-- * dependencies
98+--------------------------------------------------
99+
100+data Dependency schema
101+ = SchemaDependency schema
102+ | PropertyDependency (Set Text)
103+ deriving (Eq, Show)
104+
105+instance FromJSON schema => FromJSON (Dependency schema) where
106+ parseJSON v = fmap SchemaDependency (parseJSON v)
107+ <|> fmap PropertyDependency (parseJSON v)
108+
109+instance ToJSON schema => ToJSON (Dependency schema) where
110+ toJSON (SchemaDependency schema) = toJSON schema
111+ toJSON (PropertyDependency ts) = toJSON ts
112+
113+instance Arbitrary schema => Arbitrary (Dependency schema) where
114+ arbitrary = oneof [ SchemaDependency <$> arbitrary
115+ , PropertyDependency <$> arbitrarySetOfText
116+ ]
117+
118+data DependencyInvalid err
119+ = SchemaDependencyInvalid err
120+ | PropertyDependencyInvalid
121+ deriving (Eq, Show)
122+
123+-- | From the spec:
124+-- <http://json-schema.org/latest/json-schema-validation.html#anchor70>
125+--
126+-- > This keyword's value MUST be an object.
127+-- > Each value of this object MUST be either an object or an array.
128+-- >
129+-- > If the value is an object, it MUST be a valid JSON Schema.
130+-- > This is called a schema dependency.
131+-- >
132+-- > If the value is an array, it MUST have at least one element.
133+-- > Each element MUST be a string, and elements in the array MUST be unique.
134+-- > This is called a property dependency.
135+dependencies
136+ :: forall err schema.
137+ (schema -> Value -> [Fail err])
138+ -> HashMap Text (Dependency schema)
139+ -> HashMap Text Value
140+ -> [Fail (DependencyInvalid err)]
141+dependencies f hm x = concat . fmap (uncurry g) . H.toList $ hm
142+ where
143+ g :: Text -> Dependency schema -> [Fail (DependencyInvalid err)]
144+ g k (SchemaDependency schema)
145+ | H.member k x =
146+ fmap SchemaDependencyInvalid <$> f schema (Object x)
147+ | otherwise = mempty
148+ g k (PropertyDependency ts)
149+ | H.member k x && not allPresent =
150+ pure $ Failure PropertyDependencyInvalid
151+ (toJSON (H.singleton k ts))
152+ mempty
153+ (Object x)
154+ | otherwise = mempty
155+ where
156+ allPresent :: Bool
157+ allPresent = all (`H.member` x) ts
src/Data/Validator/Draft4/Object/Properties.hsnew file+214
@@ -0,0 +1,214 @@
1+
2+module Data.Validator.Draft4.Object.Properties where
3+
4+import Import
5+import Prelude
6+
7+import Control.Monad
8+import qualified Data.Aeson.Pointer as AP
9+import Data.Functor (($>))
10+import qualified Data.HashMap.Strict as HM
11+import Data.Text.Encoding (encodeUtf8)
12+import qualified Text.Regex.PCRE.Heavy as RE
13+
14+import Data.Validator.Failure (Fail(..), prependToPath)
15+
16+-- | For internal use.
17+newtype Remaining
18+ = Remaining { _unRemaining :: HashMap Text Value }
19+
20+--------------------------------------------------
21+-- * properties
22+--------------------------------------------------
23+
24+data PropertiesInvalid err
25+ = PropertiesInvalid err
26+ | PropPatternInvalid err
27+ | PropAdditionalInvalid (AdditionalPropertiesInvalid err)
28+ deriving (Eq, Show)
29+
30+-- | In order of what's tried: @"properties"@, @"patternProperties"@,
31+-- @"additionalProperties"@.
32+properties
33+ :: forall err schema.
34+ (schema -> Value -> [Fail err])
35+ -> Maybe (HashMap Text schema)
36+ -> Maybe (AdditionalProperties schema)
37+ -> HashMap Text schema
38+ -> HashMap Text Value
39+ -> [Fail (PropertiesInvalid err)]
40+properties f mPat mAdd propertiesHm x =
41+ fmap (fmap PropertiesInvalid) propFailures
42+ <> fmap (fmap PropPatternInvalid) patternFailures
43+ <> fmap (fmap PropAdditionalInvalid) additionalFailures
44+ where
45+ propertiesAndUnmatched :: ([Fail err], Remaining)
46+ propertiesAndUnmatched = ( failures
47+ , Remaining (HM.difference x propertiesHm)
48+ )
49+ where
50+ failures :: [Fail err]
51+ failures = HM.toList (HM.intersectionWith f propertiesHm x)
52+ >>= (\(k,vs) -> fmap (prependToPath (AP.Token k)) vs)
53+
54+ (propFailures, remaining1) = propertiesAndUnmatched
55+
56+ mPatProp :: Maybe (HashMap Text Value -> ([Fail err], Remaining))
57+ mPatProp = patternAndUnmatched f <$> mPat
58+
59+ patternFailures :: [Fail err]
60+ patternFailures = case mPatProp of
61+ Nothing -> mempty
62+ Just val -> fst (val x)
63+
64+ remaining2 :: Remaining
65+ remaining2 = case mPatProp of
66+ Nothing -> remaining1
67+ Just val -> snd . val . _unRemaining $ remaining1
68+
69+ additionalFailures :: [Fail (AdditionalPropertiesInvalid err)]
70+ additionalFailures =
71+ case mAdd of
72+ Nothing -> mempty
73+ Just a -> additionalProperties f True a (_unRemaining remaining2)
74+
75+--------------------------------------------------
76+-- * patternProperties
77+--------------------------------------------------
78+
79+data PatternPropertiesInvalid err
80+ = PPInvalid err
81+ | PPAdditionalPropertiesInvalid (AdditionalPropertiesInvalid err)
82+ deriving (Eq, Show)
83+
84+patternProperties
85+ :: forall err schema.
86+ (schema -> Value -> [Fail err])
87+ -> Bool
88+ -> Maybe (AdditionalProperties schema)
89+ -> HashMap Text schema
90+ -> HashMap Text Value
91+ -> [Fail (PatternPropertiesInvalid err)]
92+patternProperties _ False _ _ _ = mempty
93+patternProperties f _ mAdd patternPropertiesHm x =
94+ (fmap PPInvalid <$> ppFailures)
95+ <> (fmap PPAdditionalPropertiesInvalid <$> addFailures)
96+ where
97+ patternProps :: ([Fail err], Remaining)
98+ patternProps = patternAndUnmatched f patternPropertiesHm x
99+
100+ (ppFailures, remaining) = patternProps
101+
102+ addFailures :: [Fail (AdditionalPropertiesInvalid err)]
103+ addFailures =
104+ case mAdd of
105+ Nothing -> mempty
106+ Just a -> additionalProperties f True a (_unRemaining remaining)
107+
108+patternAndUnmatched
109+ :: forall err schema.
110+ (schema -> Value -> [Fail err])
111+ -> HashMap Text schema
112+ -> HashMap Text Value
113+ -> ([Fail err], Remaining)
114+patternAndUnmatched f patPropertiesHm x =
115+ (HM.foldlWithKey' runVals mempty perhapsMatches, remaining)
116+ where
117+ perhapsMatches :: HashMap Text (Value, [schema])
118+ perhapsMatches = HM.foldlWithKey' (matchingSchemas patPropertiesHm) mempty x
119+ where
120+ matchingSchemas
121+ :: HashMap Text schema
122+ -> HashMap Text (Value, [schema])
123+ -> Text
124+ -> Value
125+ -> HashMap Text (Value, [schema])
126+ matchingSchemas subSchemas acc k v =
127+ HM.insert k (v, HM.foldlWithKey' (checkKey k) mempty subSchemas) acc
128+
129+ checkKey
130+ :: Text
131+ -> [schema]
132+ -> Text
133+ -> schema
134+ -> [schema]
135+ checkKey k acc r subSchema =
136+ case RE.compileM (encodeUtf8 r) mempty of
137+ Left _ -> acc
138+ Right re -> if k RE.=~ re
139+ then pure subSchema <> acc
140+ else acc
141+
142+ runVals
143+ :: [Fail err]
144+ -> Text
145+ -> (Value, [schema])
146+ -> [Fail err]
147+ runVals acc k (v,subSchemas) =
148+ (subSchemas >>= (\schema -> prependToPath (AP.Token k) <$> f schema v))
149+ <> acc
150+
151+ remaining :: Remaining
152+ remaining = Remaining . fmap fst . HM.filter (null . snd) $ perhapsMatches
153+
154+--------------------------------------------------
155+-- * additionalProperties
156+--------------------------------------------------
157+
158+data AdditionalProperties schema
159+ = AdditionalPropertiesBool Bool
160+ | AdditionalPropertiesObject schema
161+ deriving (Eq, Show)
162+
163+instance FromJSON schema => FromJSON (AdditionalProperties schema) where
164+ parseJSON v = fmap AdditionalPropertiesBool (parseJSON v)
165+ <|> fmap AdditionalPropertiesObject (parseJSON v)
166+
167+instance ToJSON schema => ToJSON (AdditionalProperties schema) where
168+ toJSON (AdditionalPropertiesBool b) = toJSON b
169+ toJSON (AdditionalPropertiesObject hm) = toJSON hm
170+
171+instance Arbitrary schema => Arbitrary (AdditionalProperties schema) where
172+ arbitrary = oneof [ AdditionalPropertiesBool <$> arbitrary
173+ , AdditionalPropertiesObject <$> arbitrary
174+ ]
175+
176+data AdditionalPropertiesInvalid err
177+ = APBoolInvalid
178+ | APObjectInvalid err
179+ deriving (Eq, Show)
180+
181+additionalProperties
182+ :: forall err schema.
183+ (schema -> Value -> [Fail err])
184+ -> Bool
185+ -> AdditionalProperties schema
186+ -> HashMap Text Value
187+ -> [Fail (AdditionalPropertiesInvalid err)]
188+additionalProperties _ False _ _ = mempty
189+additionalProperties f _ a x =
190+ case a of
191+ AdditionalPropertiesBool b ->
192+ ($> APBoolInvalid) <$> additionalPropertiesBool b x
193+ AdditionalPropertiesObject b ->
194+ fmap APObjectInvalid <$> additionalPropertiesObject f b x
195+
196+additionalPropertiesBool
197+ :: Bool
198+ -> HashMap Text Value
199+ -> [Fail ()]
200+additionalPropertiesBool False x
201+ | HM.size x > 0 = pure $ Failure () (Bool False) mempty (Object x)
202+ | otherwise = mempty
203+additionalPropertiesBool True _ = mempty
204+
205+additionalPropertiesObject
206+ :: forall err schema.
207+ (schema -> Value -> [Fail err])
208+ -> schema
209+ -> HashMap Text Value
210+ -> [Fail err]
211+additionalPropertiesObject f schema x = HM.toList x >>= g
212+ where
213+ g :: (Text, Value) -> [Fail err]
214+ g (k,v) = prependToPath (AP.Token k) <$> f schema v
src/Data/Validator/Draft4/String.hsnew file+45
@@ -0,0 +1,45 @@
1+
2+module Data.Validator.Draft4.String where
3+
4+import Import
5+import Prelude
6+
7+import qualified Data.Text as T
8+import Data.Text.Encoding (encodeUtf8)
9+import qualified Text.Regex.PCRE.Heavy as RE
10+
11+import Data.Validator.Failure (Fail(..))
12+
13+--------------------------------------------------
14+-- * maxLength
15+--------------------------------------------------
16+
17+-- | The spec requires @"maxLength"@ to be non-negative.
18+maxLength :: Int -> Text -> Maybe (Fail ())
19+maxLength n x
20+ | n <= 0 = Nothing
21+ | T.length x > n = Just (Failure () (toJSON n) mempty (String x))
22+ | otherwise = Nothing
23+
24+--------------------------------------------------
25+-- * minLength
26+--------------------------------------------------
27+
28+-- | The spec requires @"minLength"@ to be non-negative.
29+minLength :: Int -> Text -> Maybe (Fail ())
30+minLength n x
31+ | n <= 0 = Nothing
32+ | T.length x < n = Just (Failure () (toJSON n) mempty (String x))
33+ | otherwise = Nothing
34+
35+--------------------------------------------------
36+-- * pattern
37+--------------------------------------------------
38+
39+patternVal :: Text -> Text -> Maybe (Fail ())
40+patternVal t x =
41+ case RE.compileM (encodeUtf8 t) mempty of
42+ Left _ -> Just (Failure () (toJSON t) mempty (String x))
43+ Right re -> if x RE.=~ re
44+ then Nothing
45+ else Just (Failure () (toJSON t) mempty (String x))
src/Data/Validator/Failure.hsnew file+49
@@ -0,0 +1,49 @@
1+{-# LANGUAGE DeriveFunctor #-}
2+
3+module Data.Validator.Failure where
4+
5+import Import
6+-- Hiding is for GHCs before 7.10:
7+import Prelude hiding (concat, sequence)
8+
9+import qualified Data.Aeson.Pointer as AP
10+
11+-- | Validators shouldn't know more about the schema they're going to
12+-- be used with than necessary. If a validator throws errors using the
13+-- error sum type of a particular schema, then it can't be used with
14+-- other schemas later that have different error sum types (at least not
15+-- without writing partial functions).
16+--
17+-- Because of this we make 'Fail' a higher order type, so each validator
18+-- can return a sum type describing only the failures that can occur in that
19+-- validator (or '()' if that validator can only fail in one way).
20+--
21+-- It's the job of a schema's validate function to unify the errors produced
22+-- by the validators it uses into a single error sum type for that schema.
23+-- The schema's validate function will return a 'Fail' with
24+-- that sum type as its type argument.
25+--
26+-- The slightly weird naming ('Fail' and 'Failure') is so that we can define
27+-- a 'type Failure = Fail SchemaErrorType' for each of our schemas, and
28+-- export it along with 'Fail(..)'. This way the users of the library only
29+-- use 'Failure', not 'Fail'.
30+data Fail err = Failure
31+ { _failureValidatorsCalled :: !err
32+ -- ^ E.g. @Items UniqueItems@ during draft 4 validation.
33+ , _failureFinalValidator :: !Value
34+ -- ^ The value of the validator that raised the error (e.g. the value of
35+ -- @"uniqueItems"@ in the above example.
36+ , _failureOffendingPointer :: !AP.Pointer
37+ -- ^ A pointer to the part of the data that caused invalidation.
38+ , _failureOffendingData :: !Value
39+ -- ^ The part of the data that caused invalidation. Usually this is
40+ -- identical to the result of resolving '_invalidOffendingPointer'
41+ -- against the starting data, but not always (e.g. in the case of
42+ -- 'additionalItems' where '_invalidOffendingData' will be the items
43+ -- in the array that were not allowed, instead of the entire array).
44+ } deriving (Eq, Show, Functor)
45+
46+prependToPath :: AP.Token -> Fail a -> Fail a
47+prependToPath tok failure =
48+ let old = _failureOffendingPointer failure
49+ in failure { _failureOffendingPointer = AP.Pointer [tok] <> old }
src/Data/Validator/Reference.hsnew file+79
@@ -0,0 +1,79 @@
1+-- | JSON Reference is described here:
2+-- <http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03>
3+--
4+-- And is extended for JSON Schema here:
5+-- <http://json-schema.org/latest/json-schema-core.html#anchor26>
6+
7+module Data.Validator.Reference where
8+
9+import Import
10+import Prelude
11+
12+import qualified Data.Aeson.Pointer as AP
13+import qualified Data.Text as T
14+import Data.Text.Encoding (decodeUtf8, encodeUtf8)
15+import Network.HTTP.Types.URI (urlDecode)
16+import System.FilePath ((</>), dropFileName)
17+
18+type URIBase = Maybe Text
19+type URIBaseAndFragment = (Maybe Text, Maybe Text)
20+
21+updateResolutionScope :: URIBase -> Maybe Text -> URIBase
22+updateResolutionScope mScope idKeyword
23+ | Just t <- idKeyword = fst . baseAndFragment $ resolveScopeAgainst mScope t
24+ | otherwise = mScope
25+
26+resolveReference :: URIBase -> Text -> URIBaseAndFragment
27+resolveReference mScope t = baseAndFragment $ resolveScopeAgainst mScope t
28+
29+resolveFragment
30+ :: (FromJSON schema, ToJSON schema)
31+ => Maybe Text
32+ -> schema
33+ -> Maybe schema
34+resolveFragment Nothing schema = Just schema
35+resolveFragment (Just pointer) schema = do
36+ let urlDecoded = decodeUtf8 . urlDecode True . encodeUtf8 $ pointer
37+ p <- either (const Nothing) Just (AP.unescape urlDecoded)
38+ x <- either (const Nothing) Just (AP.resolve p (toJSON schema))
39+ case fromJSON x of
40+ Error _ -> Nothing
41+ Success schema' -> Just schema'
42+
43+--------------------------------------------------
44+-- * Helpers
45+--------------------------------------------------
46+
47+isRemoteReference :: Text -> Bool
48+isRemoteReference = T.isInfixOf "://"
49+
50+baseAndFragment :: Text -> URIBaseAndFragment
51+baseAndFragment = f . T.splitOn "#"
52+ where
53+ f :: [Text] -> URIBaseAndFragment
54+ f [x] = (g x, Nothing)
55+ f [x,y] = (g x, g y)
56+ f _ = (Nothing, Nothing)
57+
58+ g "" = Nothing
59+ g x = Just x
60+
61+resolveScopeAgainst :: Maybe Text -> Text -> Text
62+resolveScopeAgainst Nothing t = t
63+resolveScopeAgainst (Just scope) t
64+ | isRemoteReference t = t
65+ | otherwise = smartAppend
66+ where
67+ -- There shouldn't be a fragment at the end of a scope URI,
68+ -- but just in case a user leaves one in we want to be sure
69+ -- to cut it off before appending.
70+ smartAppend :: Text
71+ smartAppend =
72+ case baseAndFragment scope of
73+ (Just base,_) ->
74+ case T.unpack t of
75+ -- We want "/foo" and "#/bar" to combine into
76+ -- "/foo#/bar" not "/foo/#/bar".
77+ '#':_ -> base <> t
78+ _ -> T.pack (dropFileName (T.unpack base) </> T.unpack t)
79+ _ -> t
src/JSONSchema/Validator/Types.hs → src/Data/Validator/Types.hsrenamed+9−4
@@ -1,8 +1,13 @@
1−module JSONSchema.Validator.Types where
1+{-# LANGUAGE DeriveFunctor #-}
2
2
3−import Import
3+module Data.Validator.Types where
4
4
5+import Prelude
6+
7+import Data.Aeson (Value)
5−import Data.Profunctor (Profunctor(..))
8+import Data.Profunctor (Profunctor(..))
9+
10+import Data.Validator.Failure (Fail)
6
11
7data Validator schema val err = Validator
12data Validator schema val err = Validator
8 { _embedded :: val -> ([schema], [schema])
13 { _embedded :: val -> ([schema], [schema])
@@ -14,7 +19,7 @@ data Validator schema val err = Validator
14 -- This is done to allow static detection of loops, though this isn't
19 -- This is done to allow static detection of loops, though this isn't
15 -- implemented yet (though the Draft4 code does do live loop detection
20 -- implemented yet (though the Draft4 code does do live loop detection
16 -- during validation).
21 -- during validation).
17− , _validate :: val -> Value -> [err]
22+ , _validate :: val -> Value -> [Fail err]
18 } deriving Functor
23 } deriving Functor
19
24
20instance Profunctor (Validator schema) where
25instance Profunctor (Validator schema) where
src/JSONSchema/Validator/Utils.hs → src/Data/Validator/Utils.hsrenamed+54−40
@@ -1,15 +1,18 @@
1−module JSONSchema.Validator.Utils where
2
1
3−import Import
2+module Data.Validator.Utils where
4
3
5−import Control.Monad (fail)
4+import Import
5+import Prelude
6+
7+import Control.Arrow
6−import qualified Data.HashMap.Strict as HM
8+import qualified Data.HashMap.Strict as HM
9+import Data.List.NonEmpty (NonEmpty)
7−import qualified Data.List.NonEmpty as NE
10+import qualified Data.List.NonEmpty as NE
8−import Data.Scientific (Scientific, fromFloatDigits)
11+import Data.Scientific (Scientific, fromFloatDigits)
9−import Data.Set (Set)
12+import Data.Set (Set)
10−import qualified Data.Set as S
13+import qualified Data.Set as S
11−import qualified Data.Text as T
14+import qualified Data.Text as T
12−import qualified Data.Vector as V
15+import qualified Data.Vector as V
13
16
14--------------------------------------------------
17--------------------------------------------------
15-- * QuickCheck
18-- * QuickCheck
@@ -26,32 +29,28 @@ arbitraryPositiveScientific = (fromFloatDigits :: Double -> Scientific)
26 . getPositive
29 . getPositive
27 <$> arbitrary
30 <$> arbitrary
28
31
29−newtype ArbitraryValue
32+arbitraryValue :: Gen Value
30− = ArbitraryValue { _unArbitraryValue :: Value }
33+arbitraryValue = sized f
31− deriving (Eq, Show)
32−
33−instance Arbitrary ArbitraryValue where
34− arbitrary = ArbitraryValue <$> sized f
35− where
34+ where
36− f :: Int -> Gen Value
35+ f :: Int -> Gen Value
37− f n | n <= 1 = oneof nonRecursive
36+ f n | n <= 1 = oneof nonRecursive
38− | otherwise = oneof $
37+ | otherwise = oneof $
39− fmap (Array . V.fromList) (traverse (const (f (n `div` 10)))
38+ fmap (Array . V.fromList) (traverse (const (f (n `div` 10)))
40− =<< (arbitrary :: Gen [()]))
39+ =<< (arbitrary :: Gen [()]))
41− : fmap (Object . HM.fromList) (traverse (const (g (n `div` 10)))
40+ : fmap (Object . HM.fromList) (traverse (const (g (n `div` 10)))
42− =<< (arbitrary :: Gen [()]))
41+ =<< (arbitrary :: Gen [()]))
43− : nonRecursive
42+ : nonRecursive
44−
43+
45− g :: Int -> Gen (Text, Value)
44+ g :: Int -> Gen (Text, Value)
46− g n = (,) <$> arbitraryText <*> f n
45+ g n = (,) <$> arbitraryText <*> f n
47−
46+
48− nonRecursive :: [Gen Value]
47+ nonRecursive :: [Gen Value]
49− nonRecursive =
48+ nonRecursive =
50− [ pure Null
49+ [ pure Null
51− , Bool <$> arbitrary
50+ , Bool <$> arbitrary
52− , String <$> arbitraryText
51+ , String <$> arbitraryText
53− , Number <$> arbitraryScientific
52+ , Number <$> arbitraryScientific
54− ]
53+ ]
55
54
56arbitraryHashMap :: Arbitrary a => Gen (HashMap Text a)
55arbitraryHashMap :: Arbitrary a => Gen (HashMap Text a)
57arbitraryHashMap = HM.fromList . fmap (first T.pack) <$> arbitrary
56arbitraryHashMap = HM.fromList . fmap (first T.pack) <$> arbitrary
@@ -82,11 +81,16 @@ instance Arbitrary a => Arbitrary (NonEmpty' a) where
82-- * allUniqueValues
81-- * allUniqueValues
83--------------------------------------------------
82--------------------------------------------------
84
83
85−allUniqueValues :: (Foldable f, Functor f) => f Value -> Bool
84+allUniqueValues :: Vector Value -> Bool
86−allUniqueValues = allUnique . fmap OrdValue
85+allUniqueValues = allUnique . fmap OrdValue . V.toList
87
86
87+-- NOTE: When we no longer support GHC 7.8 we can generalize
88−allUnique :: (Foldable f, Ord a) => f a -> Bool
88+-- allUnique to work on any Foldable and remove this function.
89+allUniqueValues' :: NonEmpty Value -> Bool
90+allUniqueValues' = allUnique . fmap OrdValue . NE.toList
91+
92+allUnique :: (Ord a) => [a] -> Bool
89−allUnique xs = S.size (S.fromList (toList xs)) == length xs
93+allUnique xs = S.size (S.fromList xs) == length xs
90
94
91-- | OrdValue's Ord instance needs benchmarking, but it allows us to
95-- | OrdValue's Ord instance needs benchmarking, but it allows us to
92-- use our 'allUnique' function instead of O(n^2) nub, so it's probably
96-- use our 'allUnique' function instead of O(n^2) nub, so it's probably
@@ -117,3 +121,13 @@ instance Ord OrdValue where
117
121
118 (OrdValue (Object x)) `compare` (OrdValue (Object y)) =
122 (OrdValue (Object x)) `compare` (OrdValue (Object y)) =
119 HM.toList (OrdValue <$> x) `compare` HM.toList (OrdValue <$> y)
123 HM.toList (OrdValue <$> x) `compare` HM.toList (OrdValue <$> y)
124+
125+--------------------------------------------------
126+-- * other
127+--------------------------------------------------
128+
129+fromJSONEither :: FromJSON a => Value -> Either Text a
130+fromJSONEither a =
131+ case fromJSON a of
132+ Error e -> Left (T.pack e)
133+ Success b -> Right b
src/Import.hs+9−16
@@ -1,19 +1,12 @@
1
1
2−module Import (module Export, fromJSONEither) where
2+module Import (module Export) where
3
3
4−import Protolude as Export
4+import Control.Applicative as Export
5−
6−import Data.Aeson as Export
5+import Data.Aeson as Export
6+import Data.Foldable as Export
7import Data.HashMap.Strict as Export (HashMap)
7import Data.HashMap.Strict as Export (HashMap)
8−import Data.List.NonEmpty as Export (NonEmpty)
8+import Data.Monoid as Export
9+import Data.Text as Export (Text)
10+import Data.Traversable as Export
9−import Data.Vector as Export (Vector)
11+import Data.Vector as Export (Vector)
10−import Test.QuickCheck as Export hiding (Failure, Result, Success,
12+import Test.QuickCheck as Export hiding (Failure, Result, Success)
11− (.&.))
12−
13−import qualified Data.Text as T
14−
15−fromJSONEither :: FromJSON a => Value -> Either Text a
16−fromJSONEither a =
17− case fromJSON a of
18− Error e -> Left (T.pack e)
19− Success b -> Right b
src/JSONSchema/Draft4.hsdeleted−186
@@ -1,186 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−
3−module JSONSchema.Draft4
4− ( -- * Draft 4 Schema
5− SchemaWithURI(..)
6− , Schema(..)
7− , SC.emptySchema
8−
9− -- * One-step validation (getting references over HTTP)
10− , fetchHTTPAndValidate
11− , HTTPValidationFailure(..)
12− , FE.HTTPFailure(..)
13− , SchemaInvalid(..)
14−
15− -- * One-step validation (getting references from the filesystem)
16− , fetchFilesystemAndValidate
17− , FilesystemValidationFailure(..)
18− , FE.FilesystemFailure(..)
19−
20− -- * Validation failure
21− , Invalid(..)
22− , ValidatorFailure(..)
23−
24− -- * Fetching tools
25− , URISchemaMap(..)
26− , referencesViaHTTP
27− , referencesViaFilesystem
28−
29− -- * Other Draft 4 things exported just in case
30− , metaSchema
31− , metaSchemaBytes
32− , schemaValidity
33− , referencesValidity
34− , checkSchema
35− , draft4FetchInfo
36− ) where
37−
38−import Import
39−
40−import qualified Data.ByteString as BS
41−import Data.FileEmbed (embedFile, makeRelativeToProject)
42−import qualified Data.HashMap.Strict as HM
43−import qualified Data.List.NonEmpty as NE
44−import Data.Maybe (fromMaybe)
45−
46−import JSONSchema.Draft4.Failure (Invalid(..), SchemaInvalid(..),
47− ValidatorFailure(..))
48−import JSONSchema.Draft4.Schema (Schema)
49−import qualified JSONSchema.Draft4.Schema as SC
50−import qualified JSONSchema.Draft4.Spec as Spec
51−import JSONSchema.Fetch (SchemaWithURI(..), URISchemaMap(..))
52−import qualified JSONSchema.Fetch as FE
53−
54−data HTTPValidationFailure
55− = HVRequest FE.HTTPFailure
56− | HVSchema SchemaInvalid
57− | HVData Invalid
58− deriving Show
59−
60−-- | Fetch recursively referenced schemas over HTTP, check that both the
61−-- original and referenced schemas are valid, then validate then data.
62−fetchHTTPAndValidate
63− :: SchemaWithURI Schema
64− -> Value
65− -> IO (Either HTTPValidationFailure ())
66−fetchHTTPAndValidate sw v = do
67− res <- referencesViaHTTP sw
68− pure (g =<< f =<< first HVRequest res)
69− where
70− f :: FE.URISchemaMap Schema
71− -> Either HTTPValidationFailure (Value -> [ValidatorFailure])
72− f references = first HVSchema (checkSchema references sw)
73−
74− g :: (Value -> [ValidatorFailure]) -> Either HTTPValidationFailure ()
75− g val = case NE.nonEmpty (val v) of
76− Nothing -> Right ()
77− Just failures -> Left (HVData Invalid
78− { _invalidSchema = _swSchema sw
79− , _invalidInstance = v
80− , _invalidFailures = failures
81− })
82−
83−data FilesystemValidationFailure
84− = FVRead FE.FilesystemFailure
85− | FVSchema SchemaInvalid
86− | FVData Invalid
87− deriving (Show, Eq)
88−
89−-- | Fetch recursively referenced schemas from the filesystem, check
90−-- that both the original and referenced schemas are valid, then validate
91−-- the data.
92−fetchFilesystemAndValidate
93− :: SchemaWithURI Schema
94− -> Value
95− -> IO (Either FilesystemValidationFailure ())
96−fetchFilesystemAndValidate sw v = do
97− res <- referencesViaFilesystem sw
98− pure (g =<< f =<< first FVRead res)
99− where
100− f :: FE.URISchemaMap Schema
101− -> Either FilesystemValidationFailure (Value -> [ValidatorFailure])
102− f references = first FVSchema (checkSchema references sw)
103−
104− g :: (Value -> [ValidatorFailure]) -> Either FilesystemValidationFailure ()
105− g val = case NE.nonEmpty (val v) of
106− Nothing -> Right ()
107− Just invalid -> Left (FVData Invalid
108− { _invalidSchema = _swSchema sw
109− , _invalidInstance = v
110− , _invalidFailures = invalid
111− })
112−
113−-- | An instance of 'JSONSchema.Fetch.FetchInfo' specialized for
114−-- JSON Schema Draft 4.
115−draft4FetchInfo :: FE.FetchInfo Schema
116−draft4FetchInfo = FE.FetchInfo Spec.embedded SC._schemaId SC._schemaRef
117−
118−-- | Fetch the schemas recursively referenced by a starting schema over HTTP.
119−referencesViaHTTP
120− :: SchemaWithURI Schema
121− -> IO (Either FE.HTTPFailure (FE.URISchemaMap Schema))
122−referencesViaHTTP = FE.referencesViaHTTP' draft4FetchInfo
123−
124−-- | Fetch the schemas recursively referenced by a starting schema from
125−-- the filesystem.
126−referencesViaFilesystem
127− :: SchemaWithURI Schema
128− -> IO (Either FE.FilesystemFailure (FE.URISchemaMap Schema))
129−referencesViaFilesystem = FE.referencesViaFilesystem' draft4FetchInfo
130−
131−-- | Checks if a schema and a set of referenced schemas are valid.
132−--
133−-- Return a function to validate data.
134−checkSchema
135− :: FE.URISchemaMap Schema
136− -> SchemaWithURI Schema
137− -> Either SchemaInvalid (Value -> [ValidatorFailure])
138−checkSchema sm sw =
139− case NE.nonEmpty failures of
140− Just fs -> Left (SchemaInvalid fs)
141− Nothing -> Right (Spec.specValidate sm sw)
142− where
143− failures :: [(Maybe Text, NonEmpty ValidatorFailure)]
144− failures =
145− let refFailures = first Just <$> referencesValidity sm
146− in case NE.nonEmpty (schemaValidity (_swSchema sw)) of
147− Nothing -> refFailures
148− Just errs -> (Nothing,errs) : refFailures
149−
150−metaSchema :: Schema
151−metaSchema =
152− fromMaybe (panic "Schema decode failed (this should never happen)")
153− . decodeStrict
154− $ metaSchemaBytes
155−
156−metaSchemaBytes :: BS.ByteString
157−metaSchemaBytes =
158− $(makeRelativeToProject "src/draft4.json" >>= embedFile)
159−
160−-- | Check that a schema itself is valid
161−-- (if so the returned list will be empty).
162−schemaValidity :: Schema -> [ValidatorFailure]
163−schemaValidity =
164− Spec.specValidate schemaMap (SchemaWithURI metaSchema Nothing) . toJSON
165− where
166− schemaMap :: URISchemaMap Schema
167− schemaMap =
168− URISchemaMap (HM.singleton "http://json-schema.org/draft-04/schema"
169− metaSchema)
170−
171−-- | Check that a set of referenced schemas are valid
172−-- (if so the returned list will be empty).
173−referencesValidity
174− :: FE.URISchemaMap Schema
175− -> [(Text, NonEmpty ValidatorFailure)]
176− -- ^ The first item of the tuple is the URI of a schema, the second
177− -- is that schema's validation errors.
178−referencesValidity = HM.foldlWithKey' f mempty . FE._unURISchemaMap
179− where
180− f :: [(Text, NonEmpty ValidatorFailure)]
181− -> Text
182− -> Schema
183− -> [(Text, NonEmpty ValidatorFailure)]
184− f acc k v = case NE.nonEmpty (schemaValidity v) of
185− Nothing -> acc
186− Just errs -> (k,errs) : acc
src/JSONSchema/Draft4/Failure.hsdeleted−58
@@ -1,58 +0,0 @@
1−module JSONSchema.Draft4.Failure where
2−
3−import Import
4−
5−import JSONSchema.Draft4.Schema (Schema)
6−import qualified JSONSchema.Validator.Draft4 as VAL
7−
8−-- | Used to report an entire instance being invalidated, as opposed
9−-- to the failure of a single validator.
10−data Invalid = Invalid
11− { _invalidSchema :: Schema
12− , _invalidInstance :: Value
13− , _invalidFailures :: NonEmpty ValidatorFailure
14− } deriving (Eq, Show)
15−
16−data ValidatorFailure
17− = FailureMultipleOf VAL.MultipleOfInvalid
18− | FailureMaximum VAL.MaximumInvalid
19− | FailureMinimum VAL.MinimumInvalid
20−
21− | FailureMaxLength VAL.MaxLengthInvalid
22− | FailureMinLength VAL.MinLengthInvalid
23− | FailurePattern VAL.PatternInvalid
24−
25− | FailureMaxItems VAL.MaxItemsInvalid
26− | FailureMinItems VAL.MinItemsInvalid
27− | FailureUniqueItems VAL.UniqueItemsInvalid
28− | FailureItems (VAL.ItemsInvalid ValidatorFailure)
29− | FailureAdditionalItems (VAL.AdditionalItemsInvalid ValidatorFailure)
30−
31− | FailureMaxProperties VAL.MaxPropertiesInvalid
32− | FailureMinProperties VAL.MinPropertiesInvalid
33− | FailureRequired VAL.RequiredInvalid
34− | FailureDependencies (VAL.DependenciesInvalid ValidatorFailure)
35− | FailurePropertiesRelated (VAL.PropertiesRelatedInvalid ValidatorFailure)
36−
37− | FailureRef (VAL.RefInvalid ValidatorFailure)
38− | FailureEnum VAL.EnumInvalid
39− | FailureType VAL.TypeValidatorInvalid
40− | FailureAllOf (VAL.AllOfInvalid ValidatorFailure)
41− | FailureAnyOf (VAL.AnyOfInvalid ValidatorFailure)
42− | FailureOneOf (VAL.OneOfInvalid ValidatorFailure)
43− | FailureNot VAL.NotValidatorInvalid
44− deriving (Eq, Show)
45−
46−-- | A description of why a schema (or one of its reference) is itself
47−-- invalid.
48−--
49−-- 'Nothing' indicates the starting schema. 'Just' indicates a referenced
50−-- schema. The contents of the 'Just' is the schema's URI.
51−--
52−-- NOTE: 'HashMap (Maybe Text) Invalid' would be a nicer way of
53−-- defining this, but then we lose the guarantee that there's at least
54−-- one key.
55−newtype SchemaInvalid
56− = SchemaInvalid {
57− _unSchemaInvalid :: NonEmpty (Maybe Text, NonEmpty ValidatorFailure) }
58− deriving (Eq, Show)
src/JSONSchema/Draft4/Spec.hsdeleted−150
@@ -1,150 +0,0 @@
1−module JSONSchema.Draft4.Spec where
2−
3−import Import
4−
5−import Data.Maybe (fromMaybe)
6−import Data.Profunctor (Profunctor(..))
7−
8−import JSONSchema.Draft4.Failure
9−import JSONSchema.Draft4.Schema (Schema(..), emptySchema)
10−import JSONSchema.Fetch (SchemaWithURI(..), URISchemaMap(..))
11−import qualified JSONSchema.Fetch as FE
12−import JSONSchema.Types (Spec(..))
13−import qualified JSONSchema.Types as JT
14−import JSONSchema.Validator.Draft4
15−import JSONSchema.Validator.Reference (BaseURI(..), Scope(..),
16− updateResolutionScope)
17−
18−-- | An implementation of 'JT.embedded'.
19−embedded :: Schema -> ([Schema], [Schema])
20−embedded s =
21− JT.embedded (d4Spec mempty mempty (Scope s Nothing (BaseURI Nothing))) s
22−
23−specValidate
24− :: URISchemaMap Schema
25− -> SchemaWithURI Schema
26− -> Value
27− -> [ValidatorFailure]
28−specValidate schemaMap sw =
29− JT.validate (d4Spec schemaMap visited scope) (_swSchema sw)
30− where
31− visited :: VisitedSchemas
32− visited = VisitedSchemas [(Nothing, Nothing)]
33−
34− scope :: Scope Schema
35− scope = Scope
36− { _topLevelDocument = _swSchema sw
37− , _documentURI = _swURI sw
38− , _currentBaseURI = updateResolutionScope (BaseURI (_swURI sw))
39− (_schemaId (_swSchema sw))
40− }
41−
42−validateSubschema
43− :: URISchemaMap Schema
44− -> VisitedSchemas
45− -> Scope Schema
46− -> Schema
47− -> Value
48− -> [ValidatorFailure]
49−validateSubschema schemaMap visited scope schema =
50− JT.validate (d4Spec schemaMap visited newScope) schema
51− where
52− newScope :: Scope Schema
53− newScope = scope
54− { _currentBaseURI = updateResolutionScope (_currentBaseURI scope)
55− (_schemaId schema)
56− }
57−
58−d4Spec
59− :: URISchemaMap Schema
60− -> VisitedSchemas
61− -> Scope Schema
62− -> Spec Schema ValidatorFailure
63−d4Spec schemaMap visited scope = Spec $
64− [ dimap
65− (fmap Ref . _schemaRef)
66− FailureRef
67− (refValidator (FE.getReference schemaMap) updateScope valRef visited scope)
68− ]
69−
70− <> fmap (lmap disableIfRefPresent)
71− [ dimap (fmap MultipleOf . _schemaMultipleOf) FailureMultipleOf multipleOfValidator
72− , dimap
73− (\s -> Maximum (fromMaybe False (_schemaExclusiveMaximum s)) <$> _schemaMaximum s)
74− FailureMaximum
75− maximumValidator
76− , dimap
77− (\s -> Minimum (fromMaybe False (_schemaExclusiveMinimum s)) <$> _schemaMinimum s)
78− FailureMinimum
79− minimumValidator
80−
81− , dimap (fmap MaxLength . _schemaMaxLength) FailureMaxLength maxLengthValidator
82− , dimap (fmap MinLength . _schemaMinLength) FailureMinLength minLengthValidator
83− , dimap (fmap PatternValidator . _schemaPattern) FailurePattern patternValidator
84−
85− , dimap (fmap MaxItems . _schemaMaxItems) FailureMaxItems maxItemsValidator
86− , dimap (fmap MinItems . _schemaMinItems) FailureMinItems minItemsValidator
87− , dimap (fmap UniqueItems . _schemaUniqueItems) FailureUniqueItems uniqueItemsValidator
88− , dimap
89− (\s -> ItemsRelated
90− { _irItems = _schemaItems s
91− , _irAdditional = _schemaAdditionalItems s
92− })
93− (\err -> case err of
94− IRInvalidItems e -> FailureItems e
95− IRInvalidAdditional e -> FailureAdditionalItems e)
96− (itemsRelatedValidator descend)
97− , lmap (fmap Definitions . _schemaDefinitions) definitionsEmbedded
98−
99− , dimap
100− (fmap MaxProperties . _schemaMaxProperties)
101− FailureMaxProperties
102− maxPropertiesValidator
103− , dimap
104− (fmap MinProperties . _schemaMinProperties)
105− FailureMinProperties
106− minPropertiesValidator
107− , dimap (fmap Required . _schemaRequired) FailureRequired requiredValidator
108− , dimap
109− (fmap DependenciesValidator . _schemaDependencies)
110− FailureDependencies
111− (dependenciesValidator descend)
112− , dimap
113− (\s -> PropertiesRelated
114− { _propProperties = _schemaProperties s
115− , _propPattern = _schemaPatternProperties s
116− , _propAdditional = _schemaAdditionalProperties s
117− })
118− FailurePropertiesRelated
119− (propertiesRelatedValidator descend)
120−
121− , dimap (fmap EnumValidator . _schemaEnum) FailureEnum enumValidator
122− , dimap (fmap TypeContext . _schemaType) FailureType typeValidator
123− , dimap (fmap AllOf . _schemaAllOf) FailureAllOf (allOfValidator lateral)
124− , dimap (fmap AnyOf . _schemaAnyOf) FailureAnyOf (anyOfValidator lateral)
125− , dimap (fmap OneOf . _schemaOneOf) FailureOneOf (oneOfValidator lateral)
126− , dimap (fmap NotValidator . _schemaNot) FailureNot (notValidator lateral)
127− ]
128− where
129− disableIfRefPresent :: Schema -> Schema
130− disableIfRefPresent schema =
131− case _schemaRef schema of
132− Nothing -> schema
133− Just _ -> emptySchema
134−
135− updateScope :: BaseURI -> Schema -> BaseURI
136− updateScope uri schema = updateResolutionScope uri (_schemaId schema)
137−
138− valRef
139− :: VisitedSchemas
140− -> Scope Schema
141− -> Schema
142− -> Value
143− -> [ValidatorFailure]
144− valRef vis sc = JT.validate (d4Spec schemaMap vis sc)
145−
146− descend :: Schema -> Value -> [ValidatorFailure]
147− descend = validateSubschema schemaMap mempty scope
148−
149− lateral :: Schema -> Value -> [ValidatorFailure]
150− lateral = validateSubschema schemaMap visited scope
src/JSONSchema/Fetch.hsdeleted−175
@@ -1,175 +0,0 @@
1−module JSONSchema.Fetch where
2−
3−import Import
4−
5−import qualified Control.Exception.Safe as Safe
6−import Control.Monad (foldM)
7−import qualified Data.ByteString as BS
8−import qualified Data.ByteString.Lazy as LBS
9−import qualified Data.HashMap.Strict as HM
10−import qualified Data.Semigroup as S
11−import qualified Data.Text as T
12−import qualified Network.HTTP.Client as NC
13−import qualified Network.HTTP.Client.TLS as NCTLS
14−
15−import JSONSchema.Validator.Reference (BaseURI(..), resolveReference,
16− updateResolutionScope)
17−
18−--------------------------------------------------
19−-- * Types
20−--------------------------------------------------
21−
22−-- | This is all the fetching functions need to know about a particular
23−-- JSON Schema draft, e.g. JSON Schema Draft 4.
24−data FetchInfo schema = FetchInfo
25− { _fiEmbedded :: schema -> ([schema], [schema])
26− , _fiId :: schema -> Maybe Text
27− , _fiRef :: schema -> Maybe Text
28− }
29−
30−-- | Keys are URIs (without URI fragments).
31−newtype URISchemaMap schema
32− = URISchemaMap { _unURISchemaMap :: HashMap Text schema }
33− deriving (Eq, Show, S.Semigroup, Monoid)
34−
35−-- | A top-level schema along with its location.
36−data SchemaWithURI schema = SchemaWithURI
37− { _swSchema :: !schema
38− , _swURI :: !(Maybe Text)
39− -- ^ This is the URI from which the document was originally fetched.
40− -- It's different than the schema's "id" field, which controls scope
41− -- when resolving references contained in the schema.
42− } deriving (Eq, Show)
43−
44−getReference :: URISchemaMap schema -> Text -> Maybe schema
45−getReference schemaMap t = HM.lookup t (_unURISchemaMap schemaMap)
46−
47−--------------------------------------------------
48−-- * Fetch via HTTP
49−--------------------------------------------------
50−
51−data HTTPFailure
52− = HTTPParseFailure Text
53− | HTTPRequestFailure NC.HttpException
54− deriving Show
55−
56−-- | Take a schema. Retrieve every document either it or its subschemas
57−-- include via the "$ref" keyword.
58−referencesViaHTTP'
59− :: forall schema. FromJSON schema
60− => FetchInfo schema
61− -> SchemaWithURI schema
62− -> IO (Either HTTPFailure (URISchemaMap schema))
63−referencesViaHTTP' info sw = do
64− manager <- NC.newManager NCTLS.tlsManagerSettings
65− let f = referencesMethodAgnostic (getURL manager) info sw
66− Safe.catch (first HTTPParseFailure <$> f) handler
67− where
68− getURL :: NC.Manager -> Text -> IO BS.ByteString
69− getURL man url = do
70− request <- NC.parseUrlThrow (T.unpack url)
71− LBS.toStrict . NC.responseBody <$> NC.httpLbs request man
72−
73− handler
74− :: NC.HttpException
75− -> IO (Either HTTPFailure (URISchemaMap schema))
76− handler = pure . Left . HTTPRequestFailure
77−
78−--------------------------------------------------
79−-- * Fetch via Filesystem
80−--------------------------------------------------
81−
82−data FilesystemFailure
83− = FSParseFailure Text
84− | FSReadFailure IOException
85− deriving (Show, Eq)
86−
87−referencesViaFilesystem'
88− :: forall schema. FromJSON schema
89− => FetchInfo schema
90− -> SchemaWithURI schema
91− -> IO (Either FilesystemFailure (URISchemaMap schema))
92−referencesViaFilesystem' info sw =
93− Safe.catch (first FSParseFailure <$> f) handler
94− where
95− f :: IO (Either Text (URISchemaMap schema))
96− f = referencesMethodAgnostic (BS.readFile . T.unpack) info sw
97−
98− handler
99− :: IOException
100− -> IO (Either FilesystemFailure (URISchemaMap schema))
101− handler = pure . Left . FSReadFailure
102−
103−--------------------------------------------------
104−-- * Method Agnostic Fetching Tools
105−--------------------------------------------------
106−
107−-- | A version of 'fetchReferencedSchema's where the function to fetch
108−-- schemas is provided by the user. This allows restrictions to be added,
109−-- e.g. rejecting non-local URIs.
110−referencesMethodAgnostic
111− :: forall schema. FromJSON schema
112− => (Text -> IO BS.ByteString)
113− -> FetchInfo schema
114− -> SchemaWithURI schema
115− -> IO (Either Text (URISchemaMap schema))
116−referencesMethodAgnostic fetchRef info =
117− getRecursiveReferences fetchRef info (URISchemaMap mempty)
118−
119−getRecursiveReferences
120− :: forall schema. FromJSON schema
121− => (Text -> IO BS.ByteString)
122− -> FetchInfo schema
123− -> URISchemaMap schema
124− -> SchemaWithURI schema
125− -> IO (Either Text (URISchemaMap schema))
126−getRecursiveReferences fetchRef info referenced sw =
127− foldM f (Right referenced) (includeSubschemas info sw)
128− where
129− f :: Either Text (URISchemaMap schema)
130− -> SchemaWithURI schema
131− -> IO (Either Text (URISchemaMap schema))
132− f (Left e) _ = pure (Left e)
133− f (Right (URISchemaMap schemaMap)) (SchemaWithURI schema mURI) =
134− case newRef of
135− Nothing -> pure (Right (URISchemaMap schemaMap))
136− Just uri -> do
137− bts <- fetchRef uri
138− case eitherDecodeStrict bts of
139− Left e -> pure . Left . T.pack $ e
140− Right s -> getRecursiveReferences
141− fetchRef
142− info
143− (URISchemaMap (HM.insert uri s schemaMap))
144− (SchemaWithURI s (Just uri))
145− where
146− newRef :: Maybe Text
147− newRef = do
148− ref <- _fiRef info schema
149−
150− -- Consider the reference before updating the scope.
151− -- If it's a only a fragment this isn't referencing
152− -- a new document.
153− void (fst (resolveReference (BaseURI Nothing) ref))
154−
155− uri <- fst (resolveReference (BaseURI mURI) ref)
156− case HM.lookup uri schemaMap of
157− Nothing -> Just uri
158− Just _ -> Nothing
159−
160−-- | Return the schema passed in as an argument, as well as every
161−-- subschema contained within it.
162−includeSubschemas
163− :: forall schema.
164− FetchInfo schema
165− -> SchemaWithURI schema
166− -> [SchemaWithURI schema]
167−includeSubschemas info (SchemaWithURI schema mURI) =
168− SchemaWithURI schema mURI
169− : (includeSubschemas info =<< subSchemas)
170− where
171− subSchemas :: [SchemaWithURI schema]
172− subSchemas =
173− let newScope = updateResolutionScope (BaseURI mURI) (_fiId info schema)
174− updateScope s = SchemaWithURI s (_unBaseURI newScope)
175− in updateScope <$> uncurry (<>) (_fiEmbedded info schema)
src/JSONSchema/Validator/Draft4.hsdeleted−242
@@ -1,242 +0,0 @@
1−-- | Turn the validation functions into actual 'Validator's.
2−--
3−-- This is frankly a lot of busywork. It can perhaps be moved into the
4−-- validator modules themselves once we're sure this is the right design.
5−module JSONSchema.Validator.Draft4
6− ( module JSONSchema.Validator.Draft4
7− , module Export
8− ) where
9−
10−import Import
11−
12−import qualified Data.HashMap.Strict as HM
13−import qualified Data.List.NonEmpty as NE
14−
15−import JSONSchema.Validator.Draft4.Any as Export
16−import JSONSchema.Validator.Draft4.Array as Export
17−import JSONSchema.Validator.Draft4.Number as Export
18−import JSONSchema.Validator.Draft4.Object as Export
19−import JSONSchema.Validator.Draft4.String as Export
20−import JSONSchema.Validator.Reference (BaseURI(..), Scope(..))
21−import JSONSchema.Validator.Types (Validator(..))
22−
23−-- | For internal use.
24−--
25−-- Take a validation function, a possibly existing validator, and some data.
26−-- If the validator is exists and can validate the type of data we have,
27−-- attempt to do so and return any failures.
28−run :: FromJSON b => (a -> b -> Maybe c) -> Maybe a -> Value -> [c]
29−run _ Nothing _ = mempty
30−run f (Just a) b =
31− case fromJSONEither b of
32− Left _ -> mempty
33− Right c -> maybeToList (f a c)
34−
35−-- | For internal use.
36−noEmbedded :: a -> ([b], [b])
37−noEmbedded = const (mempty, mempty)
38−
39−--------------------------------------------------
40−-- * Numbers
41−--------------------------------------------------
42−
43−multipleOfValidator :: Validator a (Maybe MultipleOf) MultipleOfInvalid
44−multipleOfValidator = Validator noEmbedded (run multipleOfVal)
45−
46−maximumValidator :: Validator a (Maybe Maximum) MaximumInvalid
47−maximumValidator = Validator noEmbedded (run maximumVal)
48−
49−minimumValidator :: Validator a (Maybe Minimum) MinimumInvalid
50−minimumValidator = Validator noEmbedded (run minimumVal)
51−
52−--------------------------------------------------
53−-- * Strings
54−--------------------------------------------------
55−
56−maxLengthValidator :: Validator a (Maybe MaxLength) MaxLengthInvalid
57−maxLengthValidator = Validator noEmbedded (run maxLengthVal)
58−
59−minLengthValidator :: Validator a (Maybe MinLength) MinLengthInvalid
60−minLengthValidator = Validator noEmbedded (run minLengthVal)
61−
62−patternValidator :: Validator a (Maybe PatternValidator) PatternInvalid
63−patternValidator = Validator noEmbedded (run patternVal)
64−
65−--------------------------------------------------
66−-- * Arrays
67−--------------------------------------------------
68−
69−maxItemsValidator :: Validator a (Maybe MaxItems) MaxItemsInvalid
70−maxItemsValidator = Validator noEmbedded (run maxItemsVal)
71−
72−minItemsValidator :: Validator a (Maybe MinItems) MinItemsInvalid
73−minItemsValidator = Validator noEmbedded (run minItemsVal)
74−
75−uniqueItemsValidator :: Validator a (Maybe UniqueItems) UniqueItemsInvalid
76−uniqueItemsValidator = Validator noEmbedded (run uniqueItemsVal)
77−
78−-- TODO: Add tests to the language agnostic test suite to
79−-- make sure @"additionalItems"@ subschemas are embedded correctly.
80−itemsRelatedValidator
81− :: (schema -> Value -> [err])
82− -> Validator schema (ItemsRelated schema) (ItemsRelatedInvalid err)
83−itemsRelatedValidator f =
84− Validator
85− (\a -> ( mempty
86− , case _irItems a of
87− Just (ItemsObject b) -> pure b
88− Just (ItemsArray cs) -> cs
89− Nothing -> mempty
90− <> case _irAdditional a of
91− Just (AdditionalObject b) -> pure b
92− _ -> mempty
93− ))
94− (\a b -> case fromJSONEither b of
95− Left _ -> mempty
96− Right c -> itemsRelatedVal f a c)
97−
98−--------------------------------------------------
99−-- * Objects
100−--------------------------------------------------
101−
102−maxPropertiesValidator
103− :: Validator
104− a
105− (Maybe MaxProperties)
106− MaxPropertiesInvalid
107−maxPropertiesValidator = Validator noEmbedded (run maxPropertiesVal)
108−
109−minPropertiesValidator
110− :: Validator
111− a
112− (Maybe MinProperties)
113− MinPropertiesInvalid
114−minPropertiesValidator = Validator noEmbedded (run minPropertiesVal)
115−
116−requiredValidator :: Validator a (Maybe Required) RequiredInvalid
117−requiredValidator = Validator noEmbedded (run requiredVal)
118−
119−dependenciesValidator
120− :: (schema -> Value -> [err])
121− -> Validator
122− schema
123− (Maybe (DependenciesValidator schema))
124− (DependenciesInvalid err)
125−dependenciesValidator f =
126− Validator
127− (maybe mempty ( (\a -> (mempty, a))
128− . catMaybes . fmap checkDependency
129− . HM.elems . _unDependenciesValidator
130− ))
131− (run (dependenciesVal f))
132− where
133− checkDependency :: Dependency schema -> Maybe schema
134− checkDependency (PropertyDependency _) = Nothing
135− checkDependency (SchemaDependency s) = Just s
136−
137−propertiesRelatedValidator
138− :: (schema -> Value -> [err])
139− -> Validator
140− schema
141− (PropertiesRelated schema)
142− (PropertiesRelatedInvalid err)
143−propertiesRelatedValidator f =
144− Validator
145− (\a -> ( mempty
146− , HM.elems (fromMaybe mempty (_propProperties a))
147− <> HM.elems (fromMaybe mempty (_propPattern a))
148− <> case _propAdditional a of
149− Just (AdditionalPropertiesObject b) -> [b]
150− _ -> mempty
151− ))
152− (\a b -> case fromJSONEither b of
153− Left _ -> mempty
154− Right c -> maybeToList (propertiesRelatedVal f a c))
155−
156−newtype Definitions schema
157− = Definitions { _unDefinitions :: HashMap Text schema }
158− deriving (Eq, Show)
159−
160−instance FromJSON schema => FromJSON (Definitions schema) where
161− parseJSON = withObject "Definitions" $ \o ->
162− Definitions <$> o .: "definitions"
163−
164−definitionsEmbedded
165− :: Validator
166− schema
167− (Maybe (Definitions schema))
168− err
169−definitionsEmbedded =
170− Validator
171− (\a -> case a of
172− Just (Definitions b) -> (mempty, HM.elems b)
173− Nothing -> (mempty, mempty))
174− (const (const mempty))
175−
176−--------------------------------------------------
177−-- * Any
178−--------------------------------------------------
179−
180−refValidator
181− :: (FromJSON schema, ToJSON schema)
182− => (Text -> Maybe schema)
183− -> (BaseURI -> schema -> BaseURI)
184− -> (VisitedSchemas -> Scope schema -> schema -> Value -> [err])
185− -> VisitedSchemas
186− -> Scope schema
187− -> Validator a (Maybe Ref) (RefInvalid err)
188−refValidator getRef updateScope f visited scope =
189− Validator
190− noEmbedded
191− (run (refVal getRef updateScope f visited scope))
192−
193−enumValidator :: Validator a (Maybe EnumValidator) EnumInvalid
194−enumValidator = Validator noEmbedded (run enumVal)
195−
196−typeValidator :: Validator a (Maybe TypeContext) TypeValidatorInvalid
197−typeValidator = Validator noEmbedded (run typeVal)
198−
199−allOfValidator
200− :: (schema -> Value -> [err])
201− -> Validator schema (Maybe (AllOf schema)) (AllOfInvalid err)
202−allOfValidator f =
203− Validator
204− (\a -> case a of
205− Just (AllOf b) -> (NE.toList b, mempty)
206− Nothing -> (mempty, mempty))
207− (run (allOfVal f))
208−
209−anyOfValidator
210− :: (schema -> Value -> [err])
211− -> Validator schema (Maybe (AnyOf schema)) (AnyOfInvalid err)
212−anyOfValidator f =
213− Validator
214− (\a -> case a of
215− Just (AnyOf b) -> (NE.toList b, mempty)
216− Nothing -> (mempty, mempty))
217− (run (anyOfVal f))
218−
219−oneOfValidator
220− :: ToJSON schema
221− => (schema -> Value -> [err])
222− -> Validator schema (Maybe (OneOf schema)) (OneOfInvalid err)
223−oneOfValidator f =
224− Validator
225− (\a -> case a of
226− Just (OneOf b) -> (NE.toList b, mempty)
227− Nothing -> (mempty, mempty))
228− (run (oneOfVal f))
229−
230−notValidator
231− :: ToJSON schema
232− => (schema -> Value -> [err])
233− -> Validator
234− schema
235− (Maybe (NotValidator schema))
236− NotValidatorInvalid
237−notValidator f =
238− Validator
239− (\a -> case a of
240− Just (NotValidator b) -> (pure b, mempty)
241− Nothing -> (mempty, mempty))
242− (run (notVal f))
src/JSONSchema/Validator/Draft4/Any.hsdeleted−509
@@ -1,509 +0,0 @@
1−module JSONSchema.Validator.Draft4.Any where
2−
3−import Import hiding ((<>))
4−
5−import Data.Aeson.TH (constructorTagModifier)
6−import Data.Char (toLower)
7−import qualified Data.HashMap.Strict as HM
8−import Data.List.NonEmpty (NonEmpty((:|)))
9−import qualified Data.List.NonEmpty as NE
10−import qualified Data.Scientific as SCI
11−import Data.Semigroup
12−import Data.Set (Set)
13−import qualified Data.Set as Set
14−import Data.Text.Encoding.Error (UnicodeException)
15−import qualified JSONPointer as JP
16−import Network.HTTP.Types.URI (urlDecode)
17−
18−import JSONSchema.Validator.Reference (BaseURI(..), Scope(..),
19− URIAndFragment,
20− resolveReference)
21−import qualified JSONSchema.Validator.Utils as UT
22−
23−--------------------------------------------------
24−-- * $ref
25−--------------------------------------------------
26−
27−newtype Ref
28− = Ref { _unRef :: Text }
29− deriving (Eq, Show)
30−
31−instance FromJSON Ref where
32− parseJSON = withObject "Ref" $ \o ->
33− Ref <$> o .: "$ref"
34−
35−data RefInvalid err
36− = RefResolution Text
37− -- ^ Indicates a reference that failed to resolve.
38− --
39− -- NOTE: The language agnostic test suite doesn't specify if this should
40− -- cause a validation error or should allow data to pass. We choose to
41− -- return a validation error.
42− --
43− -- Also note that ideally we would enforce in the type system that any
44− -- failing references be dealt with before valididation. Then this could
45− -- be removed entirely.
46− | RefPointerResolution JSONPointerError
47− | RefLoop Text VisitedSchemas URIAndFragment
48− | RefInvalid Text Value (NonEmpty err)
49− -- ^ 'Text' is the URI and 'Value' is the linked schema.
50− deriving (Eq, Show)
51−
52−newtype VisitedSchemas
53− = VisitedSchemas { _unVisited :: [URIAndFragment] }
54− deriving (Eq, Show, Semigroup, Monoid)
55−
56−refVal
57− :: forall err schema. (FromJSON schema, ToJSON schema)
58− => (Text -> Maybe schema)
59− -- ^ Look up a schema.
60− -> (BaseURI -> schema -> BaseURI)
61− -- ^ Update scope (needed after moving deeper into nested schemas).
62− -> (VisitedSchemas -> Scope schema -> schema -> Value -> [err])
63− -- ^ Validate data.
64− -> VisitedSchemas
65− -> Scope schema
66− -> Ref
67− -> Value
68− -> Maybe (RefInvalid err)
69−refVal getRef updateScope val visited scope (Ref reference) x
70− | (mURI, mFragment) `elem` _unVisited visited =
71− Just (RefLoop reference visited (mURI, mFragment))
72− | otherwise = leftToMaybe $ do
73−
74− -- Get the referenced document
75−
76− (newScope, doc) <- first RefResolution
77− $ getDocument getRef updateScope scope mURI reference
78−
79− -- Get the correct subschema within that document.
80−
81− res <- case mFragment of
82− Nothing -> Right (newScope, doc)
83− Just fragment -> first RefPointerResolution
84− $ resolveFragment updateScope newScope fragment
85− let (finalScope, schema) = res
86−
87− -- Check if that schema is valid.
88−
89− let newVisited = VisitedSchemas [(_documentURI newScope, mFragment)]
90− <> visited
91− failures = val newVisited finalScope schema x
92− first (RefInvalid reference (toJSON schema))
93− . maybeToLeft ()
94− $ NE.nonEmpty failures
95− where
96− mURI :: Maybe Text
97− mFragment :: Maybe Text
98− (mURI, mFragment) = resolveReference (_currentBaseURI scope) reference
99−
100−getDocument
101− :: forall schema. (Text -> Maybe schema)
102− -> (BaseURI -> schema -> BaseURI)
103− -> Scope schema
104− -> Maybe Text
105− -> Text
106− -> Either Text (Scope schema, schema)
107− -- ^ 'Left' is the URI of the document we failed to resolve.
108−getDocument getRef updateScope scope mURI reference =
109− case mURI <* fst (resolveReference (BaseURI Nothing) reference) of
110− Nothing -> Right topOfThisDoc
111− Just uri ->
112− case getRef uri of
113− Nothing -> Left uri
114− Just s -> Right ( Scope s mURI (updateScope (BaseURI mURI) s)
115− , s
116− )
117− where
118− topOfThisDoc :: (Scope schema, schema)
119− topOfThisDoc =
120− ( scope { _currentBaseURI =
121− updateScope (BaseURI (_documentURI scope))
122− (_topLevelDocument scope)
123− }
124− , _topLevelDocument scope
125− )
126−
127−data JSONPointerError
128− = URLDecodingError UnicodeException
129− -- ^ Aspirationally internal.
130− | FormatError JP.FormatError
131− | ResolutionError JP.ResolutionError
132− | SubschemaDecodingError Text
133− -- ^ Aspirationally internal.
134− deriving (Eq, Show)
135−
136−resolveFragment
137− :: (FromJSON schema, ToJSON schema)
138− => (BaseURI -> schema -> BaseURI)
139− -> Scope schema
140− -> Text
141− -> Either JSONPointerError (Scope schema, schema)
142−resolveFragment updateScope scope fragment = do
143− urlDecoded <- first URLDecodingError
144− . decodeUtf8'
145− . urlDecode True
146− . encodeUtf8
147− $ fragment
148− JP.Pointer tokens <- first FormatError (JP.unescape urlDecoded)
149− let acc = (toJSON (_topLevelDocument scope), _currentBaseURI scope)
150− (schemaVal, base) <- foldM go acc tokens
151− schema <- first SubschemaDecodingError (fromJSONEither schemaVal)
152− pure (scope { _currentBaseURI = base }, schema)
153− where
154− -- We have to step through the document JSON Pointer token
155− -- by JSON Pointer token so that we can update the scope
156− -- based on each @"id"@ we encounter.
157− --
158− -- TODO: Do we need specialized code to skip @"id"@s such
159− -- as property keys that aren't meant to change scope?
160− -- Perhaps this should be added to the language agnostic
161− -- test suite as well.
162− --
163− -- In the meantime 'newBaseURIFromFragment' drops all keys
164− -- from JSON objects except "id", which at least prevents
165− -- SubschemaDecodingError in a situation where one of the
166− -- values we step through isn't a valid schema.
167− go :: (Value, BaseURI)
168− -> JP.Token
169− -> Either JSONPointerError (Value, BaseURI)
170− go (lastVal, baseURI) tok = do
171− v <- first ResolutionError (JP.resolveToken tok lastVal)
172− newBase <- newBaseURIFromFragment updateScope baseURI v
173− Right (v, newBase)
174−
175−-- | Update the 'BaseURI' (the store of the current "id" value)
176−-- after resolving one token of a JSON Pointer and stepping into
177−-- a new 'Value'.
178−newBaseURIFromFragment
179− :: FromJSON schema
180− => (BaseURI -> schema -> BaseURI)
181− -> BaseURI
182− -> Value
183− -> Either JSONPointerError BaseURI
184−newBaseURIFromFragment updateScope baseURI v =
185− case v of
186− Object hm -> do
187− let hmWithOnlyId = case HM.lookup idKey hm of
188− Nothing -> mempty
189− Just idVal -> HM.singleton idKey idVal
190− schema <- first SubschemaDecodingError (fromJSONEither (Object hmWithOnlyId))
191− Right (updateScope baseURI schema)
192− _ -> Right baseURI
193− where
194− idKey :: Text
195− idKey = "id"
196−
197−--------------------------------------------------
198−-- * enum
199−--------------------------------------------------
200−
201−-- | From the spec:
202−-- <http://json-schema.org/latest/json-schema-validation.html#anchor76>
203−--
204−-- > The value of this keyword MUST be an array.
205−-- > This array MUST have at least one element.
206−-- > Elements in the array MUST be unique.
207−--
208−-- NOTE: We don't enforce the uniqueness constraint in the haskell code,
209−-- but we do in the 'FromJSON' instance.
210−newtype EnumValidator
211− = EnumValidator { _unEnumValidator :: NonEmpty Value }
212− -- Given a choice, we'd prefer to enforce uniqueness through the type
213− -- system over having at least one element. To use a 'Set' though we'd
214− -- have to use 'OrdValue' here (there's no 'Ord' instance for plain Values)
215− -- and we'd rather not make users mess with 'OrdValue'.
216− deriving (Eq, Show)
217−
218−instance FromJSON EnumValidator where
219− parseJSON = withObject "EnumValidator" $ \o ->
220− EnumValidator <$> o .: "enum"
221−
222−instance Arbitrary EnumValidator where
223− arbitrary = do
224− xs <- (fmap.fmap) UT._unArbitraryValue arbitrary
225− case NE.nonEmpty (toUnique xs) of
226− Nothing -> EnumValidator . pure . UT._unArbitraryValue <$> arbitrary
227− Just ne -> pure (EnumValidator ne)
228− where
229− toUnique :: [Value] -> [Value]
230− toUnique = fmap UT._unOrdValue
231− . Set.toList
232− . Set.fromList
233− . fmap UT.OrdValue
234−
235−data EnumInvalid
236− = EnumInvalid EnumValidator Value
237− deriving (Eq, Show)
238−
239−enumVal :: EnumValidator -> Value -> Maybe EnumInvalid
240−enumVal a@(EnumValidator vs) x
241− | not (UT.allUniqueValues vs) = Nothing
242− | x `elem` vs = Nothing
243− | otherwise = Just $ EnumInvalid a x
244−
245−--------------------------------------------------
246−-- * type
247−--------------------------------------------------
248−
249−-- | This is separate from 'TypeValidator' so that 'TypeValidator' can
250−-- be used to write 'JSONSchema.Draft4.Schema.Schema' without
251−-- messing up the 'FromJSON' instance of that data type.
252−newtype TypeContext
253− = TypeContext { _unTypeContext :: TypeValidator }
254− deriving (Eq, Show)
255−
256−instance FromJSON TypeContext where
257− parseJSON = withObject "TypeContext" $ \o ->
258− TypeContext <$> o .: "type"
259−
260−data TypeValidator
261− = TypeValidatorString SchemaType
262− | TypeValidatorArray (Set SchemaType)
263− deriving (Eq, Show)
264−
265−instance Semigroup TypeValidator where
266− (<>) x y
267− | isEmpty x = x
268− | isEmpty y = y
269− | x == y = x
270− | otherwise = TypeValidatorArray (setFromTypeValidator x
271− `Set.union`
272− setFromTypeValidator y)
273− where
274− isEmpty :: TypeValidator -> Bool
275− isEmpty (TypeValidatorString _) = False
276− isEmpty (TypeValidatorArray ts) = Set.null ts
277−
278− stimes = stimesIdempotent
279−
280−instance FromJSON TypeValidator where
281− parseJSON v = fmap TypeValidatorString (parseJSON v)
282− <|> fmap TypeValidatorArray (parseJSON v)
283−
284−instance ToJSON TypeValidator where
285− toJSON (TypeValidatorString t) = toJSON t
286− toJSON (TypeValidatorArray ts) = toJSON ts
287−
288−instance Arbitrary TypeValidator where
289− arbitrary = oneof [ TypeValidatorString <$> arbitrary
290− , TypeValidatorArray <$> arbitrary
291− ]
292−
293−data SchemaType
294− = SchemaObject
295− | SchemaArray
296− | SchemaString
297− | SchemaNumber
298− | SchemaInteger
299− | SchemaBoolean
300− | SchemaNull
301− deriving (Eq, Ord, Show, Bounded, Enum, Generic)
302−
303−instance FromJSON SchemaType where
304− parseJSON = genericParseJSON
305− defaultOptions
306− { constructorTagModifier = fmap toLower . drop 6 }
307−
308−instance ToJSON SchemaType where
309− toJSON = genericToJSON
310− defaultOptions
311− { constructorTagModifier = fmap toLower . drop 6 }
312−
313−instance Arbitrary SchemaType where
314− arbitrary = arbitraryBoundedEnum
315−
316−data TypeValidatorInvalid
317− = TypeValidatorInvalid TypeValidator Value
318− deriving (Eq, Show)
319−
320−typeVal :: TypeContext -> Value -> Maybe TypeValidatorInvalid
321−typeVal (TypeContext tv) x
322− | Set.null matches = Just (TypeValidatorInvalid tv x)
323− | otherwise = Nothing
324− where
325− -- There can be more than one match because a 'Value' can be both a
326− -- @"number"@ and an @"integer"@.
327− matches :: Set SchemaType
328− matches = Set.intersection okTypes (setFromTypeValidator tv)
329−
330− okTypes :: Set SchemaType
331− okTypes =
332− case x of
333− Null -> Set.singleton SchemaNull
334− (Array _) -> Set.singleton SchemaArray
335− (Bool _) -> Set.singleton SchemaBoolean
336− (Object _) -> Set.singleton SchemaObject
337− (String _) -> Set.singleton SchemaString
338− (Number y) ->
339− if SCI.isInteger y
340− then Set.fromList [SchemaNumber, SchemaInteger]
341− else Set.singleton SchemaNumber
342−
343−-- | Internal.
344−setFromTypeValidator :: TypeValidator -> Set SchemaType
345−setFromTypeValidator (TypeValidatorString t) = Set.singleton t
346−setFromTypeValidator (TypeValidatorArray ts) = ts
347−
348−--------------------------------------------------
349−-- * allOf
350−--------------------------------------------------
351−
352−newtype AllOf schema
353− = AllOf { _unAllOf :: NonEmpty schema }
354− deriving (Eq, Show)
355−
356−instance FromJSON schema => FromJSON (AllOf schema) where
357− parseJSON = withObject "AllOf" $ \o ->
358− AllOf <$> o .: "allOf"
359−
360−newtype AllOfInvalid err
361− = AllOfInvalid (NonEmpty (JP.Index, NonEmpty err))
362− deriving (Eq, Show)
363−
364−allOfVal
365− :: forall err schema.
366− (schema -> Value -> [err])
367− -> AllOf schema
368− -> Value
369− -> Maybe (AllOfInvalid err)
370−allOfVal f (AllOf subSchemas) x = AllOfInvalid <$> NE.nonEmpty failures
371− where
372− perhapsFailures :: [(JP.Index, [err])]
373− perhapsFailures = zip (JP.Index <$> [0..])
374− (flip f x <$> NE.toList subSchemas)
375−
376− failures :: [(JP.Index, NonEmpty err)]
377− failures = mapMaybe (traverse NE.nonEmpty) perhapsFailures
378−
379−--------------------------------------------------
380−-- * anyOf
381−--------------------------------------------------
382−
383−newtype AnyOf schema
384− = AnyOf { _unAnyOf :: NonEmpty schema }
385− deriving (Eq, Show)
386−
387−instance FromJSON schema => FromJSON (AnyOf schema) where
388− parseJSON = withObject "AnyOf" $ \o ->
389− AnyOf <$> o .: "anyOf"
390−
391−newtype AnyOfInvalid err
392− = AnyOfInvalid (NonEmpty (JP.Index, NonEmpty err))
393− deriving (Eq, Show)
394−
395−anyOfVal
396− :: forall err schema.
397− (schema -> Value -> [err])
398− -> AnyOf schema
399− -> Value
400− -> Maybe (AnyOfInvalid err)
401−anyOfVal f (AnyOf subSchemas) x
402− | any (null . snd) perhapsFailures = Nothing
403− | otherwise = AnyOfInvalid <$> NE.nonEmpty failures
404− where
405− perhapsFailures :: [(JP.Index, [err])]
406− perhapsFailures = zip (JP.Index <$> [0..])
407− (flip f x <$> NE.toList subSchemas)
408−
409− failures :: [(JP.Index, NonEmpty err)]
410− failures = mapMaybe (traverse NE.nonEmpty) perhapsFailures
411−
412−--------------------------------------------------
413−-- * oneOf
414−--------------------------------------------------
415−
416−newtype OneOf schema
417− = OneOf { _unOneOf :: NonEmpty schema }
418− deriving (Eq, Show)
419−
420−instance FromJSON schema => FromJSON (OneOf schema) where
421− parseJSON = withObject "OneOf" $ \o ->
422− OneOf <$> o .: "oneOf"
423−
424−data OneOfInvalid err
425− = TooManySuccesses (NonEmpty (JP.Index, Value)) Value
426− -- ^ The NonEmpty lists contains tuples whose contents
427− -- are the index of a schema that validated the data
428− -- and the contents of that schema.
429− | NoSuccesses (NonEmpty (JP.Index, NonEmpty err)) Value
430− -- ^ The NonEmpty lists contains tuples whose contents
431− -- are the index of a schema that failed to validate the data
432− -- and the failures it produced.
433− deriving (Eq, Show)
434−
435−oneOfVal
436− :: forall err schema. ToJSON schema
437− => (schema -> Value -> [err])
438− -> OneOf schema
439− -> Value
440− -> Maybe (OneOfInvalid err)
441−oneOfVal f (OneOf (firstSubSchema :| otherSubSchemas)) x =
442− -- Producing the NonEmpty lists needed by the error constructors
443− -- is a little tricky. If we had a partition function like this
444− -- it might help:
445− -- @
446− -- (a -> Either b c) -> NonEmpty a -> Either (NonEmpty b, [c])
447− -- ([b], NonEmpty c)
448− -- @
449− case (firstSuccess, otherSuccesses) of
450− (Right _, Nothing) -> Nothing
451− (Right a, Just successes) -> Just (TooManySuccesses
452− (a NE.<| successes) x)
453− (Left e, Nothing) -> Just (NoSuccesses (e :| otherFailures) x)
454− (Left _, Just (_ :| [])) -> Nothing
455− (Left _, Just successes) -> Just (TooManySuccesses successes x)
456− where
457− firstSuccess :: Either (JP.Index, NonEmpty err) (JP.Index, Value)
458− firstSuccess =
459− case NE.nonEmpty (f firstSubSchema x) of
460− Nothing -> Right (JP.Index 0, toJSON firstSubSchema)
461− Just errs -> Left (JP.Index 0, errs)
462−
463− otherPerhapsFailures :: [(JP.Index, Value, [err])]
464− otherPerhapsFailures =
465− zipWith
466− (\index schema -> (index, toJSON schema, f schema x))
467− (JP.Index <$> [0..])
468− otherSubSchemas
469−
470− otherSuccesses :: Maybe (NonEmpty (JP.Index, Value))
471− otherSuccesses = NE.nonEmpty
472− $ mapMaybe (\(index,val,errs) ->
473− case errs of
474− [] -> Just (index,val)
475− _ -> Nothing
476− ) otherPerhapsFailures
477−
478− otherFailures :: [(JP.Index, NonEmpty err)]
479− otherFailures = mapMaybe (traverse NE.nonEmpty . mid) otherPerhapsFailures
480−
481− mid :: (a,b,c) -> (a,c)
482− mid (a,_,c) = (a,c)
483−
484−--------------------------------------------------
485−-- * not
486−--------------------------------------------------
487−
488−newtype NotValidator schema
489− = NotValidator { _unNotValidator :: schema }
490− deriving (Eq, Show)
491−
492−instance FromJSON schema => FromJSON (NotValidator schema) where
493− parseJSON = withObject "NotValidator" $ \o ->
494− NotValidator <$> o .: "not"
495−
496−data NotValidatorInvalid
497− = NotValidatorInvalid Value Value
498− deriving (Eq, Show)
499−
500−notVal
501− :: ToJSON schema =>
502− (schema -> Value -> [err])
503− -> NotValidator schema
504− -> Value
505− -> Maybe NotValidatorInvalid
506−notVal f (NotValidator schema) x =
507− case f schema x of
508− [] -> Just (NotValidatorInvalid (toJSON schema) x)
509− _ -> Nothing
src/JSONSchema/Validator/Draft4/Array.hsdeleted−225
@@ -1,225 +0,0 @@
1−module JSONSchema.Validator.Draft4.Array where
2−
3−import Import
4−
5−import qualified Data.List.NonEmpty as NE
6−import qualified Data.Vector as V
7−import qualified JSONPointer as JP
8−
9−import JSONSchema.Validator.Utils (allUniqueValues)
10−
11−--------------------------------------------------
12−-- * maxItems
13−--------------------------------------------------
14−
15−newtype MaxItems
16− = MaxItems { _unMaxItems :: Int }
17− deriving (Eq, Show)
18−
19−instance FromJSON MaxItems where
20− parseJSON = withObject "MaxItems" $ \o ->
21− MaxItems <$> o .: "maxItems"
22−
23−data MaxItemsInvalid
24− = MaxItemsInvalid MaxItems (Vector Value)
25− deriving (Eq, Show)
26−
27−-- | The spec requires @"maxItems"@ to be non-negative.
28−maxItemsVal :: MaxItems -> Vector Value -> Maybe MaxItemsInvalid
29−maxItemsVal a@(MaxItems n) xs
30− | n < 0 = Nothing
31− | V.length xs > n = Just (MaxItemsInvalid a xs)
32− | otherwise = Nothing
33−
34−--------------------------------------------------
35−-- * minItems
36−--------------------------------------------------
37−
38−newtype MinItems
39− = MinItems { _unMinItems :: Int }
40− deriving (Eq, Show)
41−
42−instance FromJSON MinItems where
43− parseJSON = withObject "MinItems" $ \o ->
44− MinItems <$> o .: "minItems"
45−
46−data MinItemsInvalid
47− = MinItemsInvalid MinItems (Vector Value)
48− deriving (Eq, Show)
49−
50−-- | The spec requires @"minItems"@ to be non-negative.
51−minItemsVal :: MinItems -> Vector Value -> Maybe MinItemsInvalid
52−minItemsVal a@(MinItems n) xs
53− | n < 0 = Nothing
54− | V.length xs < n = Just (MinItemsInvalid a xs)
55− | otherwise = Nothing
56−
57−--------------------------------------------------
58−-- * uniqueItems
59−--------------------------------------------------
60−
61−newtype UniqueItems
62− = UniqueItems { _unUniqueItems :: Bool }
63− deriving (Eq, Show)
64−
65−instance FromJSON UniqueItems where
66− parseJSON = withObject "UniqueItems" $ \o ->
67− UniqueItems <$> o .: "uniqueItems"
68−
69−newtype UniqueItemsInvalid
70− = UniqueItemsInvalid (Vector Value)
71− deriving (Eq, Show)
72−
73−uniqueItemsVal :: UniqueItems -> Vector Value -> Maybe UniqueItemsInvalid
74−uniqueItemsVal (UniqueItems True) xs
75− | allUniqueValues xs = Nothing
76− | otherwise = Just (UniqueItemsInvalid xs)
77−uniqueItemsVal (UniqueItems False) _ = Nothing
78−
79−--------------------------------------------------
80−-- * items
81−--------------------------------------------------
82−
83−data ItemsRelated schema = ItemsRelated
84− { _irItems :: Maybe (Items schema)
85− , _irAdditional :: Maybe (AdditionalItems schema)
86− } deriving (Eq, Show)
87−
88−instance FromJSON schema => FromJSON (ItemsRelated schema) where
89− parseJSON = withObject "ItemsRelated" $ \o -> ItemsRelated
90− <$> o .:! "items"
91− <*> o .:! "additionalItems"
92−
93−emptyItems :: ItemsRelated schema
94−emptyItems = ItemsRelated
95− { _irItems = Nothing
96− , _irAdditional = Nothing
97− }
98−
99−data Items schema
100− = ItemsObject schema
101− | ItemsArray [schema]
102− deriving (Eq, Show)
103−
104−instance FromJSON schema => FromJSON (Items schema) where
105− parseJSON v = fmap ItemsObject (parseJSON v)
106− <|> fmap ItemsArray (parseJSON v)
107−
108−instance ToJSON schema => ToJSON (Items schema) where
109− toJSON (ItemsObject hm) = toJSON hm
110− toJSON (ItemsArray schemas) = toJSON schemas
111−
112−instance Arbitrary schema => Arbitrary (Items schema) where
113− arbitrary = oneof [ ItemsObject <$> arbitrary
114− , ItemsArray <$> arbitrary
115− ]
116−
117−data ItemsRelatedInvalid err
118− = IRInvalidItems (ItemsInvalid err)
119− | IRInvalidAdditional (AdditionalItemsInvalid err)
120− deriving (Eq, Show)
121−
122−data ItemsInvalid err
123− = ItemsObjectInvalid (NonEmpty (JP.Index, NonEmpty err))
124− | ItemsArrayInvalid (NonEmpty (JP.Index, NonEmpty err))
125− deriving (Eq, Show)
126−
127−-- | @"additionalItems"@ only matters if @"items"@ exists
128−-- and is a JSON Array.
129−itemsRelatedVal
130− :: forall err schema.
131− (schema -> Value -> [err])
132− -> ItemsRelated schema
133− -> Vector Value
134− -> [ItemsRelatedInvalid err] -- NOTE: 'Data.These' would help here.
135−itemsRelatedVal f a xs =
136− let (itemsFailure, remaining) = case _irItems a of
137− Nothing -> (Nothing, mempty)
138− Just b -> itemsVal f b xs
139− additionalFailure = (\b -> additionalItemsVal f b remaining)
140− =<< _irAdditional a
141− in catMaybes [ IRInvalidItems <$> itemsFailure
142− , IRInvalidAdditional <$> additionalFailure
143− ]
144−
145−-- | Internal.
146−--
147−-- This is because 'itemsRelated' handles @"items"@ validation.
148−itemsVal
149− :: forall err schema.
150− (schema -> Value -> [err])
151− -> Items schema
152− -> Vector Value
153− -> (Maybe (ItemsInvalid err), [(JP.Index, Value)])
154− -- ^ The second item in the tuple is the elements of the original
155− -- JSON Array still remaining to be checked by @"additionalItems"@.
156−itemsVal f a xs =
157− case a of
158− ItemsObject subSchema ->
159− case NE.nonEmpty (mapMaybe (validateElem subSchema) indexed) of
160− Nothing -> (Nothing, mempty)
161− Just errs -> (Just (ItemsObjectInvalid errs), mempty)
162− ItemsArray subSchemas ->
163− let remaining = drop (length subSchemas) indexed
164− res = catMaybes (zipWith validateElem subSchemas indexed)
165− in case NE.nonEmpty res of
166− Nothing -> (Nothing, remaining)
167− Just errs -> (Just (ItemsArrayInvalid errs), remaining)
168− where
169− indexed :: [(JP.Index, Value)]
170− indexed = zip (JP.Index <$> [0..]) (V.toList xs)
171−
172− validateElem
173− :: schema
174− -> (JP.Index, Value)
175− -> Maybe (JP.Index, NonEmpty err)
176− validateElem schema (index,x) =
177− (\v -> (index, v)) <$> NE.nonEmpty (f schema x)
178−
179−--------------------------------------------------
180−-- * additionalItems
181−--------------------------------------------------
182−
183−data AdditionalItems schema
184− = AdditionalBool Bool
185− | AdditionalObject schema
186− deriving (Eq, Show)
187−
188−instance FromJSON schema => FromJSON (AdditionalItems schema) where
189− parseJSON v = fmap AdditionalBool (parseJSON v)
190− <|> fmap AdditionalObject (parseJSON v)
191−
192−instance ToJSON schema => ToJSON (AdditionalItems schema) where
193− toJSON (AdditionalBool b) = toJSON b
194− toJSON (AdditionalObject hm) = toJSON hm
195−
196−instance Arbitrary schema => Arbitrary (AdditionalItems schema) where
197− arbitrary = oneof [ AdditionalBool <$> arbitrary
198− , AdditionalObject <$> arbitrary
199− ]
200−
201−data AdditionalItemsInvalid err
202− = AdditionalItemsBoolInvalid (NonEmpty (JP.Index, Value))
203− | AdditionalItemsObjectInvalid (NonEmpty (JP.Index, NonEmpty err))
204− deriving (Eq, Show)
205−
206−-- | Internal.
207−--
208−-- This is because 'itemsRelated' handles @"additionalItems"@ validation.
209−additionalItemsVal
210− :: forall err schema.
211− (schema -> Value -> [err])
212− -> AdditionalItems schema
213− -> [(JP.Index, Value)]
214− -- ^ The elements remaining to validate after the ones covered by
215− -- @"items"@ have been removed.
216− -> Maybe (AdditionalItemsInvalid err)
217−additionalItemsVal _ (AdditionalBool True) _ = Nothing
218−additionalItemsVal _ (AdditionalBool False) xs =
219− AdditionalItemsBoolInvalid <$> NE.nonEmpty xs
220−additionalItemsVal f (AdditionalObject subSchema) xs =
221− let res = mapMaybe
222− (\(index,x) -> (\v -> (index, v))
223− <$> NE.nonEmpty (f subSchema x))
224− xs
225− in AdditionalItemsObjectInvalid <$> NE.nonEmpty res
src/JSONSchema/Validator/Draft4/Number.hsdeleted−83
@@ -1,83 +0,0 @@
1−module JSONSchema.Validator.Draft4.Number where
2−
3−import Import
4−
5−import Data.Fixed (mod')
6−import Data.Scientific (Scientific)
7−
8−--------------------------------------------------
9−-- * multipleOf
10−--------------------------------------------------
11−
12−newtype MultipleOf
13− = MultipleOf { _unMultipleOf :: Scientific }
14− deriving (Eq, Show)
15−
16−instance FromJSON MultipleOf where
17− parseJSON = withObject "MultipleOf" $ \o ->
18− MultipleOf <$> o .: "multipleOf"
19−
20−data MultipleOfInvalid
21− = MultipleOfInvalid MultipleOf Scientific
22− deriving (Eq, Show)
23−
24−-- | The spec requires @"multipleOf"@ to be positive.
25−multipleOfVal :: MultipleOf -> Scientific -> Maybe MultipleOfInvalid
26−multipleOfVal a@(MultipleOf n) x
27− | n <= 0 = Nothing
28− | x `mod'` n /= 0 = Just (MultipleOfInvalid a x)
29− | otherwise = Nothing
30−
31−--------------------------------------------------
32−-- * maximum
33−--------------------------------------------------
34−
35−data Maximum = Maximum
36− { _maximumExclusive :: Bool
37− , _maximumValue :: Scientific
38− } deriving (Eq, Show)
39−
40−instance FromJSON Maximum where
41− parseJSON = withObject "Maximum" $ \o -> Maximum
42− <$> o .:! "exclusiveMaximum" .!= False
43− <*> o .: "maximum"
44−
45−data MaximumInvalid
46− = MaximumInvalid Maximum Scientific
47− deriving (Eq, Show)
48−
49−maximumVal
50− :: Maximum
51− -> Scientific
52− -> Maybe MaximumInvalid
53−maximumVal a@(Maximum exclusive n) x
54− | exclusive && x >= n = Just (MaximumInvalid a x)
55− | x > n = Just (MaximumInvalid a x)
56− | otherwise = Nothing
57−
58−--------------------------------------------------
59−-- * minimum
60−--------------------------------------------------
61−
62−data Minimum = Minimum
63− { _minimumExclusive :: Bool
64− , _minimumValue :: Scientific
65− } deriving (Eq, Show)
66−
67−instance FromJSON Minimum where
68− parseJSON = withObject "Minimum" $ \o -> Minimum
69− <$> o .:! "exclusiveMinimum" .!= False
70− <*> o .: "minimum"
71−
72−data MinimumInvalid
73− = MinimumInvalid Minimum Scientific
74− deriving (Eq, Show)
75−
76−minimumVal
77− :: Minimum
78− -> Scientific
79− -> Maybe MinimumInvalid
80−minimumVal a@(Minimum exclusive n) x
81− | exclusive && x <= n = Just (MinimumInvalid a x)
82− | x < n = Just (MinimumInvalid a x)
83− | otherwise = Nothing
src/JSONSchema/Validator/Draft4/Object.hsdeleted−182
@@ -1,182 +0,0 @@
1−module JSONSchema.Validator.Draft4.Object
2− ( module JSONSchema.Validator.Draft4.Object
3− , module JSONSchema.Validator.Draft4.Object.Properties
4− ) where
5−
6−import Import
7−
8−import qualified Data.HashMap.Strict as HM
9−import qualified Data.List.NonEmpty as NE
10−import Data.Set (Set)
11−import qualified Data.Set as Set
12−import qualified Data.Text as T
13−
14−import JSONSchema.Validator.Draft4.Object.Properties
15−import JSONSchema.Validator.Utils
16−
17−--------------------------------------------------
18−-- * maxProperties
19−--------------------------------------------------
20−
21−newtype MaxProperties
22− = MaxProperties { _unMaxProperties :: Int }
23− deriving (Eq, Show)
24−
25−instance FromJSON MaxProperties where
26− parseJSON = withObject "MaxProperties" $ \o ->
27− MaxProperties <$> o .: "maxProperties"
28−
29−data MaxPropertiesInvalid
30− = MaxPropertiesInvalid MaxProperties (HashMap Text Value)
31− deriving (Eq, Show)
32−
33−-- | The spec requires @"maxProperties"@ to be non-negative.
34−maxPropertiesVal
35− :: MaxProperties
36− -> HashMap Text Value
37− -> Maybe MaxPropertiesInvalid
38−maxPropertiesVal a@(MaxProperties n) x
39− | n < 0 = Nothing
40− | HM.size x > n = Just (MaxPropertiesInvalid a x)
41− | otherwise = Nothing
42−
43−--------------------------------------------------
44−-- * minProperties
45−--------------------------------------------------
46−
47−newtype MinProperties
48− = MinProperties { _unMinProperties :: Int }
49− deriving (Eq, Show)
50−
51−instance FromJSON MinProperties where
52− parseJSON = withObject "MinProperties" $ \o ->
53− MinProperties <$> o .: "minProperties"
54−
55−data MinPropertiesInvalid
56− = MinPropertiesInvalid MinProperties (HashMap Text Value)
57− deriving (Eq, Show)
58−
59−-- | The spec requires @"minProperties"@ to be non-negative.
60−minPropertiesVal
61− :: MinProperties
62− -> HashMap Text Value
63− -> Maybe MinPropertiesInvalid
64−minPropertiesVal a@(MinProperties n) x
65− | n < 0 = Nothing
66− | HM.size x < n = Just (MinPropertiesInvalid a x)
67− | otherwise = Nothing
68−
69−--------------------------------------------------
70−-- * required
71−--------------------------------------------------
72−
73−-- | From the spec:
74−--
75−-- > The value of this keyword MUST be an array.
76−-- > This array MUST have at least one element.
77−-- > Elements of this array MUST be strings, and MUST be unique.
78−newtype Required
79− = Required { _unRequired :: Set Text }
80− deriving (Eq, Show)
81−
82−instance FromJSON Required where
83− parseJSON = withObject "Required" $ \o ->
84− Required <$> o .: "required"
85−
86−instance Arbitrary Required where
87− arbitrary = do
88− x <- arbitraryText -- Guarantee at least one element.
89− xs <- (fmap.fmap) T.pack arbitrary
90− pure . Required . Set.fromList $ x:xs
91−
92−data RequiredInvalid
93− = RequiredInvalid Required (Set Text) (HashMap Text Value)
94− deriving (Eq, Show)
95−
96−requiredVal :: Required -> HashMap Text Value -> Maybe RequiredInvalid
97−requiredVal r@(Required ts) x
98− | Set.null ts = Nothing
99− | Set.null leftovers = Nothing
100− | otherwise = Just (RequiredInvalid r leftovers x)
101− where
102− leftovers :: Set Text
103− leftovers =
104− Set.difference -- Items of the first set not in the second.
105− ts
106− (Set.fromList (HM.keys x))
107−
108−--------------------------------------------------
109−-- * dependencies
110−--------------------------------------------------
111−
112−newtype DependenciesValidator schema
113− = DependenciesValidator
114− { _unDependenciesValidator :: HashMap Text (Dependency schema) }
115− deriving (Eq, Show)
116−
117−instance FromJSON schema => FromJSON (DependenciesValidator schema) where
118− parseJSON = withObject "DependenciesValidator" $ \o ->
119− DependenciesValidator <$> o .: "dependencies"
120−
121−data Dependency schema
122− = SchemaDependency schema
123− | PropertyDependency (Set Text)
124− deriving (Eq, Show)
125−
126−instance FromJSON schema => FromJSON (Dependency schema) where
127− parseJSON v = fmap SchemaDependency (parseJSON v)
128− <|> fmap PropertyDependency (parseJSON v)
129−
130−instance ToJSON schema => ToJSON (Dependency schema) where
131− toJSON (SchemaDependency schema) = toJSON schema
132− toJSON (PropertyDependency ts) = toJSON ts
133−
134−instance Arbitrary schema => Arbitrary (Dependency schema) where
135− arbitrary = oneof [ SchemaDependency <$> arbitrary
136− , PropertyDependency <$> arbitrarySetOfText
137− ]
138−
139−data DependencyMemberInvalid err
140− = SchemaDepInvalid (NonEmpty err)
141− | PropertyDepInvalid (Set Text) (HashMap Text Value)
142− deriving (Eq, Show)
143−
144−newtype DependenciesInvalid err
145− = DependenciesInvalid (HashMap Text (DependencyMemberInvalid err))
146− deriving (Eq, Show)
147−
148−-- | From the spec:
149−-- <http://json-schema.org/latest/json-schema-validation.html#anchor70>
150−--
151−-- > This keyword's value MUST be an object.
152−-- > Each value of this object MUST be either an object or an array.
153−-- >
154−-- > If the value is an object, it MUST be a valid JSON Schema.
155−-- > This is called a schema dependency.
156−-- >
157−-- > If the value is an array, it MUST have at least one element.
158−-- > Each element MUST be a string, and elements in the array MUST be unique.
159−-- > This is called a property dependency.
160−dependenciesVal
161− :: forall err schema.
162− (schema -> Value -> [err])
163− -> DependenciesValidator schema
164− -> HashMap Text Value
165− -> Maybe (DependenciesInvalid err)
166−dependenciesVal f (DependenciesValidator hm) x =
167− let res = HM.mapMaybeWithKey g hm
168− in if HM.null res
169− then Nothing
170− else Just (DependenciesInvalid res)
171− where
172− g :: Text -> Dependency schema -> Maybe (DependencyMemberInvalid err)
173− g k (SchemaDependency schema)
174− | HM.member k x = SchemaDepInvalid
175− <$> NE.nonEmpty (f schema (Object x))
176− | otherwise = Nothing
177− g k (PropertyDependency ts)
178− | HM.member k x && not allPresent = Just (PropertyDepInvalid ts x)
179− | otherwise = Nothing
180− where
181− allPresent :: Bool
182− allPresent = all (`HM.member` x) ts
src/JSONSchema/Validator/Draft4/Object/Properties.hsdeleted−209
@@ -1,209 +0,0 @@
1−module JSONSchema.Validator.Draft4.Object.Properties where
2−
3−import Import
4−
5−import qualified Data.Hashable as HA
6−import qualified Data.HashMap.Strict as HM
7−import qualified Data.List.NonEmpty as NE
8−import Data.Text.Encoding (encodeUtf8)
9−import qualified JSONPointer as JP
10−import qualified Text.Regex.PCRE.Heavy as RE
11−
12−data PropertiesRelated schema = PropertiesRelated
13− { _propProperties :: Maybe (HashMap Text schema)
14− -- ^ 'Maybe' is used to distinguish whether the key is present or not.
15− , _propPattern :: Maybe (HashMap Text schema)
16− , _propAdditional :: Maybe (AdditionalProperties schema)
17− } deriving (Eq, Show)
18−
19−instance FromJSON schema => FromJSON (PropertiesRelated schema) where
20− parseJSON = withObject "PropertiesRelated" $ \o -> PropertiesRelated
21− <$> o .:! "properties"
22− <*> o .:! "patternProperties"
23− <*> o .:! "additionalProperties"
24−
25−emptyProperties :: PropertiesRelated schema
26−emptyProperties = PropertiesRelated
27− { _propProperties = Nothing
28− , _propPattern = Nothing
29− , _propAdditional = Nothing
30− }
31−
32−data AdditionalProperties schema
33− = AdditionalPropertiesBool Bool
34− | AdditionalPropertiesObject schema
35− deriving (Eq, Show)
36−
37−instance FromJSON schema => FromJSON (AdditionalProperties schema) where
38− parseJSON v = fmap AdditionalPropertiesBool (parseJSON v)
39− <|> fmap AdditionalPropertiesObject (parseJSON v)
40−
41−instance ToJSON schema => ToJSON (AdditionalProperties schema) where
42− toJSON (AdditionalPropertiesBool b) = toJSON b
43− toJSON (AdditionalPropertiesObject hm) = toJSON hm
44−
45−instance Arbitrary schema => Arbitrary (AdditionalProperties schema) where
46− arbitrary = oneof [ AdditionalPropertiesBool <$> arbitrary
47− , AdditionalPropertiesObject <$> arbitrary
48− ]
49−
50−-- | A glorified @type@ alias.
51−newtype Regex
52− = Regex { _unRegex :: Text }
53− deriving (Eq, Show, Generic)
54−
55−instance HA.Hashable Regex
56−
57−-- NOTE: We'd like to enforce that at least one error exists here.
58−data PropertiesRelatedInvalid err = PropertiesRelatedInvalid
59− { _prInvalidProperties :: HashMap Text [err]
60− , _prInvalidPattern :: HashMap (Regex, JP.Key) [err]
61− , _prInvalidAdditional :: Maybe (APInvalid err)
62− } deriving (Eq, Show)
63−
64−data APInvalid err
65− = APBoolInvalid (HashMap Text Value)
66− | APObjectInvalid (HashMap Text (NonEmpty err))
67− deriving (Eq, Show)
68−
69−-- | First @"properties"@ and @"patternProperties"@ are run simultaneously
70−-- on the data, then @"additionalProperties"@ is run on the remainder.
71−propertiesRelatedVal
72− :: forall err schema.
73− (schema -> Value -> [err])
74− -> PropertiesRelated schema
75− -> HashMap Text Value
76− -> Maybe (PropertiesRelatedInvalid err)
77−propertiesRelatedVal f props x
78− | all null (HM.elems propFailures)
79− && all null (HM.elems patFailures)
80− && isNothing addFailures = Nothing
81− | otherwise =
82− Just PropertiesRelatedInvalid
83− { _prInvalidProperties = propFailures
84− , _prInvalidPattern = patFailures
85− , _prInvalidAdditional = addFailures
86− }
87− where
88− propertiesHm :: HashMap Text schema
89− propertiesHm = fromMaybe mempty (_propProperties props)
90−
91− patHm :: HashMap Text schema
92− patHm = fromMaybe mempty (_propPattern props)
93−
94− propAndUnmatched :: (HashMap Text [err], Remaining)
95− propAndUnmatched = ( HM.intersectionWith f propertiesHm x
96− , Remaining (HM.difference x propertiesHm)
97− )
98−
99− (propFailures, propRemaining) = propAndUnmatched
100−
101− patAndUnmatched :: (HashMap (Regex, JP.Key) [err], Remaining)
102− patAndUnmatched = patternAndUnmatched f patHm x
103−
104− (patFailures, patRemaining) = patAndUnmatched
105−
106− finalRemaining :: Remaining
107− finalRemaining = Remaining (HM.intersection (_unRemaining patRemaining)
108− (_unRemaining propRemaining))
109−
110− addFailures :: Maybe (APInvalid err)
111− addFailures = (\addProp -> additionalProperties f addProp finalRemaining)
112− =<< _propAdditional props
113−
114−-- | Internal.
115−newtype Remaining
116− = Remaining { _unRemaining :: HashMap Text Value }
117−
118−-- | Internal.
119−patternAndUnmatched
120− :: forall err schema.
121− (schema -> Value -> [err])
122− -> HashMap Text schema
123− -> HashMap Text Value
124− -> (HashMap (Regex, JP.Key) [err], Remaining)
125−patternAndUnmatched f patPropertiesHm x =
126− (HM.foldlWithKey' runMatches mempty perhapsMatches, remaining)
127− where
128− -- @[(Regex, schema)]@ will have one item per match.
129− perhapsMatches :: HashMap Text ([(Regex, schema)], Value)
130− perhapsMatches =
131− HM.foldlWithKey' (matchingSchemas patPropertiesHm) mempty x
132− where
133− matchingSchemas
134− :: HashMap Text schema
135− -> HashMap Text ([(Regex, schema)], Value)
136− -> Text
137− -> Value
138− -> HashMap Text ([(Regex, schema)], Value)
139− matchingSchemas subSchemas acc k v =
140− HM.insert k
141− (HM.foldlWithKey' (checkKey k) mempty subSchemas, v)
142− acc
143−
144− checkKey
145− :: Text
146− -> [(Regex, schema)]
147− -> Text
148− -> schema
149− -> [(Regex, schema)]
150− checkKey k acc r subSchema =
151− case RE.compileM (encodeUtf8 r) mempty of
152− Left _ -> acc
153− Right re -> if k RE.=~ re
154− then (Regex r, subSchema) : acc
155− else acc
156−
157− runMatches
158− :: HashMap (Regex, JP.Key) [err]
159− -> Text
160− -> ([(Regex, schema)], Value)
161− -> HashMap (Regex, JP.Key) [err]
162− runMatches acc k (matches,v) =
163− foldr runMatch acc matches
164− where
165− runMatch
166− :: (Regex, schema)
167− -> HashMap (Regex, JP.Key) [err]
168− -> HashMap (Regex, JP.Key) [err]
169− runMatch (r,schema) = HM.insert (r, JP.Key k) (f schema v)
170−
171− remaining :: Remaining
172− remaining = Remaining . fmap snd . HM.filter (null . fst) $ perhapsMatches
173−
174−-- Internal.
175−additionalProperties
176− :: forall err schema.
177− (schema -> Value -> [err])
178− -> AdditionalProperties schema
179− -> Remaining
180− -> Maybe (APInvalid err)
181−additionalProperties f a x =
182− case a of
183− AdditionalPropertiesBool b ->
184− APBoolInvalid <$> additionalPropertiesBool b x
185− AdditionalPropertiesObject b ->
186− APObjectInvalid <$> additionalPropertiesObject f b x
187−
188−-- | Internal.
189−additionalPropertiesBool
190− :: Bool
191− -> Remaining
192− -> Maybe (HashMap Text Value)
193−additionalPropertiesBool True _ = Nothing
194−additionalPropertiesBool False (Remaining x)
195− | HM.size x > 0 = Just x
196− | otherwise = Nothing
197−
198−-- | Internal.
199−additionalPropertiesObject
200− :: forall err schema.
201− (schema -> Value -> [err])
202− -> schema
203− -> Remaining
204− -> Maybe (HashMap Text (NonEmpty err))
205−additionalPropertiesObject f schema (Remaining x) =
206− let errs = HM.mapMaybe (NE.nonEmpty . f schema) x
207− in if HM.null errs
208− then Nothing
209− else Just errs
src/JSONSchema/Validator/Draft4/String.hsdeleted−78
@@ -1,78 +0,0 @@
1−module JSONSchema.Validator.Draft4.String where
2−
3−import Import
4−
5−import qualified Data.Text as T
6−import Data.Text.Encoding (encodeUtf8)
7−import qualified Text.Regex.PCRE.Heavy as RE
8−
9−--------------------------------------------------
10−-- * maxLength
11−--------------------------------------------------
12−
13−newtype MaxLength
14− = MaxLength { _unMaxLength :: Int }
15− deriving (Eq, Show)
16−
17−instance FromJSON MaxLength where
18− parseJSON = withObject "MaxLength" $ \o ->
19− MaxLength <$> o .: "maxLength"
20−
21−data MaxLengthInvalid
22− = MaxLengthInvalid MaxLength Text
23− deriving (Eq, Show)
24−
25−-- | The spec requires @"maxLength"@ to be non-negative.
26−maxLengthVal :: MaxLength -> Text -> Maybe MaxLengthInvalid
27−maxLengthVal a@(MaxLength n) x
28− | n <= 0 = Nothing
29− | T.length x > n = Just (MaxLengthInvalid a x)
30− | otherwise = Nothing
31−
32−--------------------------------------------------
33−-- * minLength
34−--------------------------------------------------
35−
36−newtype MinLength
37− = MinLength { _unMinLength :: Int }
38− deriving (Eq, Show)
39−
40−instance FromJSON MinLength where
41− parseJSON = withObject "MinLength" $ \o ->
42− MinLength <$> o .: "minLength"
43−
44−data MinLengthInvalid
45− = MinLengthInvalid MinLength Text
46− deriving (Eq, Show)
47−
48−-- | The spec requires @"minLength"@ to be non-negative.
49−minLengthVal :: MinLength -> Text -> Maybe MinLengthInvalid
50−minLengthVal a@(MinLength n) x
51− | n <= 0 = Nothing
52− | T.length x < n = Just (MinLengthInvalid a x)
53− | otherwise = Nothing
54−
55−--------------------------------------------------
56−-- * pattern
57−--------------------------------------------------
58−
59−newtype PatternValidator
60− = PatternValidator { _unPatternValidator :: Text }
61− deriving (Eq, Show)
62−
63−instance FromJSON PatternValidator where
64− parseJSON = withObject "PatternValidator" $ \o ->
65− PatternValidator <$> o .: "pattern"
66−
67−data PatternInvalid
68− = PatternNotRegex -- TODO: let these pass successfully?
69− | PatternInvalid PatternValidator Text
70− deriving (Eq, Show)
71−
72−patternVal :: PatternValidator -> Text -> Maybe PatternInvalid
73−patternVal a@(PatternValidator t) x =
74− case RE.compileM (encodeUtf8 t) mempty of
75− Left _ -> Just PatternNotRegex
76− Right re -> if x RE.=~ re
77− then Nothing
78− else Just (PatternInvalid a x)
src/JSONSchema/Validator/Reference.hsdeleted−71
@@ -1,71 +0,0 @@
1−-- | JSON Reference is described here:
2−-- <http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03>
3−--
4−-- And is extended for JSON Schema here:
5−-- <http://json-schema.org/latest/json-schema-core.html#anchor26>
6−module JSONSchema.Validator.Reference where
7−
8−import Import
9−
10−import qualified Data.Text as T
11−import System.FilePath (dropFileName, (</>))
12−
13−data Scope schema = Scope
14− { _topLevelDocument :: schema
15− , _documentURI :: Maybe Text
16− , _currentBaseURI :: BaseURI
17− } deriving (Eq, Show)
18−
19−newtype BaseURI
20− = BaseURI { _unBaseURI :: Maybe Text }
21− deriving (Eq, Show)
22−
23−-- | TODO: no `type`s.
24−type URIAndFragment = (Maybe Text, Maybe Text)
25−
26−updateResolutionScope :: BaseURI -> Maybe Text -> BaseURI
27−updateResolutionScope base idKeyword =
28− case idKeyword of
29− Just t -> BaseURI . fst . baseAndFragment $ resolveScopeAgainst base t
30− Nothing -> base
31−
32−resolveReference :: BaseURI -> Text -> URIAndFragment
33−resolveReference base t = baseAndFragment (resolveScopeAgainst base t)
34−
35−--------------------------------------------------
36−-- * Helpers
37−--------------------------------------------------
38−
39−isRemoteReference :: Text -> Bool
40−isRemoteReference = T.isInfixOf "://"
41−
42−baseAndFragment :: Text -> URIAndFragment
43−baseAndFragment = f . T.splitOn "#"
44− where
45− f :: [Text] -> URIAndFragment
46− f [x] = (g x, Nothing)
47− f [x,y] = (g x, g y)
48− f _ = (Nothing, Nothing)
49−
50− g "" = Nothing
51− g x = Just x
52−
53−resolveScopeAgainst :: BaseURI -> Text -> Text
54−resolveScopeAgainst (BaseURI Nothing) t = t
55−resolveScopeAgainst (BaseURI (Just base)) t
56− | isRemoteReference t = t
57− | otherwise = smartAppend
58− where
59− -- There shouldn't be a fragment at the end of a scope URI,
60− -- but just in case a user leaves one in we want to be sure
61− -- to cut it off before appending.
62− smartAppend :: Text
63− smartAppend =
64− case baseAndFragment base of
65− (Just uri,_) ->
66− case T.unpack t of
67− -- We want "/foo" and "#/bar" to combine into
68− -- "/foo#/bar" not "/foo/#/bar".
69− '#':_ -> base <> t
70− _ -> T.pack (dropFileName (T.unpack uri) </> T.unpack t)
71− _ -> t
stack.yamldeleted−2
@@ -1,2 +0,0 @@
1−resolver: lts-10.8
2−pvp-bounds: upper-revision
test/Local.hs+76−52
@@ -1,29 +1,35 @@
1−module Main where
2
1
3−import Protolude
2+module Main where
4
3
4+import Control.Applicative
5+import Control.Monad (unless)
5import Data.Aeson
6import Data.Aeson
7+import Data.Foldable (traverse_)
6−import qualified Data.List.NonEmpty as NE
8+import qualified Data.List.NonEmpty as N
9+import Data.Monoid
7−import qualified System.Timeout as TO
10+import qualified System.Timeout as TO
8−import Test.Hspec
11+import Test.Tasty (TestTree, defaultMain, testGroup)
12+import qualified Test.Tasty.HUnit as HU
9−import Test.QuickCheck (property)
13+import Test.Tasty.QuickCheck (testProperty)
10−
14+
11−import qualified JSONSchema.Draft4 as D4
15+import qualified Data.JsonSchema.Draft4 as D4
16+import Data.JsonSchema.Fetch (ReferencedSchemas(..))
12−import qualified JSONSchema.Types as JT
17+import qualified Data.JsonSchema.Types as JT
13−import qualified Local.Failure
18+import Local.Failure (correctPaths)
14−import qualified Local.Reference
15−import qualified Local.Validation
19+import Local.Validation (fetchFromFilesystem,
20+ generalValidation)
21+import Local.Reference (referenceTests)
16import Shared
22import Shared
17
23
18-- Examples
24-- Examples
19import qualified AlternateSchema
25import qualified AlternateSchema
26+import qualified Full
20import qualified Simple
27import qualified Simple
21−import qualified TwoStep
22
28
23−dir :: FilePath
29+dir :: String
24dir = "JSON-Schema-Test-Suite/tests/draft4"
30dir = "JSON-Schema-Test-Suite/tests/draft4"
25
31
26−supplementDir :: FilePath
32+supplementDir :: String
27supplementDir = "test/supplement"
33supplementDir = "test/supplement"
28
34
29main :: IO ()
35main :: IO ()
@@ -32,67 +38,85 @@ main = do
32 -- Language agnostic tests
38 -- Language agnostic tests
33 ts <- readSchemaTests
39 ts <- readSchemaTests
34 dir
40 dir
35− (\a -> not (isHTTPTest a || skipTest a))
41+ (\a -> not (isHTTPTest a || skipOptional a))
36
42
37 -- Custom supplements to the language agnostic tests
43 -- Custom supplements to the language agnostic tests
38− supplementTs <- readSchemaTests supplementDir (not . isHTTPTest)
44+ supplementTs <- readSchemaTests
39−
45+ supplementDir
40− hspec $ do
46+ (\a -> not (isHTTPTest a || skipOptional a))
47+
48+ defaultMain . testGroup "Tests not requiring an HTTP server" $
49+ [ testGroup
50+ "Check that examples compile and don't throw errors"
41− describe "Examples" exampleTests
51+ exampleTests
52+ , testGroup
53+ "QuickCheck tests"
42− describe "QuickCheck" quickCheckTests
54+ quickCheckTests
43− describe "Failure" Local.Failure.spec
55+ , testGroup
44− describe "JSONSchema.Validator.Reference" Local.Reference.spec
56+ "Report the path to invalid data correctly"
57+ correctPaths
58+ , testGroup
59+ "Test the Reference module"
60+ referenceTests
61+ , testGroup
62+ "Test the referencesViaFilesystem function"
63+ fetchFromFilesystem
64+ , testGroup
45− describe "Supplementary validation tests written in Haskell"
65+ "Supplementary validation tests written in Haskell"
46− Local.Validation.spec
66+ generalValidation
47−
67+ , testGroup
48− describe "Supplementary tests written in JSON (using the record based schema)"
68+ "Supplementary tests written in JSON (using the record based schema)"
49− (traverse_ (toTest (fmap timeout . validate)) supplementTs)
69+ (toTest (fmap timeout . validate) <$> supplementTs)
70+ , testGroup
50− describe "Supplementary tests written in JSON (using the 'Value' based schema)"
71+ "Supplementary tests written in JSON (using the 'Value' based schema)"
51− (traverse_ (toTest (fmap timeout . validateExample)) supplementTs)
72+ (toTest (fmap timeout . validateExample) <$> supplementTs)
52−
73+ , testGroup
53− describe "Language agnostic tests (using the record based schema)"
74+ "Language agnostic local tests (using the record based schema)"
54− (traverse_ (toTest validate) ts)
75+ (toTest validate <$> ts)
76+ , testGroup
55− describe "Language agnostic tests (using the 'Value' based schema)"
77+ "Language agnostic local tests (using the 'Value' based schema)"
56− (traverse_ (toTest validateExample) ts)
78+ (toTest validateExample <$> ts)
79+ ]
57 where
80 where
58− timeout :: Expectation -> Expectation
81+ timeout :: HU.Assertion -> HU.Assertion
59 timeout f = do
82 timeout f = do
60 res <- TO.timeout 3000000 f
83 res <- TO.timeout 3000000 f
61 case res of
84 case res of
62− Nothing -> expectationFailure "timeout expired"
85+ Nothing -> HU.assertFailure "timeout expired"
63 Just a -> pure a
86 Just a -> pure a
64
87
65− validate :: D4.Schema -> SchemaTestCase -> Expectation
88+ validate :: D4.Schema -> SchemaTestCase -> HU.Assertion
66 validate s sc = do
89 validate s sc = do
67 res <- D4.fetchHTTPAndValidate (D4.SchemaWithURI s Nothing) (_scData sc)
90 res <- D4.fetchHTTPAndValidate (D4.SchemaWithURI s Nothing) (_scData sc)
68 let failures = case res of
91 let failures = case res of
69 Right () -> mempty
92 Right () -> mempty
70− Left (D4.HVData a) -> NE.toList (D4._invalidFailures a)
93+ Left (D4.HVData a) -> N.toList a
71− other -> panic ("Local.validate error: "
94+ other -> error ("Local.validate error: "
72 <> show other)
95 <> show other)
96+ traverse_ (checkPointer (_scData sc)) failures
73 assertResult sc failures
97 assertResult sc failures
74
98
75− validateExample :: JT.Schema -> SchemaTestCase -> Expectation
99+ validateExample :: JT.Schema -> SchemaTestCase -> HU.Assertion
76 validateExample s sc = do
100 validateExample s sc = do
77 res <- AlternateSchema.referencesViaHTTP (D4.SchemaWithURI s Nothing)
101 res <- AlternateSchema.referencesViaHTTP (D4.SchemaWithURI s Nothing)
78 case res of
102 case res of
79− Left e -> panic ("Local.validateExample error: " <> show e)
103+ Left e -> error ("Local.validateExample error: " <> show e)
80 Right schemaMap -> do
104 Right schemaMap -> do
81 let failures = AlternateSchema.validate
105 let failures = AlternateSchema.validate
82− schemaMap
106+ (ReferencedSchemas s schemaMap)
83− (D4.SchemaWithURI s Nothing)
107+ Nothing s (_scData sc)
84− (_scData sc)
108+ traverse_ (checkPointer (_scData sc)) failures
85 assertResult sc failures
109 assertResult sc failures
86
110
87−quickCheckTests :: Spec
111+quickCheckTests :: [TestTree]
88quickCheckTests =
112quickCheckTests =
89− it "schemas invert through JSON without change" $ do
113+ [testProperty "Invert schemas through JSON without change" invertSchema]
90− property invertSchema
91 where
114 where
92 invertSchema :: D4.Schema -> Bool
115 invertSchema :: D4.Schema -> Bool
93 invertSchema a = Just a == decode (encode a)
116 invertSchema a = Just a == decode (encode a)
94
117
95−exampleTests :: Spec
118+exampleTests :: [TestTree]
96−exampleTests = do
119+exampleTests =
97− it "Simple.example compiles successfully" Simple.example
120+ [ HU.testCase "Full example" Full.example
98− it "TwoStep.example compiles successfully" TwoStep.example
121+ , HU.testCase "Simple example" Simple.example
122+ ]
test/Local/Failure.hs+37−74
@@ -1,70 +1,39 @@
1+
1module Local.Failure where
2module Local.Failure where
2
3
3−import Protolude
4+import Prelude
4
5
5import Data.Aeson
6import Data.Aeson
6−import qualified Data.Vector as V
7+import qualified Data.Aeson.Pointer as P
8+import Data.Monoid
9+import Test.Tasty (TestTree)
7−import qualified JSONPointer as JP
10+import qualified Test.Tasty.HUnit as HU
8−import Test.Hspec
11+
9−
12+import Data.JsonSchema.Draft4
10−import JSONSchema.Draft4
13+import Data.JsonSchema.Draft4.Spec (validate)
11−import qualified JSONSchema.Draft4.Spec as Spec
12−import qualified JSONSchema.Validator.Draft4 as VAL
14+import qualified Data.Validator.Draft4.Array as AR
13−
15+
14−spec :: Spec
16+correctPaths :: [TestTree]
15−spec = do
17+correctPaths =
16− it "items array failures are constructed correctly" itemsArray
18+ [ HU.testCase "Items object" itemsObject
17− it "items object failures are constructed correctly" itemsObject
19+ , HU.testCase "Items array" itemsArray
18− it "reports infinitely looping references correctly" loopRef
20+ ]
19−
21+
20−itemsArray :: Expectation
21−itemsArray =
22− failures `shouldBe`
23− [ FailureItems (VAL.ItemsArrayInvalid (
24− pure ( JP.Index 0
25− , pure (FailureUniqueItems (VAL.UniqueItemsInvalid
26− (V.fromList [Null, Null])
27− ))
28− )
29− ))
30− ]
31− where
32− failures :: [ValidatorFailure]
33− failures = Spec.specValidate mempty sw badData
34−
35− schema :: Schema
36− schema = emptySchema
37− { _schemaItems = Just $ VAL.ItemsArray
38− [emptySchema { _schemaUniqueItems = Just True }]
39− }
40−
41− sw :: SchemaWithURI Schema
42− sw = SchemaWithURI
43− { _swSchema = schema
44− , _swURI = Nothing
45− }
46−
47− badData :: Value
48− badData = toJSON [[Null, Null]]
49−
50−itemsObject :: Expectation
22+itemsObject :: IO ()
51itemsObject =
23itemsObject =
52− failures `shouldBe`
24+ HU.assertEqual
53− [ FailureItems (VAL.ItemsObjectInvalid (
25+ "Path to invalid data"
54− pure ( JP.Index 1
26+ [Failure (Items UniqueItems) (Bool True) (P.Pointer [P.Token "0"])
55− , pure (FailureUniqueItems (VAL.UniqueItemsInvalid
27+ (toJSON [True, True])
56− (V.fromList [Null, Null])
57− ))
58− )
59− ))
60 ]
28 ]
29+ failures
61 where
30 where
62− failures :: [ValidatorFailure]
31+ failures = validate (ReferencedSchemas schema mempty)
63− failures = Spec.specValidate mempty sw badData
32+ sw (toJSON [[True, True]])
64
33
65 schema :: Schema
34 schema :: Schema
66 schema = emptySchema
35 schema = emptySchema
67− { _schemaItems = Just $ VAL.ItemsObject
36+ { _schemaItems = Just $ AR.ItemsObject
68 (emptySchema { _schemaUniqueItems = Just True })
37 (emptySchema { _schemaUniqueItems = Just True })
69 }
38 }
70
39
@@ -74,25 +43,22 @@ itemsObject =
74 , _swURI = Nothing
43 , _swURI = Nothing
75 }
44 }
76
45
77− badData :: Value
46+itemsArray :: IO ()
78− badData = toJSON [Null, toJSON [Null, Null]]
47+itemsArray =
79−
48+ HU.assertEqual
80−loopRef :: Expectation
49+ "Path to invalid data"
81−loopRef =
50+ [Failure (Items UniqueItems) (Bool True) (P.Pointer [P.Token "0"])
82− failures `shouldBe`
51+ (toJSON [True, True])
83− [ FailureRef (VAL.RefLoop
84− "#"
85− (VAL.VisitedSchemas [(Nothing, Nothing)])
86− (Nothing, Nothing)
87− )
88 ]
52 ]
53+ failures
89 where
54 where
90− failures :: [ValidatorFailure]
55+ failures = validate (ReferencedSchemas schema mempty)
91− failures = Spec.specValidate mempty sw badData
56+ sw (toJSON [[True, True]])
92
57
93 schema :: Schema
58 schema :: Schema
94 schema = emptySchema
59 schema = emptySchema
95− { _schemaRef = Just "#"
60+ { _schemaItems = Just $ AR.ItemsArray
61+ [emptySchema { _schemaUniqueItems = Just True }]
96 }
62 }
97
63
98 sw :: SchemaWithURI Schema
64 sw :: SchemaWithURI Schema
@@ -100,6 +66,3 @@ loopRef =
100 { _swSchema = schema
66 { _swSchema = schema
101 , _swURI = Nothing
67 , _swURI = Nothing
102 }
68 }
103−
104− badData :: Value
105− badData = toJSON [[Null, Null]]
test/Local/Reference.hs+49−34
@@ -1,36 +1,51 @@
1−module Local.Reference where
2−
3−import Protolude
4−
5−import Test.Hspec
6−
7−import JSONSchema.Validator.Reference
8−
9−spec :: Spec
10−spec = do
11− it "updateResolutionScope gives correct results" $ do
12− updateResolutionScope (BaseURI Nothing) Nothing
13− `shouldBe` BaseURI Nothing
14−
15− updateResolutionScope (BaseURI Nothing) (Just "#")
16− `shouldBe` BaseURI Nothing
17
1
18− updateResolutionScope (BaseURI Nothing) (Just "foo")
2+module Local.Reference where
19− `shouldBe` BaseURI (Just "foo")
20−
21− -- TODO: Normalize after updateResolutionScope:
22− updateResolutionScope (BaseURI (Just "/foo")) (Just "./bar")
23− `shouldBe` BaseURI (Just "/./bar")
24−
25− it "resolveReference gives correct results" $ do
26− resolveReference (BaseURI (Just "/foo")) "bar"
27− `shouldBe` (Just "/bar", Nothing)
28−
29− resolveReference (BaseURI (Just "/foo/bar")) "/baz"
30− `shouldBe` (Just "/baz", Nothing)
31−
32− resolveReference (BaseURI Nothing) "#/bar"
33− `shouldBe` (Nothing, Just "/bar")
34
3
4+import Test.Tasty (TestTree)
5+import qualified Test.Tasty.HUnit as HU
6+
7+import Data.Validator.Reference
8+
9+referenceTests :: [TestTree]
10+referenceTests =
11+ [ HU.testCase "updateResolutionScope test cases" updateResolutionScopeTests
12+ , HU.testCase "resolveReference test cases" resolveReferenceTests
13+ ]
14+
15+updateResolutionScopeTests :: IO ()
16+updateResolutionScopeTests = do
17+ HU.assertEqual
18+ "case 1 result"
19+ Nothing
20+ (updateResolutionScope Nothing Nothing)
21+ HU.assertEqual
22+ "case 2 result"
23+ Nothing
24+ (updateResolutionScope Nothing (Just "#"))
25+ HU.assertEqual
26+ "case 3 result"
27+ (Just "foo")
28+ (updateResolutionScope Nothing (Just "foo"))
29+ HU.assertEqual
30+ "case 4 result"
31+ (Just "/./bar") -- TODO: Normalize after updateResolutionScope.
32+ (updateResolutionScope (Just "/foo") (Just "./bar"))
33+
34+resolveReferenceTests :: IO ()
35+resolveReferenceTests = do
36+ HU.assertEqual
37+ "case 1 result"
38+ (Just "/bar", Nothing)
35− resolveReference (BaseURI (Just "/foo")) "#/bar"
39+ (resolveReference (Just "/foo") "bar")
40+ HU.assertEqual
41+ "case 2 result"
42+ (Just "/baz", Nothing)
43+ (resolveReference (Just "/foo/bar") "/baz")
44+ HU.assertEqual
45+ "case 3 result"
46+ (Nothing, Just "/bar")
47+ (resolveReference Nothing "#/bar")
48+ HU.assertEqual
49+ "case 4 result"
36− `shouldBe` (Just "/foo", Just "/bar")
50+ (Just "/foo", Just "/bar")
51+ (resolveReference (Just "/foo") "#/bar")
test/Local/Validation.hs+47−35
@@ -1,54 +1,55 @@
1−module Local.Validation where
2
1
3−import Protolude
2+module Local.Validation where
4
3
4+import Control.Applicative
5import Data.Aeson
5import Data.Aeson
6−import qualified Data.Aeson as AE
6+import qualified Data.Aeson as AE
7−import qualified Data.HashMap.Strict as HM
7+import qualified Data.HashMap.Strict as HM
8−import Data.String (String)
8+import Data.Monoid
9−import Test.Hspec
9+import Data.Text (Text)
10−
10+import Test.Tasty (TestTree)
11−import qualified AlternateSchema as AS
12−import JSONSchema.Draft4
13−import qualified JSONSchema.Types as JT
11+import qualified Test.Tasty.HUnit as HU
14
12
15−spec :: Spec
13+import Data.JsonSchema.Draft4
16−spec = do
14+import qualified Data.JsonSchema.Types as JT
17− -- Fetching from filesystem.
18
15
19− it "a readFile exception during validation is turned into a left"
16+import qualified AlternateSchema as AS
20− readFileException
21− it "Relative reference to local file"
22− (resolutionInvalid "test/Local/schema.json")
23− it "Chained relative references to local files"
24− (resolutionInvalid "./test/Local/schema-with-ref.json")
25
17
26− -- Validation tests that were hard to write in JSON for whatever reason.
18+fetchFromFilesystem :: [TestTree]
19+fetchFromFilesystem =
20+ [ HU.testCase
21+ "readFile exceptions are turned into Lefts"
22+ readFileExceptions
27
23
28− it "Don't parse schemas that have Null in a forbidden location"
24+ , HU.testCase
29− forbidNull
25+ "Relative reference to local file"
30− it "The 'Value' based Schema's checkSchema should catch Nulls"
26+ (resolve "test/Local/schema.json")
31− exampleForbidNull
27+ , HU.testCase
28+ "Chained relative references to local files"
29+ (resolve "./test/Local/schema-with-ref.json")
30+ ]
32
31
33−readFileException :: Expectation
32+readFileExceptions :: IO ()
34−readFileException = do
33+readFileExceptions = do
35− let schema = emptySchema { _schemaRef = Just "does-not-exist.json" }
36 res <- referencesViaFilesystem (SchemaWithURI schema Nothing)
34 res <- referencesViaFilesystem (SchemaWithURI schema Nothing)
37 case res of
35 case res of
38 Left (FSReadFailure _) -> pure ()
36 Left (FSReadFailure _) -> pure ()
39− a -> expectationFailure (msg <> show a)
37+ a -> error (msg <> show a)
40 where
38 where
39+ schema :: Schema
40+ schema = emptySchema { _schemaRef = Just "does-not-exist.json" }
41+
41 msg :: String
42 msg :: String
42 msg = "expected referencesViaFilesystem to return ReadFailure,"
43 msg = "expected referencesViaFilesystem to return ReadFailure,"
43 <> " instead got: "
44 <> " instead got: "
44
45
45−resolutionInvalid :: Text -> Expectation
46+resolve :: Text -> IO ()
46−resolutionInvalid ref = do
47+resolve ref = do
47 let schema = emptySchema { _schemaRef = Just ref }
48 let schema = emptySchema { _schemaRef = Just ref }
48 res <- fetchFilesystemAndValidate (SchemaWithURI schema Nothing) badData
49 res <- fetchFilesystemAndValidate (SchemaWithURI schema Nothing) badData
49 case res of
50 case res of
50 Left (FVData _) -> pure ()
51 Left (FVData _) -> pure ()
51− a -> expectationFailure (msg <> show a)
52+ a -> error (msg <> show a)
52 where
53 where
53 badData :: Value
54 badData :: Value
54 badData = toJSON [True, True]
55 badData = toJSON [True, True]
@@ -57,14 +58,25 @@ resolutionInvalid ref = do
57 msg = "expected fetchFilesystemAndValidate to return"
58 msg = "expected fetchFilesystemAndValidate to return"
58 <> " Left (FVData [_]), instead got: "
59 <> " Left (FVData [_]), instead got: "
59
60
61+-- | Validation tests that were hard to write in JSON for whatever reason.
62+generalValidation :: [TestTree]
63+generalValidation =
64+ [ HU.testCase
65+ "Don't parse schemas that have Null in a forbidden location"
60−forbidNull :: Expectation
66+ forbidNull
67+ , HU.testCase
68+ "The 'Value' based Schema's checkSchema should catch Nulls"
69+ exampleForbidNull
70+ ]
71+
72+forbidNull :: IO ()
61forbidNull =
73forbidNull =
62 case fromJSON (Object (HM.singleton "type" Null)) of
74 case fromJSON (Object (HM.singleton "type" Null)) of
63 AE.Error _ -> pure ()
75 AE.Error _ -> pure ()
64− AE.Success a -> expectationFailure ("parsed to: " <> show (a :: Schema))
76+ AE.Success a -> HU.assertFailure ("parsed to: " <> show (a :: Schema))
65
77
66−exampleForbidNull :: Expectation
78+exampleForbidNull :: IO ()
67exampleForbidNull =
79exampleForbidNull =
68 case AS.checkSchema (JT.Schema (HM.singleton "type" Null)) of
80 case AS.checkSchema (JT.Schema (HM.singleton "type" Null)) of
69− [] -> expectationFailure "No checkSchema failures"
81+ [] -> HU.assertFailure "No checkSchema failures"
70 _ -> pure ()
82 _ -> pure ()
test/Remote.hs+30−23
@@ -1,57 +1,64 @@
1module Main where
1module Main where
2
2
3−import Protolude
3+import Control.Applicative
4−
5−import Control.Concurrent.Async (withAsync)
4+import Control.Concurrent.Async (withAsync)
5+import Data.Foldable (traverse_)
6−import qualified Data.List.NonEmpty as NE
6+import qualified Data.List.NonEmpty as N
7+import Data.Monoid
7import Network.Wai.Application.Static (defaultFileServerSettings,
8import Network.Wai.Application.Static (defaultFileServerSettings,
8 staticApp)
9 staticApp)
9−import qualified Network.Wai.Handler.Warp as Warp
10+import Network.Wai.Handler.Warp (run)
10−import Test.Hspec
11+import Test.Tasty (defaultMain, testGroup)
12+import qualified Test.Tasty.HUnit as HU
11
13
12−import qualified JSONSchema.Draft4 as D4
14+import qualified Data.JsonSchema.Draft4 as D4
15+import Data.JsonSchema.Fetch (ReferencedSchemas(..))
13−import qualified JSONSchema.Types as JT
16+import qualified Data.JsonSchema.Types as JT
14import Shared
17import Shared
15
18
16-- Examples
19-- Examples
17import qualified AlternateSchema
20import qualified AlternateSchema
18
21
19−dir :: FilePath
22+dir :: String
20dir = "JSON-Schema-Test-Suite/tests/draft4"
23dir = "JSON-Schema-Test-Suite/tests/draft4"
21
24
22main :: IO ()
25main :: IO ()
23main = withAsync serve $ \_ -> do
26main = withAsync serve $ \_ -> do
24 ts <- readSchemaTests dir isHTTPTest
27 ts <- readSchemaTests dir isHTTPTest
25− hspec $ do
28+ defaultMain . testGroup "Tests that run an HTTP server" $
29+ [ testGroup
26− describe "Language agnostic tests (using the record based schema)"
30+ "Language agnostic remote tests (using the record based schema)"
27− (traverse_ (toTest validate) ts)
31+ (toTest validate <$> ts)
32+ , testGroup
28− describe "Language agnostic tests (using the 'Value' based schema)"
33+ "Language agnostic remote tests (using the 'Value' based schema)"
29− (traverse_ (toTest validateExample) ts)
34+ (toTest validateExample <$> ts)
35+ ]
30 where
36 where
31− validate :: D4.Schema -> SchemaTestCase -> Expectation
37+ validate :: D4.Schema -> SchemaTestCase -> HU.Assertion
32 validate s sc = do
38 validate s sc = do
33 res <- D4.fetchHTTPAndValidate (D4.SchemaWithURI s Nothing) (_scData sc)
39 res <- D4.fetchHTTPAndValidate (D4.SchemaWithURI s Nothing) (_scData sc)
34 let failures = case res of
40 let failures = case res of
35 Right () -> mempty
41 Right () -> mempty
36− Left (D4.HVData a) -> NE.toList (D4._invalidFailures a)
42+ Left (D4.HVData a) -> N.toList a
37− other -> panic ("Local.validate error: "
43+ other -> error ("Local.validate error: "
38 <> show other)
44 <> show other)
45+ traverse_ (checkPointer (_scData sc)) failures
39 assertResult sc failures
46 assertResult sc failures
40
47
41− validateExample :: JT.Schema -> SchemaTestCase -> Expectation
48+ validateExample :: JT.Schema -> SchemaTestCase -> HU.Assertion
42 validateExample s sc = do
49 validateExample s sc = do
43 res <- AlternateSchema.referencesViaHTTP (D4.SchemaWithURI s Nothing)
50 res <- AlternateSchema.referencesViaHTTP (D4.SchemaWithURI s Nothing)
44 case res of
51 case res of
45− Left e -> panic ("Remote.validateExample error: " <> show e)
52+ Left e -> error ("Remote.validateExample error: " <> show e)
46 Right schemaMap -> do
53 Right schemaMap -> do
47 let failures = AlternateSchema.validate
54 let failures = AlternateSchema.validate
48− schemaMap
55+ (ReferencedSchemas s schemaMap)
49− (D4.SchemaWithURI s Nothing)
56+ Nothing s (_scData sc)
50− (_scData sc)
57+ traverse_ (checkPointer (_scData sc)) failures
51 assertResult sc failures
58 assertResult sc failures
52
59
53serve :: IO ()
60serve :: IO ()
54−serve = Warp.run 1234
61+serve = run 1234
55 . staticApp
62 . staticApp
56 . defaultFileServerSettings
63 . defaultFileServerSettings
57 $ "JSON-Schema-Test-Suite/remotes"
64 $ "JSON-Schema-Test-Suite/remotes"
test/Shared.hs+72−37
@@ -1,28 +1,29 @@
1−module Shared where
1+{-# LANGUAGE DeriveGeneric #-}
2
2
3−import Protolude
3+module Shared where
4
4
5−import Control.Monad (fail)
5+import Control.Applicative
6+import Control.Monad
6import Data.Aeson
7import Data.Aeson
8+import qualified Data.Aeson.Pointer as AP
7−import Data.Aeson.TH (fieldLabelModifier)
9+import Data.Aeson.TH
8−import qualified Data.ByteString as BS
10+import qualified Data.ByteString.Lazy as LBS
9−import Data.Char (toLower)
11+import Data.Char (toLower)
12+import qualified Data.HashMap.Strict as HM
10−import Data.List (stripPrefix, unlines)
13+import Data.List (isInfixOf, stripPrefix)
14+import Data.Maybe
15+import Data.Monoid
16+import Data.Text (Text)
11−import qualified Data.Text as T
17+import qualified Data.Text as T
18+import Data.Traversable
19+import qualified Data.Vector as V
20+import GHC.Generics
12−import qualified System.Directory as SD
21+import qualified System.Directory as SD
13−import System.FilePath ((</>))
22+import System.FilePath ((</>))
14−import Test.Hspec
23+import Test.Tasty (TestTree)
15−
24+import qualified Test.Tasty.HUnit as HU
16−skipTest :: FilePath -> Bool
25+
17−skipTest file = (file == "optional/format.json") -- Optional
26+import qualified Data.JsonSchema.Draft4 as D4
18− || (file == "optional/zeroTerminatedFloats.json")
19− || (file == "optional/ecmascript-regex.json")
20−
21−
22−isHTTPTest :: FilePath -> Bool
23−isHTTPTest file = (file == "definitions.json")
24− || (file == "ref.json")
25− || (file == "refRemote.json")
26
27
27-- Recursively return the contents of a directory
28-- Recursively return the contents of a directory
28-- (or return itself if given a file as an argument).
29-- (or return itself if given a file as an argument).
@@ -47,6 +48,40 @@ listFilesFullPath path = do
47 fs <- fmap (path </>) <$> SD.listDirectory path
48 fs <- fmap (path </>) <$> SD.listDirectory path
48 concat <$> traverse listFilesFullPath fs
49 concat <$> traverse listFilesFullPath fs
49
50
51+checkPointer :: Value -> D4.Failure -> HU.Assertion
52+checkPointer v failure =
53+ case AP.resolve (D4._failureOffendingPointer failure) v of
54+ Left e -> error ("Couldn't resolve pointer: " <> show e)
55+ Right a -> assertContains a (D4._failureOffendingData failure)
56+ where
57+ -- Some validators, such as 'additionalItems', only return a subset
58+ -- of the data incated by their '_failureOffendingPointer'.
59+ -- See the comments on 'Data.Validator.Failure.Failure' for more info.
60+ assertContains :: Value -> Value -> HU.Assertion
61+ assertContains x y
62+ | x == y = pure ()
63+ | otherwise =
64+ case (x,y) of
65+ (Array xs, Array ys) ->
66+ HU.assertBool "Pointer resolution incorrect"
67+ (V.toList ys `isInfixOf` V.toList xs)
68+ (Object xhm, Object yhm) ->
69+ HU.assertBool "Pointer resolution incorrect"
70+ (HM.toList yhm `isInfixOf` HM.toList xhm)
71+ _ -> HU.assertFailure
72+ "Pointer resolution incorrect: result mismatch"
73+
74+isHTTPTest :: String -> Bool
75+isHTTPTest file = (file == "definitions.json")
76+ || (file == "ref.json")
77+ || (file == "refRemote.json")
78+
79+-- | We may never support the @"format"@ keywords, and
80+-- are currently failing the zeroTerminatedFloats test.
81+skipOptional :: String -> Bool
82+skipOptional file = (file == "optional/format.json")
83+ || (file == "optional/zeroTerminatedFloats.json")
84+
50data SchemaTest = SchemaTest
85data SchemaTest = SchemaTest
51 { _stDescription :: Text
86 { _stDescription :: Text
52 , _stSchema :: Value
87 , _stSchema :: Value
@@ -72,9 +107,9 @@ instance FromJSON SchemaTestCase where
72 { fieldLabelModifier = fmap toLower . drop 3 }
107 { fieldLabelModifier = fmap toLower . drop 3 }
73
108
74readSchemaTests
109readSchemaTests
75− :: FilePath
110+ :: String
76 -- ^ The path to a directory.
111 -- ^ The path to a directory.
77− -> (FilePath -> Bool)
112+ -> (String -> Bool)
78 -- ^ A function to decide what we're interested in within that directory.
113 -- ^ A function to decide what we're interested in within that directory.
79 -> IO [SchemaTest]
114 -> IO [SchemaTest]
80readSchemaTests dir filterFunc = do
115readSchemaTests dir filterFunc = do
@@ -82,51 +117,51 @@ readSchemaTests dir filterFunc = do
82 concat <$> traverse fileToCases files
117 concat <$> traverse fileToCases files
83 where
118 where
84 -- Each file contains an array of SchemaTests, not just one.
119 -- Each file contains an array of SchemaTests, not just one.
85− fileToCases :: FilePath -> IO [SchemaTest]
120+ fileToCases :: String -> IO [SchemaTest]
86 fileToCases name = do
121 fileToCases name = do
87 let fullPath = dir </> name
122 let fullPath = dir </> name
88− jsonBS <- BS.readFile fullPath
123+ jsonBS <- LBS.readFile fullPath
89− case eitherDecodeStrict jsonBS of
124+ case eitherDecode jsonBS of
90 Left e -> fail $ "couldn't parse file '" <> fullPath <> "': " <> e
125 Left e -> fail $ "couldn't parse file '" <> fullPath <> "': " <> e
91 Right schemaTests -> pure $ prependFileName name <$> schemaTests
126 Right schemaTests -> pure $ prependFileName name <$> schemaTests
92
127
93− prependFileName :: FilePath -> SchemaTest -> SchemaTest
128+ prependFileName :: String -> SchemaTest -> SchemaTest
94 prependFileName fileName s = s
129 prependFileName fileName s = s
95 { _stDescription = T.pack fileName <> ": " <> _stDescription s
130 { _stDescription = T.pack fileName <> ": " <> _stDescription s
96 }
131 }
97
132
98toTest
133toTest
99 :: forall schema. FromJSON schema
134 :: forall schema. FromJSON schema
100− => (schema -> SchemaTestCase -> Expectation)
135+ => (schema -> SchemaTestCase -> HU.Assertion)
101 -> SchemaTest
136 -> SchemaTest
102− -> Spec
137+ -> TestTree
103toTest validate st =
138toTest validate st =
104− it (T.unpack (_stDescription st)) $
139+ HU.testCase (T.unpack (_stDescription st)) $
105 forM_ (_stCases st) (validate schema)
140 forM_ (_stCases st) (validate schema)
106 where
141 where
107 schema :: schema
142 schema :: schema
108 schema = case fromJSON (_stSchema st) of
143 schema = case fromJSON (_stSchema st) of
109− Error e -> panic ("Couldn't parse schema: " <> show e)
144+ Error e -> error ("Couldn't parse schema: " <> show e)
110 Success a -> a
145 Success a -> a
111
146
112−assertResult :: Show err => SchemaTestCase -> [err] -> Expectation
147+assertResult :: SchemaTestCase -> [D4.Failure] -> HU.Assertion
113assertResult sc failures
148assertResult sc failures
114 | _scValid sc = assertValid sc failures
149 | _scValid sc = assertValid sc failures
115 | otherwise = assertInvalid sc failures
150 | otherwise = assertInvalid sc failures
116
151
117−assertValid :: Show err => SchemaTestCase -> [err] -> Expectation
152+assertValid :: SchemaTestCase -> [D4.Failure] -> HU.Assertion
118assertValid _ [] = pure ()
153assertValid _ [] = pure ()
119assertValid sc failures =
154assertValid sc failures =
120− expectationFailure $ unlines
155+ HU.assertFailure $ unlines
121 [ " Failed to validate data"
156 [ " Failed to validate data"
122 , " Description: " <> T.unpack (_scDescription sc)
157 , " Description: " <> T.unpack (_scDescription sc)
123 , " Data: " <> show (_scData sc)
158 , " Data: " <> show (_scData sc)
124 , " Validation failures: " <> show failures
159 , " Validation failures: " <> show failures
125 ]
160 ]
126
161
127−assertInvalid :: SchemaTestCase -> [err] -> Expectation
162+assertInvalid :: SchemaTestCase -> [D4.Failure] -> HU.Assertion
128assertInvalid sc [] =
163assertInvalid sc [] =
129− expectationFailure $ unlines
164+ HU.assertFailure $ unlines
130 [ " Validated invalid data"
165 [ " Validated invalid data"
131 , " Description: " <> T.unpack (_scDescription sc)
166 , " Description: " <> T.unpack (_scDescription sc)
132 , " Data: " <> show (_scData sc)
167 , " Data: " <> show (_scData sc)