From fe68965f239ed511ca7604d7f08353a8feb2b973 Mon Sep 17 00:00:00 2001 From: lazedo Date: Thu, 8 Sep 2016 21:00:59 +0100 Subject: [PATCH 1/8] support 'infinity' in oneOf , anyOf resolves #22 --- src/jesse_validator_draft4.erl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/jesse_validator_draft4.erl b/src/jesse_validator_draft4.erl index 16f8d85..c991eff 100644 --- a/src/jesse_validator_draft4.erl +++ b/src/jesse_validator_draft4.erl @@ -1103,7 +1103,11 @@ check_any_of_(Value, [], State) -> handle_data_invalid(?any_schemas_not_valid, Value, State); check_any_of_(Value, [Schema | Schemas], State) -> case validate_schema(Value, Schema, State) of - {true, NewState} -> NewState; + {true, NewState} -> + case jesse_state:get_error_list(NewState) of + [] -> NewState; + _ -> check_any_of_(Value, Schemas, State) + end; {false, _} -> check_any_of_(Value, Schemas, State) end. @@ -1137,7 +1141,10 @@ check_one_of_(Value, _Schemas, State, Valid) when Valid > 1 -> check_one_of_(Value, [Schema | Schemas], State, Valid) -> case validate_schema(Value, Schema, State) of {true, NewState} -> - check_one_of_(Value, Schemas, NewState, Valid + 1); + case jesse_state:get_error_list(NewState) of + [] -> check_one_of_(Value, Schemas, NewState, Valid + 1); + _ -> check_one_of_(Value, Schemas, State, Valid) + end; {false, _} -> check_one_of_(Value, Schemas, State, Valid) end. From 73cc4eaee6f60c494e884225b39b7364f9099717 Mon Sep 17 00:00:00 2001 From: Andrei Neculau Date: Sat, 10 Dec 2016 23:11:23 +0100 Subject: [PATCH 2/8] add tests #22 #26 --- src/jesse_tests_util.erl | 27 ++++++- .../anyOfOneOfAllowedErrorsInfinityExtra.json | 75 +++++++++++++++++++ .../anyOfOneOfAllowedErrorsOneExtra.json | 75 +++++++++++++++++++ .../anyOfOneOfAllowedErrorsZeroExtra.json | 75 +++++++++++++++++++ test/jesse_tests_draft4_SUITE.erl | 9 +++ 5 files changed, 257 insertions(+), 4 deletions(-) create mode 100644 test/JSON-Schema-Test-Suite-extra/draft4/anyOfOneOfAllowedErrorsInfinityExtra.json create mode 100644 test/JSON-Schema-Test-Suite-extra/draft4/anyOfOneOfAllowedErrorsOneExtra.json create mode 100644 test/JSON-Schema-Test-Suite-extra/draft4/anyOfOneOfAllowedErrorsZeroExtra.json diff --git a/src/jesse_tests_util.erl b/src/jesse_tests_util.erl index cbc9241..9c8586a 100644 --- a/src/jesse_tests_util.erl +++ b/src/jesse_tests_util.erl @@ -30,6 +30,7 @@ %% JSON-Schema-Test-Suite attributes definitions -define(DATA, <<"data">>). -define(DESCRIPTION, <<"description">>). +-define(OPTIONS, <<"options">>). -define(SCHEMA, <<"schema">>). -define(TESTS, <<"tests">>). -define(VALID, <<"valid">>). @@ -57,26 +58,29 @@ do_test(Key, Config) -> Description = get_path(?DESCRIPTION, Test), Schema = get_path(?SCHEMA, Test), SchemaTests = get_path(?TESTS, Test), + Options = get_path(?OPTIONS, Test), ct:pal( "** Description: ~s~n" + "** Options: ~p~n" "** Schema: ~p~n" "** Schema tests: ~p~n" - , [Description, Schema, SchemaTests] + , [Description, Options, Schema, SchemaTests] ), - test_schema(DefaultSchema, Schema, SchemaTests) + test_schema(DefaultSchema, Options, Schema, SchemaTests) end , Tests ). %%% Internal functions -test_schema(DefaultSchema, Schema, SchemaTests) -> +test_schema(DefaultSchema, Opts0, Schema, SchemaTests) -> + Opts1 = make_options(Opts0), lists:foreach( fun(Test) -> Description = get_path(?DESCRIPTION, Test), Instance = get_path(?DATA, Test), ct:pal("* Test case: ~s~n", [Description]), Opts = [ {default_schema_ver, DefaultSchema} , {schema_loader_fun, fun load_schema/1} - ], + ] ++ Opts1, try jesse:validate_with_schema(Schema, Instance, Opts) of Result -> ct:pal("Result: ~p~n", [Result]), @@ -96,6 +100,21 @@ test_schema(DefaultSchema, Schema, SchemaTests) -> , SchemaTests ). +make_options(Options) -> + lists:map( fun ({Key0, Value0}) -> + Key = case is_binary(Key0) of + true -> list_to_existing_atom(binary_to_list(Key0)); + false -> Key0 + end, + Value = case is_binary(Value0) of + true -> list_to_existing_atom(binary_to_list(Value0)); + false -> Value0 + end, + {Key, Value} + end + , Options + ). + testfile_to_key(TestFile) -> filename:rootname(filename:basename(TestFile)). diff --git a/test/JSON-Schema-Test-Suite-extra/draft4/anyOfOneOfAllowedErrorsInfinityExtra.json b/test/JSON-Schema-Test-Suite-extra/draft4/anyOfOneOfAllowedErrorsInfinityExtra.json new file mode 100644 index 0000000..ce55c9b --- /dev/null +++ b/test/JSON-Schema-Test-Suite-extra/draft4/anyOfOneOfAllowedErrorsInfinityExtra.json @@ -0,0 +1,75 @@ +[ + { + "description": "anyOf/oneOf with allowed_errors", + "options": { + "allowed_errors": "infinity" + }, + "schema": { + "type": "object", + "properties": { + "anyOf": { + "anyOf": [ + { + "enum": [0] + }, { + "enum": [0, 1] + } + ] + }, + "oneOf": { + "oneOf": [ + { + "enum": [0] + }, { + "enum": [0, 1] + } + ] + } + } + }, + "tests": [ + { + "description": "anyOf: all valid", + "data": { + "anyOf": 0 + }, + "valid": true + }, + { + "description": "anyOf: one valid", + "data": { + "anyOf": 1 + }, + "valid": true + }, + { + "description": "anyOf: invalid", + "data": { + "anyOf": 2 + }, + "valid": false + }, + { + "description": "oneOf: all valid", + "data": { + "oneOf": 0 + }, + "valid": false + }, + { + "description": "oneOf: one valid", + "data": { + "oneOf": 1 + }, + "valid": true + }, + { + "description": "oneOf: invalid", + "data": { + "oneOf": 2 + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite-extra/draft4/anyOfOneOfAllowedErrorsOneExtra.json b/test/JSON-Schema-Test-Suite-extra/draft4/anyOfOneOfAllowedErrorsOneExtra.json new file mode 100644 index 0000000..296f95a --- /dev/null +++ b/test/JSON-Schema-Test-Suite-extra/draft4/anyOfOneOfAllowedErrorsOneExtra.json @@ -0,0 +1,75 @@ +[ + { + "description": "anyOf/oneOf with allowed_errors", + "options": { + "allowed_errors": 1 + }, + "schema": { + "type": "object", + "properties": { + "anyOf": { + "anyOf": [ + { + "enum": [0] + }, { + "enum": [0, 1] + } + ] + }, + "oneOf": { + "oneOf": [ + { + "enum": [0] + }, { + "enum": [0, 1] + } + ] + } + } + }, + "tests": [ + { + "description": "anyOf: all valid", + "data": { + "anyOf": 0 + }, + "valid": true + }, + { + "description": "anyOf: one valid", + "data": { + "anyOf": 1 + }, + "valid": true + }, + { + "description": "anyOf: invalid", + "data": { + "anyOf": 2 + }, + "valid": false + }, + { + "description": "oneOf: all valid", + "data": { + "oneOf": 0 + }, + "valid": false + }, + { + "description": "oneOf: one valid", + "data": { + "oneOf": 1 + }, + "valid": true + }, + { + "description": "oneOf: invalid", + "data": { + "oneOf": 2 + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite-extra/draft4/anyOfOneOfAllowedErrorsZeroExtra.json b/test/JSON-Schema-Test-Suite-extra/draft4/anyOfOneOfAllowedErrorsZeroExtra.json new file mode 100644 index 0000000..0b346f0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite-extra/draft4/anyOfOneOfAllowedErrorsZeroExtra.json @@ -0,0 +1,75 @@ +[ + { + "description": "anyOf/oneOf with allowed_errors", + "options": { + "allowed_errors": 0 + }, + "schema": { + "type": "object", + "properties": { + "anyOf": { + "anyOf": [ + { + "enum": [0] + }, { + "enum": [0, 1] + } + ] + }, + "oneOf": { + "oneOf": [ + { + "enum": [0] + }, { + "enum": [0, 1] + } + ] + } + } + }, + "tests": [ + { + "description": "anyOf: all valid", + "data": { + "anyOf": 0 + }, + "valid": true + }, + { + "description": "anyOf: one valid", + "data": { + "anyOf": 1 + }, + "valid": true + }, + { + "description": "anyOf: invalid", + "data": { + "anyOf": 2 + }, + "valid": false + }, + { + "description": "oneOf: all valid", + "data": { + "oneOf": 0 + }, + "valid": false + }, + { + "description": "oneOf: one valid", + "data": { + "oneOf": 1 + }, + "valid": true + }, + { + "description": "oneOf: invalid", + "data": { + "oneOf": 2 + }, + "valid": false + } + ] + } +] diff --git a/test/jesse_tests_draft4_SUITE.erl b/test/jesse_tests_draft4_SUITE.erl index 2422ea9..8a0d6b3 100644 --- a/test/jesse_tests_draft4_SUITE.erl +++ b/test/jesse_tests_draft4_SUITE.erl @@ -155,3 +155,12 @@ itemsExtra(Config) -> remoteRefExtra(Config) -> do_test("remoteRefExtra", Config). + +anyOfOneOfAllowedErrorsZeroExtra(Config) -> + do_test("anyOfOneOfAllowedErrorsZeroExtra", Config). + +anyOfOneOfAllowedErrorsOneExtra(Config) -> + do_test("anyOfOneOfAllowedErrorsOneExtra", Config). + +anyOfOneOfAllowedErrorsInfinityExtra(Config) -> + do_test("anyOfOneOfAllowedErrorsInfinityExtra", Config). From 2970e0fa99312679428e02321c9768d60eef945e Mon Sep 17 00:00:00 2001 From: Arpad Budai Date: Mon, 21 Nov 2016 15:35:01 +0000 Subject: [PATCH 3/8] - use the unicode option for re:run() when filtering the extra names so it won't reject on pattern matching a unicode field --- src/jesse_validator_draft3.erl | 2 +- src/jesse_validator_draft4.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jesse_validator_draft3.erl b/src/jesse_validator_draft3.erl index fdcaf63..826bcd3 100644 --- a/src/jesse_validator_draft3.erl +++ b/src/jesse_validator_draft3.erl @@ -487,7 +487,7 @@ get_additional_properties(Value, Properties, PatternProperties) -> %% @private filter_extra_names(Pattern, ExtraNames) -> Filter = fun(ExtraName) -> - case re:run(ExtraName, Pattern, [{capture, none}]) of + case re:run(ExtraName, Pattern, [{capture, none}, unicode]) of match -> false; nomatch -> true end diff --git a/src/jesse_validator_draft4.erl b/src/jesse_validator_draft4.erl index c991eff..9e8d893 100644 --- a/src/jesse_validator_draft4.erl +++ b/src/jesse_validator_draft4.erl @@ -490,7 +490,7 @@ get_additional_properties(Value, Properties, PatternProperties) -> %% @private filter_extra_names(Pattern, ExtraNames) -> Filter = fun(ExtraName) -> - case re:run(ExtraName, Pattern, [{capture, none}]) of + case re:run(ExtraName, Pattern, [{capture, none}, unicode]) of match -> false; nomatch -> true end From 84a53a3ea4417d570e5e2abf48cde3e92bf203d2 Mon Sep 17 00:00:00 2001 From: Arpad Budai Date: Thu, 15 Dec 2016 16:22:46 +0000 Subject: [PATCH 4/8] - add test for non ascii pattern properties for draft3 and draft4 --- .../draft3/unicodePatternProperties.json | 21 +++++++++++++++++++ .../draft4/unicodePatternProperties.json | 21 +++++++++++++++++++ test/jesse_tests_draft3_SUITE.erl | 3 +++ test/jesse_tests_draft4_SUITE.erl | 3 +++ 4 files changed, 48 insertions(+) create mode 100644 test/JSON-Schema-Test-Suite-extra/draft3/unicodePatternProperties.json create mode 100644 test/JSON-Schema-Test-Suite-extra/draft4/unicodePatternProperties.json diff --git a/test/JSON-Schema-Test-Suite-extra/draft3/unicodePatternProperties.json b/test/JSON-Schema-Test-Suite-extra/draft3/unicodePatternProperties.json new file mode 100644 index 0000000..06e114f --- /dev/null +++ b/test/JSON-Schema-Test-Suite-extra/draft3/unicodePatternProperties.json @@ -0,0 +1,21 @@ +{ + "description": "using non-ascii regex additionalProperties being false does not allow other properties ", + "schema": { + "patternProperties": { + "^á": {} + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "non-ascii patternProperties matching the pattern is valid", + "data": {"ármányos": 2}, + "valid": true + }, + { + "description": "additional non-ascii patternProperties not matching the pattern is invalid", + "data": {"élmény": 2}, + "valid": false + } + ] +} \ No newline at end of file diff --git a/test/JSON-Schema-Test-Suite-extra/draft4/unicodePatternProperties.json b/test/JSON-Schema-Test-Suite-extra/draft4/unicodePatternProperties.json new file mode 100644 index 0000000..06e114f --- /dev/null +++ b/test/JSON-Schema-Test-Suite-extra/draft4/unicodePatternProperties.json @@ -0,0 +1,21 @@ +{ + "description": "using non-ascii regex additionalProperties being false does not allow other properties ", + "schema": { + "patternProperties": { + "^á": {} + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "non-ascii patternProperties matching the pattern is valid", + "data": {"ármányos": 2}, + "valid": true + }, + { + "description": "additional non-ascii patternProperties not matching the pattern is invalid", + "data": {"élmény": 2}, + "valid": false + } + ] +} \ No newline at end of file diff --git a/test/jesse_tests_draft3_SUITE.erl b/test/jesse_tests_draft3_SUITE.erl index 4abdb89..d3d1460 100644 --- a/test/jesse_tests_draft3_SUITE.erl +++ b/test/jesse_tests_draft3_SUITE.erl @@ -140,3 +140,6 @@ itemsExtra(Config) -> remoteRefExtra(Config) -> do_test("remoteRefExtra", Config). + +unicodePatternProperties(Config) -> + do_test("unicodePatternProperties", Config). diff --git a/test/jesse_tests_draft4_SUITE.erl b/test/jesse_tests_draft4_SUITE.erl index 8a0d6b3..424ea28 100644 --- a/test/jesse_tests_draft4_SUITE.erl +++ b/test/jesse_tests_draft4_SUITE.erl @@ -164,3 +164,6 @@ anyOfOneOfAllowedErrorsOneExtra(Config) -> anyOfOneOfAllowedErrorsInfinityExtra(Config) -> do_test("anyOfOneOfAllowedErrorsInfinityExtra", Config). + +unicodePatternProperties(Config) -> + do_test("unicodePatternProperties", Config). \ No newline at end of file From 9502fe3edefff14ad8e645fcd12d6ba117553dcc Mon Sep 17 00:00:00 2001 From: Andrei Neculau Date: Fri, 16 Dec 2016 11:58:00 +0100 Subject: [PATCH 5/8] add comments with regards to R15 compatibility efforts --- src/jesse_validator_draft4.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/jesse_validator_draft4.erl b/src/jesse_validator_draft4.erl index bcdaa51..866838c 100644 --- a/src/jesse_validator_draft4.erl +++ b/src/jesse_validator_draft4.erl @@ -968,11 +968,13 @@ check_format(Value, _Format = <<"email">>, State) when is_binary(Value) -> nomatch -> handle_data_invalid(?wrong_format, Value, State) end; check_format(Value, _Format = <<"ip-address">>, State) when is_binary(Value) -> + %% avoiding inet:parse_ipv4strict_address to maintain R15 compatibility case inet_parse:ipv4strict_address(binary_to_list(Value)) of {ok, _IPv4Address} -> State; {error, einval} -> handle_data_invalid(?wrong_format, Value, State) end; check_format(Value, _Format = <<"ipv6">>, State) when is_binary(Value) -> + %% avoiding inet:parse_ipv6strict_address to maintain R15 compatibility case inet_parse:ipv6strict_address(binary_to_list(Value)) of {ok, _IPv6Address} -> State; {error, einval} -> handle_data_invalid(?wrong_format, Value, State) @@ -1342,6 +1344,7 @@ remove_last_from_path(State) -> %% @private valid_date(<>) -> try + %% avoiding binary_to_integer to maintain R15 compatibility calendar:valid_date( list_to_integer(binary_to_list(Year)) , list_to_integer(binary_to_list(Month)) , list_to_integer(binary_to_list(Day)) @@ -1353,6 +1356,7 @@ valid_date(_Other) -> false. %% @private valid_time(<>) -> + %% avoiding binary_to_integer to maintain R15 compatibility try { list_to_integer(binary_to_list(Hour)) , list_to_integer(binary_to_list(Minute)) , list_to_integer(binary_to_list(Second)) From dafe47f287aab0b9a8d8ecca9e3b022afddbc057 Mon Sep 17 00:00:00 2001 From: Andrei Neculau Date: Fri, 16 Dec 2016 12:06:22 +0100 Subject: [PATCH 6/8] remove note about not supporting "format" after #31 --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 3a1caf0..2b6a534 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,7 @@ jesse (JSON Schema Erlang) is an implementation of a JSON Schema validator for Erlang. jesse implements [Draft 03] (http://tools.ietf.org/html/draft-zyp-json-schema-03) and [Draft 04] (http://tools.ietf.org/html/draft-zyp-json-schema-04) of -the specification. It supports all core schema definitions except: - -* format +the specification. ## Quick start - CLI From 509ee0da113e38ecc4241ac9b4ff1327ef9e65bf Mon Sep 17 00:00:00 2001 From: Andrei Neculau Date: Fri, 16 Dec 2016 12:07:46 +0100 Subject: [PATCH 7/8] markdown style --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2b6a534..25bdbf0 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,10 @@ jesse (JSON Schema Erlang) is an implementation of a JSON Schema validator for Erlang. -jesse implements [Draft 03] (http://tools.ietf.org/html/draft-zyp-json-schema-03) and [Draft 04] (http://tools.ietf.org/html/draft-zyp-json-schema-04) of -the specification. +jesse implements the following specifications: + +* [Draft 03](http://tools.ietf.org/html/draft-zyp-json-schema-03) +* [Draft 04](http://tools.ietf.org/html/draft-zyp-json-schema-04) ## Quick start - CLI From 3308bb5bca88f9ba92b55a589cb8b8273a8480a7 Mon Sep 17 00:00:00 2001 From: Andrei Neculau Date: Fri, 16 Dec 2016 12:09:15 +0100 Subject: [PATCH 8/8] add links to erldocs. fix #25 --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 25bdbf0..0c79342 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ jesse implements the following specifications: * [Draft 03](http://tools.ietf.org/html/draft-zyp-json-schema-03) * [Draft 04](http://tools.ietf.org/html/draft-zyp-json-schema-04) +## Erlang API Docs + +Automatically generated docs are available https://dev.erldocs.com/github.com/for-get/jesse/ . + +Please keep in mind that the public API is the `jesse.erl` module alone. + ## Quick start - CLI You can fire up `jesse` from the CLI, with