-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
A bit different approach to external validators plus validation against definition in a schema #53
base: master
Are you sure you want to change the base?
Changes from 6 commits
e21065f
c774499
7ef3873
e2ce63d
9ba3702
3910592
0dffb4a
723e835
7640f47
f583a3b
a21da06
600cc83
5bb91aa
2a0fa89
287efa0
ba495bc
bbb58fc
9b980b7
0bef37c
0953543
edf44ce
fd4eee8
df03b5c
6ee802a
f4ff58e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,3 +15,4 @@ Andrei Neculau <[email protected]> | |
Stefan Strigler <[email protected]> | ||
Sergey Prokhorov <[email protected]> | ||
Yakov <[email protected]> | ||
Anton Belyaev <[email protected]> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,12 +25,29 @@ | |
|
||
%% API | ||
-export([ validate/3 | ||
, validate_definition/4 | ||
, validate_with_state/3 | ||
]). | ||
|
||
%% Includes | ||
-include("jesse_schema_validator.hrl"). | ||
|
||
%% Behaviour definition | ||
-callback check_value(Value, Attr, State) -> | ||
State | no_return() | ||
when | ||
Value :: any(), | ||
Attr :: {binary(), jesse:json_term()}, | ||
State :: jesse_state:state(). | ||
|
||
-callback init_state(Opts :: jesse_state:validator_opts()) -> | ||
validator_state(). | ||
|
||
-type validator_state() :: any(). | ||
|
||
-export_type([ validator_state/0 | ||
]). | ||
|
||
%%% API | ||
%% @doc Validates json `Data' against `JsonSchema' with `Options'. | ||
%% If the given json is valid, then it is returned to the caller as is, | ||
|
@@ -45,6 +62,21 @@ validate(JsonSchema, Value, Options) -> | |
NewState = validate_with_state(JsonSchema, Value, State), | ||
{result(NewState), Value}. | ||
|
||
%% @doc Validates json `Data' against `Definition' in `JsonSchema' with `Options'. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to Elvis:
|
||
%% If the given json is valid, then it is returned to the caller as is, | ||
%% otherwise an exception will be thrown. | ||
-spec validate_definition( Definition :: string() | ||
, JsonSchema :: jesse:json_term() | ||
, Data :: jesse:json_term() | ||
, Options :: [{Key :: atom(), Data :: any()}] | ||
) -> {ok, jesse:json_term()} | ||
| no_return(). | ||
validate_definition(Defintion, JsonSchema, Value, Options) -> | ||
State = jesse_state:new(JsonSchema, Options), | ||
Schema = make_definition_ref(Defintion), | ||
NewState = validate_with_state(Schema, Value, State), | ||
{result(NewState), Value}. | ||
|
||
%% @doc Validates json `Data' against `JsonSchema' with `State'. | ||
%% If the given json is valid, then the latest state is returned to the caller, | ||
%% otherwise an exception will be thrown. | ||
|
@@ -53,11 +85,31 @@ validate(JsonSchema, Value, Options) -> | |
, State :: jesse_state:state() | ||
) -> jesse_state:state() | ||
| no_return(). | ||
validate_with_state(JsonSchema, Value, State) -> | ||
SchemaVer = get_schema_ver(JsonSchema, State), | ||
select_and_run_validator(SchemaVer, JsonSchema, Value, State). | ||
validate_with_state(JsonSchema0, Value, State) -> | ||
Validator = select_validator(JsonSchema0, State), | ||
JsonSchema = jesse_json_path:unwrap_value(JsonSchema0), | ||
run_validator(Validator, Value, JsonSchema, State). | ||
|
||
|
||
%%% Internal functions | ||
%% @doc Gets validator from the state or else | ||
%% selects an appropriate one by schema version. | ||
%% @private | ||
select_validator(JsonSchema, State) -> | ||
case jesse_state:get_validator(State) of | ||
undefined -> | ||
select_validator_by_schema(get_schema_ver(JsonSchema, State), State); | ||
Validator -> | ||
Validator | ||
end. | ||
|
||
select_validator_by_schema(?json_schema_draft3, _) -> | ||
jesse_validator_draft3; | ||
select_validator_by_schema(?json_schema_draft4, _) -> | ||
jesse_validator_draft4; | ||
select_validator_by_schema(SchemaURI, State) -> | ||
jesse_error:handle_schema_invalid({?schema_unsupported, SchemaURI}, State). | ||
|
||
%% @doc Returns "$schema" property from `JsonSchema' if it is present, | ||
%% otherwise the default schema version from `State' is returned. | ||
%% @private | ||
|
@@ -76,18 +128,21 @@ result(State) -> | |
_ -> throw(ErrorList) | ||
end. | ||
|
||
%% @doc Runs appropriate validator depending on schema version | ||
%% it is called with. | ||
%% @doc Goes through attributes of the given `JsonSchema' and | ||
%% validates the `Value' against them calling `Validator'. | ||
%% @private | ||
select_and_run_validator(?json_schema_draft3, JsonSchema, Value, State) -> | ||
jesse_validator_draft3:check_value( Value | ||
, jesse_json_path:unwrap_value(JsonSchema) | ||
, State | ||
); | ||
select_and_run_validator(?json_schema_draft4, JsonSchema, Value, State) -> | ||
jesse_validator_draft4:check_value( Value | ||
, jesse_json_path:unwrap_value(JsonSchema) | ||
, State | ||
); | ||
select_and_run_validator(SchemaURI, _JsonSchema, _Value, State) -> | ||
jesse_error:handle_schema_invalid({?schema_unsupported, SchemaURI}, State). | ||
run_validator(_Validator, _Value, [], State) -> | ||
State; | ||
run_validator(Validator, Value, [Attr | Attrs], State0) -> | ||
State = Validator:check_value( Value | ||
, Attr | ||
, State0 | ||
), | ||
run_validator(Validator, Value, Attrs, State). | ||
|
||
%% @doc Makes a $ref schema object pointing to the given `Definition' | ||
%% in schema defintions. | ||
%% @private | ||
make_definition_ref(Definition) -> | ||
Definition1 = list_to_binary(Definition), | ||
[{<<"$ref">>, <<"#/definitions/", Definition1/binary>>}]. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,26 +30,32 @@ | |
, get_current_schema/1 | ||
, get_current_schema_id/1 | ||
, get_default_schema_ver/1 | ||
, get_validator/1 | ||
, get_validator_state/1 | ||
, get_error_handler/1 | ||
, get_error_list/1 | ||
, new/2 | ||
, remove_last_from_path/1 | ||
, set_allowed_errors/2 | ||
, set_current_schema/2 | ||
, set_error_list/2 | ||
, set_validator_state/2 | ||
, resolve_ref/2 | ||
, undo_resolve_ref/2 | ||
, canonical_path/2 | ||
, combine_id/2 | ||
]). | ||
|
||
-export_type([ state/0 | ||
, validator_opts/0 | ||
]). | ||
|
||
%% Includes | ||
-include("jesse_schema_validator.hrl"). | ||
|
||
%% Internal datastructures | ||
-type http_uri() :: string(). | ||
|
||
-record( state | ||
, { root_schema :: jesse:json_term() | ||
, current_schema :: jesse:json_term() | ||
|
@@ -62,18 +68,22 @@ | |
, non_neg_integer() | ||
) -> list() | no_return() | ||
) | ||
, validator :: module() | 'undefined' | ||
, validator_state :: any() | 'undefined' | ||
, default_schema_ver :: binary() | ||
, schema_loader_fun :: fun(( string() | ||
) -> {ok, jesse:json_term()} | | ||
jesse:json_term() | | ||
?not_found | ||
) | ||
, id :: http_uri:uri() | 'undefined' | ||
, id :: http_uri() | 'undefined' | ||
} | ||
). | ||
|
||
-opaque state() :: #state{}. | ||
|
||
-type validator_opts() :: any(). | ||
|
||
%%% API | ||
%% @doc Adds `Property' to the `current_path' in `State'. | ||
-spec add_to_path(State :: state(), | ||
|
@@ -110,6 +120,16 @@ get_current_schema_id(#state{ current_schema = CurrentSchema | |
get_default_schema_ver(#state{default_schema_ver = SchemaVer}) -> | ||
SchemaVer. | ||
|
||
%% @doc Getter for `validator'. | ||
-spec get_validator(State :: state()) -> module() | undefined. | ||
get_validator(#state{validator = Validator}) -> | ||
Validator. | ||
|
||
%% @doc Getter for `validator_state'. | ||
-spec get_validator_state(State :: state()) -> any() | undefined. | ||
get_validator_state(#state{validator_state = ValidatorState}) -> | ||
ValidatorState. | ||
|
||
%% @doc Getter for `error_handler'. | ||
-spec get_error_handler(State :: state()) -> fun(( jesse_error:error_reason() | ||
, [jesse_error:error_reason()] | ||
|
@@ -144,15 +164,28 @@ new(JsonSchema, Options) -> | |
, Options | ||
, MetaSchemaVer | ||
), | ||
LoaderFun = proplists:get_value( schema_loader_fun | ||
, Options | ||
, ?default_schema_loader_fun | ||
), | ||
Validator = proplists:get_value( validator | ||
, Options | ||
, undefined | ||
), | ||
ValidatorOpts = proplists:get_value( validator_opts | ||
, Options | ||
, undefined | ||
), | ||
ValidatorState = init_validator_state( Validator | ||
, ValidatorOpts | ||
), | ||
LoaderFun = proplists:get_value( schema_loader_fun | ||
, Options | ||
, ?default_schema_loader_fun | ||
), | ||
NewState = #state{ root_schema = JsonSchema | ||
, current_path = [] | ||
, allowed_errors = AllowedErrors | ||
, error_list = [] | ||
, error_handler = ErrorHandler | ||
, validator = Validator | ||
, validator_state = ValidatorState | ||
, default_schema_ver = DefaultSchemaVer | ||
, schema_loader_fun = LoaderFun | ||
}, | ||
|
@@ -183,6 +216,11 @@ set_current_schema(#state{id = Id} = State, NewSchema) -> | |
set_error_list(State, ErrorList) -> | ||
State#state{error_list = ErrorList}. | ||
|
||
%% @doc Setter for `validator_state'. | ||
-spec set_validator_state(State :: state(), ValidatorState :: any()) -> state(). | ||
set_validator_state(State, ValidatorState) -> | ||
State#state{validator_state = ValidatorState}. | ||
|
||
%% @doc Resolve a reference. | ||
-spec resolve_ref(State :: state(), Reference :: binary()) -> state(). | ||
resolve_ref(State, Reference) -> | ||
|
@@ -238,6 +276,16 @@ undo_resolve_ref(RefState, OriginalState) -> | |
, id = OriginalState#state.id | ||
}. | ||
|
||
%% @doc Init custom validator state. | ||
%% @private | ||
-spec init_validator_state( Validator :: module() | undefined | ||
, Opts :: validator_opts() | ||
) -> jesse_schema_validator:validator_state(). | ||
init_validator_state(undefined, _) -> | ||
undefined; | ||
init_validator_state(Validator, Opts) -> | ||
Validator:init_state(Opts). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to Elvis:
|
||
|
||
%% @doc Retrieve a specific part of a schema | ||
%% @private | ||
-spec load_local_schema( Schema :: not_found | jesse:json_term() | ||
|
@@ -277,8 +325,8 @@ load_local_schema(Schema, [Key | Keys]) -> | |
|
||
%% @doc Resolve a new id | ||
%% @private | ||
-spec combine_id(undefined | http_uri:uri(), | ||
undefined | binary()) -> http_uri:uri(). | ||
-spec combine_id(undefined | http_uri(), | ||
undefined | binary()) -> http_uri(). | ||
combine_id(Id, undefined) -> | ||
Id; | ||
combine_id(Id, RefBin) -> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to Elvis: