Skip to content

Commit a4bf90f

Browse files
committed
set default values for properties
persistence depends on setter_fun option
1 parent 46f3d9a commit a4bf90f

File tree

6 files changed

+204
-31
lines changed

6 files changed

+204
-31
lines changed

src/jesse_error.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
%% throws an exeption, otherwise adds a new element to the list and returs it.
6161
-spec default_error_handler( Error :: error_reason()
6262
, ErrorList :: [error_reason()]
63-
, AllowedErrors :: non_neg_integer()
63+
, AllowedErrors :: non_neg_integer() | 'infinity'
6464
) -> [error_reason()] | no_return().
6565
default_error_handler(Error, ErrorList, AllowedErrors) ->
6666
case AllowedErrors > length(ErrorList) orelse AllowedErrors =:= 'infinity' of

src/jesse_schema_validator.hrl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
-define(MULTIPLEOF, <<"multipleOf">>).
5656
-define(MAXPROPERTIES, <<"maxProperties">>).
5757
-define(MINPROPERTIES, <<"minProperties">>).
58+
-define(DEFAULT, <<"default">>).
5859

5960
%% Constant definitions for Json types
6061
-define(ANY, <<"any">>).

src/jesse_state.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ add_to_path(State, Property) ->
9292
State#state{current_path = [Property | CurrentPath]}.
9393

