Current section

Files

Jump to
jesse src jesse.erl
Raw

src/jesse.erl

%%%=============================================================================
%% Copyright 2012- Klarna AB
%% Copyright 2015- AUTHORS
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% @doc JESSE (JSon Schema Erlang)
%%
%% This is an interface module which provides an access to the main
%% functionality of jesse, such as 1) updating of the schema definitions cache;
%% 2) validation json data against a schema.
%% @end
%%%=============================================================================
-module(jesse).
%% API
-export([ main/1
, add_schema/2
, add_schema/3
, del_schema/1
, load_schemas/2
, load_schemas/4
, validate/2
, validate/3
, validate_with_schema/2
, validate_with_schema/3
]).
-export_type([ json_term/0
]).
-type json_term() :: term().
%%% API
%% @doc Run from CLI with arguments.
-spec main([string()]) -> ok.
main(Args) ->
jesse_cli:main(Args).
%% @doc Adds a schema definition `Schema' to in-memory storage associated with
%% a key `Key'. It will overwrite an existing schema with the same key if
%% there is any.
-spec add_schema(Key :: any(), Schema :: json_term()) ->
ok | jesse_error:error().
add_schema(Key, Schema) ->
ValidationFun = fun jesse_lib:is_json_object/1,
MakeKeyFun = fun(_) -> Key end,
jesse_database:add(Schema, ValidationFun, MakeKeyFun).
%% @doc Equivalent to `add_schema/2', but `Schema' is a binary string, and
%% the third agument is a parse function to convert the binary string to
%% a supported internal representation of json.
-spec add_schema( Key :: any()
, Schema :: binary()
, Options :: [{Key :: atom(), Data :: any()}]
) -> ok | jesse_error:error().
add_schema(Key, Schema, Options) ->
try
ParserFun = proplists:get_value(parser_fun, Options, fun(X) -> X end),
ParsedSchema = try_parse(schema, ParserFun, Schema),
add_schema(Key, ParsedSchema)
catch
throw:Error -> {error, Error}
end.
%% @doc Deletes a schema definition from in-memory storage associated with
%% the key `Key'.
-spec del_schema(Key :: any()) -> ok.
del_schema(Key) ->
jesse_database:delete(Key).
%% @doc Loads schema definitions from filesystem to in-memory storage.
%%
%% Equivalent to `load_schemas(Path, ParserFun, ValidationFun, MakeKeyFun)'
%% where `ValidationFun' is `fun jesse_json:is_json_object/1' and
%% `MakeKeyFun' is `fun jesse_lib:get_schema_id/1'. In this case
%% the key will be the value of `id' attribute from the given schemas.
-spec load_schemas( Path :: string()
, ParserFun :: fun((binary()) -> json_term())
) -> jesse_database:update_result().
load_schemas(Path, ParserFun) ->
load_schemas( Path
, ParserFun
, fun jesse_lib:is_json_object/1
, fun jesse_lib:get_schema_id/1
).
%% @doc Loads schema definitions from filesystem to in-memory storage.
%% The function loads all the files from directory `Path', then each schema
%% entry will be checked for a validity by function `ValidationFun', and
%% will be stored in in-memory storage with a key returned by `MakeKeyFun'
%% function.
%%
%% In addition to a schema definition, a timestamp of the schema file will be
%% stored, so, during the next update timestamps will be compared to avoid
%% unnecessary updates.
%%
%% Schema definitions are stored in the format which json parsing function
%% `ParserFun' returns.
%%
%% NOTE: it's impossible to automatically update schema definitions added by
%% add_schema/2, the only way to update them is to use add_schema/2
%% again with the new definition.
-spec load_schemas( Path :: string()
, ParserFun :: fun((binary()) -> json_term())
, ValidationFun :: fun((any()) -> boolean())
, MakeKeyFun :: fun((json_term()) -> any())
) -> jesse_database:update_result().
load_schemas(Path, ParserFun, ValidationFun, MakeKeyFun) ->
jesse_database:update(Path, ParserFun, ValidationFun, MakeKeyFun).
%% @doc Equivalent to {@link validate/3} where `Options' is an empty list.
-spec validate( Schema :: any()
, Data :: json_term() | binary()
) -> {ok, json_term()}
| jesse_error:error().
validate(Schema, Data) ->
validate(Schema, Data, []).
%% @doc Validates json `Data' against a schema with the same key as `Schema'
%% in the internal storage, using `Options'. If the given json is valid,
%% then it is returned to the caller, otherwise an error with an appropriate
%% error reason is returned. If the `parser_fun' option is provided, then
%% `Data' is considered to be a binary string, so `parser_fun' is used
%% to convert the binary string to a supported internal representation of json.
%% If `parser_fun' is not provided, then `Data' is considered to already be a
%% supported internal representation of json.
-spec validate( Schema :: any()
, Data :: json_term() | binary()
, Options :: [{Key :: atom(), Data :: any()}]
) -> {ok, json_term()}
| jesse_error:error().
validate(Schema, Data, Options) ->
try
ParserFun = proplists:get_value(parser_fun, Options, fun(X) -> X end),
ParsedData = try_parse(data, ParserFun, Data),
JsonSchema = jesse_database:read(Schema),
jesse_schema_validator:validate(JsonSchema, ParsedData, Options)
catch
throw:Error -> {error, Error}
end.
%% @doc Equivalent to {@link validate_with_schema/3} where `Options'
%% is an empty list.
-spec validate_with_schema( Schema :: json_term() | binary()
, Data :: json_term() | binary()
) -> {ok, json_term()}
| jesse_error:error().
validate_with_schema(Schema, Data) ->
validate_with_schema(Schema, Data, []).
%% @doc Validates json `Data' agains the given schema `Schema', using `Options'.
%% If the given json is valid, then it is returned to the caller, otherwise
%% an error with an appropriate error reason is returned. If the `parser_fun'
%% option is provided, then both `Schema' and `Data' are considered to be a
%% binary string, so `parser_fun' is used to convert both binary strings to a
%% supported internal representation of json.
%% If `parser_fun' is not provided, then both `Schema' and `Data' are considered
%% to already be a supported internal representation of json.
-spec validate_with_schema( Schema :: json_term() | binary()
, Data :: json_term() | binary()
, Options :: [{Key :: atom(), Data :: any()}]
) -> {ok, json_term()}
| jesse_error:error().
validate_with_schema(Schema, Data, Options) ->
try
ParserFun = proplists:get_value(parser_fun, Options, fun(X) -> X end),
ParsedSchema = try_parse(schema, ParserFun, Schema),
ParsedData = try_parse(data, ParserFun, Data),
jesse_schema_validator:validate(ParsedSchema, ParsedData, Options)
catch
throw:Error -> {error, Error}
end.
%%% Internal functions
%% @doc Wraps up calls to a third party json parser.
%% @private
try_parse(Type, ParserFun, JsonBin) ->
try
ParserFun(JsonBin)
catch
_:Error ->
case Type of
data -> throw({data_error, {parse_error, Error}});
schema -> throw({schema_error, {parse_error, Error}})
end
end.