9494
%% @doc Getter for `allowed_errors'.
95-
-spec get_allowed_errors(State :: state()) -> non_neg_integer().
95+
-spec get_allowed_errors(State :: state()) -> non_neg_integer() | 'infinity'.
9696
get_allowed_errors(#state{allowed_errors = AllowedErrors}) ->
9797
AllowedErrors.
9898

@@ -186,7 +186,7 @@ remove_last_from_path(State = #state{current_path = [_Property | Path]}) ->
186186

187187
%% @doc Getter for `allowed_errors'.
188188
-spec set_allowed_errors( State :: state()
189-
, AllowedErrors :: non_neg_integer()
189+
, AllowedErrors :: non_neg_integer() | 'infinity'
190190
) -> state().
191191
set_allowed_errors(#state{} = State, AllowedErrors) ->
192192
State#state{allowed_errors = AllowedErrors}.

src/jesse_validator_draft3.erl

Lines changed: 83 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
-include("jesse_schema_validator.hrl").
3232

3333
-type schema_error() :: ?wrong_type_dependency
34+
| ?schema_invalid
3435
| ?wrong_type_items.
3536

3637
-type schema_error_type() :: schema_error()
@@ -348,21 +349,14 @@ check_properties(Value, Properties, State) ->
348349
= lists:foldl( fun({PropertyName, PropertySchema}, CurrentState) ->
349350
case get_value(PropertyName, Value) of
350351
?not_found ->
351-
%% @doc 5.7. required
352-
%%
353-
%% This attribute indicates if the instance must have a value, and not
354-
%% be undefined. This is false by default, making the instance
355-
%% optional.
356-
%% @end
357-
case get_value(?REQUIRED, PropertySchema) of
358-
true ->
359-
handle_data_invalid( {?missing_required_property
360-
, PropertyName}
361-
, Value
362-
, CurrentState);
363-
_ ->
364-
CurrentState
365-
end;
352+
case get_value(?DEFAULT, PropertySchema) of
353+
?not_found -> check_required( PropertySchema
354+
, PropertyName
355+
, Value
356+
, CurrentState
357+
);
358+
Default -> check_default(PropertyName, PropertySchema, Default, CurrentState)
359+
end;
366360
Property ->
367361
NewState = set_current_schema( CurrentState
368362
, PropertySchema
@@ -583,6 +577,24 @@ check_items_fun(Tuples, State) ->
583577
),
584578
set_current_schema(TmpState, get_current_schema(State)).
585579

580+
581+
%% @doc 5.7. required
582+
%%
583+
%% This attribute indicates if the instance must have a value, and not
584+
%% be undefined. This is false by default, making the instance
585+
%% optional.
586+
%% @private
587+
check_required(PropertySchema, PropertyName, Value, CurrentState) ->
588+
case get_value(?REQUIRED, PropertySchema) of
589+
true ->
590+
handle_data_invalid( {?missing_required_property
591+
, PropertyName}
592+
, Value
593+
, CurrentState);
594+
_ ->
595+
CurrentState
596+
end.
597+
586598
%% @doc 5.8. dependencies
587599
%%
588600
%% This attribute is an object that defines the requirements of a
@@ -980,7 +992,11 @@ compare_properties(Value1, Value2) ->
980992
%% Wrappers
981993
%% @private
982994
get_value(Key, Schema) ->
983-
jesse_json_path:value(Key, Schema, ?not_found).
995+
get_value(Key, Schema, ?not_found).
996+
997+
%% @private
998+
get_value(Key, Schema, Default) ->
999+
jesse_json_path:value(Key, Schema, Default).
9841000

9851001
%% @private
9861002
unwrap(Value) ->
@@ -1027,3 +1043,54 @@ check_external_validation(Value, State) ->
10271043
undefined -> State;
10281044
Fun -> Fun(Value, State)
10291045
end.
1046+
1047+
%% @private
1048+
set_value(PropertyName, Value, State) ->
1049+
Path = lists:reverse([PropertyName] ++ jesse_state:get_current_path(State)),
1050+
jesse_state:set_value(State, Path, Value).
1051+
1052+
-define(types_for_defaults, [ ?STRING
1053+
, ?NUMBER
1054+
, ?INTEGER
1055+
, ?BOOLEAN
1056+
, ?OBJECT
1057+
]).
1058+
1059+
%% @private
1060+
check_default(PropertyName, PropertySchema, Default, State) ->
1061+
Type = get_value(?TYPE, PropertySchema, ?not_found),
1062+
case Type =/= ?not_found
1063+
andalso lists:member(Type, ?types_for_defaults)
1064+
andalso is_type_valid(Default, Type, State) of
1065+
false -> State;
1066+
true -> set_default(PropertyName, PropertySchema, Default, State)
1067+
end.
1068+
1069+
%% @private
1070+
set_default(PropertyName, PropertySchema, Default, State) ->
1071+
State1 = set_value(PropertyName, Default, State),
1072+
Schema = jesse_state:get_current_schema(State1),
1073+
case Schema =/= PropertySchema andalso validate_schema(Default, PropertySchema, State1) of
1074+
{true, State4} -> State4;
1075+
_ -> State
1076+
end.
1077+
1078+
%% @doc Validate a value against a schema in a given state.
1079+
%% Used by all combinators to run validation on a schema.
1080+
%% @private
1081+
validate_schema(Value, Schema, State0) ->
1082+
try
1083+
case jesse_lib:is_json_object(Schema) of
1084+
true ->
1085+
State1 = set_current_schema(State0, Schema),
1086+
State2 = jesse_schema_validator:validate_with_state( Schema
1087+
, Value
1088+
, State1
1089+
),
1090+
{true, State2};
1091+
false ->
1092+
handle_schema_invalid(?schema_invalid, State0)
1093+
end
1094+
catch
1095+
throw:Errors -> {false, Errors}
1096+
end.

src/jesse_validator_draft4.erl

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -377,17 +377,20 @@ check_properties(Value, Properties, State) ->
377377
TmpState
378378
= lists:foldl( fun({PropertyName, PropertySchema}, CurrentState) ->
379379
case get_value(PropertyName, Value) of
380-
?not_found ->
381-
CurrentState;
382-
Property ->
383-
NewState = set_current_schema( CurrentState
384-
, PropertySchema
385-
),
386-
check_value( PropertyName
387-
, Property
388-
, PropertySchema
389-
, NewState
390-
)
380+
?not_found ->
381+
case get_value(?DEFAULT, PropertySchema) of
382+
?not_found -> CurrentState;
383+
Default -> check_default(PropertyName, PropertySchema, Default, CurrentState)
384+
end;
385+
Property ->
386+
NewState = set_current_schema( CurrentState
387+
, PropertySchema
388+
),
389+
check_value( PropertyName
390+
, Property
391+
, PropertySchema
392+
, NewState
393+
)
391394
end
392395
end
393396
, State
@@ -1304,7 +1307,11 @@ compare_properties(Value1, Value2) ->
13041307
%% Wrappers
13051308
%% @private
13061309
get_value(Key, Schema) ->
1307-
jesse_json_path:value(Key, Schema, ?not_found).
1310+
get_value(Key, Schema, ?not_found).
1311+
1312+
%% @private
1313+
get_value(Key, Schema, Default) ->
1314+
jesse_json_path:value(Key, Schema, Default).
13081315

13091316
%% @private
13101317
unwrap(Value) ->
@@ -1359,3 +1366,33 @@ check_external_validation(Value, State) ->
13591366
undefined -> State;
13601367
Fun -> Fun(Value, State)
13611368
end.
1369+
1370+
%% @private
1371+
set_value(PropertyName, Value, State) ->
1372+
Path = lists:reverse([PropertyName] ++ jesse_state:get_current_path(State)),
1373+
jesse_state:set_value(State, Path, Value).
1374+
1375+
-define(types_for_defaults, [ ?STRING
1376+
, ?NUMBER
1377+
, ?INTEGER
1378+
, ?BOOLEAN
1379+
, ?OBJECT
1380+
]).
1381+
1382+
%% @private
1383+
check_default(PropertyName, PropertySchema, Default, State) ->
1384+
Type = get_value(?TYPE, PropertySchema, ?not_found),
1385+
case Type =/= ?not_found
1386+
andalso lists:member(Type, ?types_for_defaults)
1387+
andalso is_type_valid(Default, Type) of
1388+
false -> State;
1389+
true -> set_default(PropertyName, PropertySchema, Default, State)
1390+
end.
1391+
1392+
%% @private
1393+
set_default(PropertyName, PropertySchema, Default, State) ->
1394+
State1 = set_value(PropertyName, Default, State),
1395+
case validate_schema(Default, PropertySchema, State1) of
1396+
{true, State4} -> State4;
1397+
_ -> State
1398+
end.

test/jesse_schema_validator_tests.erl

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,74 @@
2121
-module(jesse_schema_validator_tests).
2222
-include_lib("eunit/include/eunit.hrl").
2323

24+
setter_test() ->
25+
Schema = {[
26+
{<<"type">>, <<"object">>},
27+
{<<"properties">>, {[
28+
{<<"bar">>, {[
29+
{<<"type">>, <<"string">>},
30+
{<<"minLength">>, 4},
31+
{<<"default">>, <<"awesome">>}
32+
]}}
33+
]}}
34+
]},
35+
36+
Default = {[{<<"bar">>, <<"awesome">>}]},
37+
Value = {[]},
38+
Fun = fun([K], V, {L1}) ->
39+
{[{K, V} | proplists:delete(K, L1)]}
40+
end,
41+
Options = [{setter_fun, Fun}],
42+
43+
[ ?assertEqual({ok, Value}
44+
,jesse_schema_validator:validate(Schema, Value, [])
45+
)
46+
, ?assertEqual({ok, Default}
47+
,jesse_schema_validator:validate(Schema, Value, Options)
48+
)
49+
].
50+
51+
invalid_default_test() ->
52+
BadSchema = {[
53+
{<<"type">>, <<"object">>},
54+
{<<"properties">>, {[
55+
{<<"bar">>, {[
56+
{<<"type">>, <<"string">>},
57+
{<<"minLength">>, 4},
58+
{<<"default">>, <<"bad">>}
59+
]}}
60+
]}}
61+
]},
62+
63+
GoodSchema = {[
64+
{<<"type">>, <<"object">>},
65+
{<<"properties">>, {[
66+
{<<"bar">>, {[
67+
{<<"type">>, <<"string">>},
68+
{<<"minLength">>, 4},
69+
{<<"default">>, <<"awesome">>}
70+
]}}
71+
]}}
72+
]},
73+
74+
WithDefault = {[{<<"bar">>, <<"good">>}]},
75+
WithoutDefault = {[]},
76+
77+
?assertEqual(
78+
{ok, WithoutDefault},
79+
jesse_schema_validator:validate(BadSchema, WithoutDefault, [])
80+
),
81+
82+
?assertEqual(
83+
{ok, WithDefault},
84+
jesse_schema_validator:validate(BadSchema, WithDefault, [])
85+
),
86+
87+
?assertEqual(
88+
{ok, WithoutDefault},
89+
jesse_schema_validator:validate(GoodSchema, WithoutDefault, [])
90+
).
91+
2492
data_invalid_test() ->
2593
IntegerSchema = {[{<<"type">>, <<"integer">>}]},
2694

0 commit comments

Comments
 (0)