From bdcdab1c4939a784c7923ba0867228c2ff0add91 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Tue, 27 Aug 2024 21:20:53 +0200 Subject: [PATCH 1/6] Remove unneeded dependency, OTP covers since 21.0 --- rebar.config | 1 - src/jesse.app.src | 1 - src/jesse_validator_draft4.erl | 8 +++----- src/jesse_validator_draft6.erl | 8 +++----- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/rebar.config b/rebar.config index e9d7f12b..0e727645 100644 --- a/rebar.config +++ b/rebar.config @@ -20,7 +20,6 @@ ]}. { deps , [ {jsx, "3.1.0"} - , {rfc3339, "0.9.0"} ]}. { project_plugins diff --git a/src/jesse.app.src b/src/jesse.app.src index 6c689e97..1749e092 100644 --- a/src/jesse.app.src +++ b/src/jesse.app.src @@ -12,7 +12,6 @@ , ssl , inets , jsx - , rfc3339 ]} , {env, [ {re_options, [unicode, ucp]} ]} diff --git a/src/jesse_validator_draft4.erl b/src/jesse_validator_draft4.erl index cc6d0745..4fcb1790 100644 --- a/src/jesse_validator_draft4.erl +++ b/src/jesse_validator_draft4.erl @@ -1336,11 +1336,9 @@ remove_last_from_path(State) -> %% @private valid_datetime(DateTimeBin) -> - case rfc3339:parse(DateTimeBin) of - {ok, _} -> - true; - _ -> - false + try calendar:rfc3339_to_system_time(binary_to_list(DateTimeBin)) of + _ -> true + catch error:_Error -> false end. maybe_external_check_value(Value, State) -> diff --git a/src/jesse_validator_draft6.erl b/src/jesse_validator_draft6.erl index e43d1616..dcd84830 100644 --- a/src/jesse_validator_draft6.erl +++ b/src/jesse_validator_draft6.erl @@ -1286,11 +1286,9 @@ remove_last_from_path(State) -> %% @private valid_datetime(DateTimeBin) -> - case rfc3339:parse(DateTimeBin) of - {ok, _} -> - true; - _ -> - false + try calendar:rfc3339_to_system_time(binary_to_list(DateTimeBin)) of + _ -> true + catch error:_Error -> false end. maybe_external_check_value(Value, State) -> From 72a1da98dd85e239419c837641c9584eca0a6035 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Wed, 28 Aug 2024 07:50:56 +0200 Subject: [PATCH 2/6] Remain backwards-compatible with older OTP --- rebar.config.script | 12 ++++++++++++ src/jesse_validator_draft4.erl | 12 ++++++++++++ src/jesse_validator_draft6.erl | 12 ++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 rebar.config.script diff --git a/rebar.config.script b/rebar.config.script new file mode 100644 index 00000000..b2658c6a --- /dev/null +++ b/rebar.config.script @@ -0,0 +1,12 @@ +{Deps0, Config0} = case lists:keytake(deps, 1, CONFIG) of + false -> {[], CONFIG}; + {value, {deps, D}, Cfg} -> {D, Cfg} + end, + +Deps = case list_to_integer(erlang:system_info(otp_release)) of + N when N >= 21 -> + []; + _ -> + [{rfc3339, "0.9.0"}] + end, +[{deps, Deps ++ Deps0} | Config0]. diff --git a/src/jesse_validator_draft4.erl b/src/jesse_validator_draft4.erl index 4fcb1790..98d25d52 100644 --- a/src/jesse_validator_draft4.erl +++ b/src/jesse_validator_draft4.erl @@ -1335,11 +1335,23 @@ remove_last_from_path(State) -> jesse_state:remove_last_from_path(State). %% @private +-ifdef(OTP_RELEASE). +%% OTP 21 or higher valid_datetime(DateTimeBin) -> try calendar:rfc3339_to_system_time(binary_to_list(DateTimeBin)) of _ -> true catch error:_Error -> false end. +-else. +%% OTP 20 or lower. +valid_datetime(DateTimeBin) -> + case rfc3339:parse(DateTimeBin) of + {ok, _} -> + true; + _ -> + false + end. +-endif. maybe_external_check_value(Value, State) -> case jesse_state:get_external_validator(State) of diff --git a/src/jesse_validator_draft6.erl b/src/jesse_validator_draft6.erl index dcd84830..a54103c9 100644 --- a/src/jesse_validator_draft6.erl +++ b/src/jesse_validator_draft6.erl @@ -1285,11 +1285,23 @@ remove_last_from_path(State) -> jesse_state:remove_last_from_path(State). %% @private +-ifdef(OTP_RELEASE). +%% OTP 21 or higher valid_datetime(DateTimeBin) -> try calendar:rfc3339_to_system_time(binary_to_list(DateTimeBin)) of _ -> true catch error:_Error -> false end. +-else. +%% OTP 20 or lower. +valid_datetime(DateTimeBin) -> + case rfc3339:parse(DateTimeBin) of + {ok, _} -> + true; + _ -> + false + end. +-endif. maybe_external_check_value(Value, State) -> case jesse_state:get_external_validator(State) of From 9a39bf2ccf07ecccbb8d1912f7a9137baa0e0051 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Wed, 28 Aug 2024 07:31:19 +0200 Subject: [PATCH 3/6] Use json from OTP27 and jsx optionally otherwise --- rebar.config | 6 +----- rebar.config.script | 7 +++++-- src/jesse.app.src | 1 - src/jesse_cli.erl | 6 +++--- src/jesse_database.erl | 4 ++-- src/jesse_error.erl | 10 ++-------- src/jesse_schema_validator.hrl | 14 ++++++++++++++ 7 files changed, 27 insertions(+), 21 deletions(-) diff --git a/rebar.config b/rebar.config index 0e727645..3db29c6b 100644 --- a/rebar.config +++ b/rebar.config @@ -1,5 +1,5 @@ %%-*- mode: erlang -*- -{profiles, [{test, [{deps, [{ proper, "1.4.0"}]}]}]}. +{profiles, [{test, [{deps, [{jsx, "3.1.0"}, {proper, "1.4.0"}]}]}]}. {erl_opts, [ {platform_define, "^R[0-9]+", erlang_deprecated_types} %% , warn_export_all , warn_export_vars @@ -18,10 +18,6 @@ , undefined_function_calls , deprecated_function_calls ]}. -{ deps -, [ {jsx, "3.1.0"} - ]}. - { project_plugins , [ {rebar3_lint, "3.2.6"} , {rebar3_proper, "0.12.1"} diff --git a/rebar.config.script b/rebar.config.script index b2658c6a..8f2cec39 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -4,9 +4,12 @@ end, Deps = case list_to_integer(erlang:system_info(otp_release)) of - N when N >= 21 -> + N when N >= 27 -> []; + N when N >= 21 -> + [{jsx, "3.1.0"}]; _ -> - [{rfc3339, "0.9.0"}] + [{jsx, "3.1.0"}, {rfc3339, "0.9.0"}] end, + [{deps, Deps ++ Deps0} | Config0]. diff --git a/src/jesse.app.src b/src/jesse.app.src index 1749e092..6aaca89b 100644 --- a/src/jesse.app.src +++ b/src/jesse.app.src @@ -11,7 +11,6 @@ , public_key , ssl , inets - , jsx ]} , {env, [ {re_options, [unicode, ucp]} ]} diff --git a/src/jesse_cli.erl b/src/jesse_cli.erl index 9a0917cd..7162a270 100644 --- a/src/jesse_cli.erl +++ b/src/jesse_cli.erl @@ -75,7 +75,7 @@ run(Options, [Schema|_] = Schemata, [JsonInstance|JsonInstances]) -> undefined -> io:fwrite("~p\n\n", [Result]); true -> - io:fwrite("~s\n\n", [jsx:encode(Result)]) + io:fwrite("~s\n\n", [?JSON:encode(Result)]) end, case JesseResult of {ok, _} -> @@ -90,7 +90,7 @@ jesse_run(JsonInstance, Schema, Schemata) -> {ok, _} = application:ensure_all_started(jesse), ok = add_schemata(Schemata), {ok, JsonInstanceBinary} = file:read_file(JsonInstance), - JsonInstanceJsx = jsx:decode(JsonInstanceBinary, [{return_maps, false}]), + JsonInstanceJsx = ?JSON:decode(JsonInstanceBinary), jesse:validate( Schema , JsonInstanceJsx ). @@ -99,7 +99,7 @@ add_schemata([]) -> ok; add_schemata([SchemaFile|Rest]) -> {ok, SchemaBin} = file:read_file(SchemaFile), - Schema0 = jsx:decode(SchemaBin, [{return_maps, false}]), + Schema0 = ?JSON:decode(SchemaBin), Schema = maybe_fill_schema_id(SchemaFile, Schema0), ok = jesse:add_schema(SchemaFile, Schema), add_schemata(Rest). diff --git a/src/jesse_database.erl b/src/jesse_database.erl index f4a3e941..a11a4585 100644 --- a/src/jesse_database.erl +++ b/src/jesse_database.erl @@ -307,7 +307,7 @@ add_file_uri(Key0) -> "file://" ++ File = Key, {ok, SchemaBin} = file:read_file(File), {ok, #file_info{mtime = Mtime}} = file:read_file_info(File), - Schema = jsx:decode(SchemaBin, [{return_maps, false}]), + Schema = ?JSON:decode(SchemaBin), SchemaInfos = [{Key, Mtime, Schema}], ValidationFun = fun jesse_lib:is_json_object/1, store_schemas(SchemaInfos, ValidationFun). @@ -321,7 +321,7 @@ add_http_uri(Key0) -> , HttpOptions , [{body_format, binary}]), {{_Line, 200, _}, Headers, SchemaBin} = Response, - Schema = jsx:decode(SchemaBin, [{return_maps, false}]), + Schema = ?JSON:decode(SchemaBin), SchemaInfos = [{Key, get_http_mtime(Headers), Schema}], ValidationFun = fun jesse_lib:is_json_object/1, store_schemas(SchemaInfos, ValidationFun). diff --git a/src/jesse_error.erl b/src/jesse_error.erl index 32c12a8b..ebabf6e5 100644 --- a/src/jesse_error.erl +++ b/src/jesse_error.erl @@ -27,7 +27,6 @@ , handle_data_invalid/3 , handle_schema_invalid/2 , to_json/1 - , to_json/2 , reason_to_jsx/1 ]). @@ -105,14 +104,9 @@ handle_schema_invalid(Info, State) -> %% @doc Convert an error to a JSON string using jsx -spec to_json(Error :: error()) -> binary(). -to_json(Error) -> - to_json(Error, [indent]). - -%% @doc Convert an error to a JSON string using jsx --spec to_json(Error :: error(), JsxOptions :: [atom()]) -> binary(). -to_json({error, Reasons}, JsxOptions) -> +to_json({error, Reasons}) -> JsxReasons = lists:map(fun reason_to_jsx/1, Reasons), - jsx:encode([{reasons, JsxReasons}], JsxOptions). + ?JSON:encode([{reasons, JsxReasons}]). %% @doc Convert an error reason to jsx structs -spec reason_to_jsx(Reason :: error_reason()) -> jesse:json_term(). diff --git a/src/jesse_schema_validator.hrl b/src/jesse_schema_validator.hrl index 4ebcd5c1..db77c3b6 100644 --- a/src/jesse_schema_validator.hrl +++ b/src/jesse_schema_validator.hrl @@ -28,6 +28,20 @@ -define(IF_MAPS(Exp), Exp). -endif. +%% Use new json library if available +-ifdef(OTP_RELEASE). + -if(?OTP_RELEASE >= 27). + %% OTP 27 or higher + -define(JSON, json). + -else. + %% OTP 26 to 21. + -define(JSON, jsx). + -endif. +-else. + %% OTP 20 or lower. + -define(JSON, jsx). +-endif. + %% Use optimization for sets if available -ifdef(OTP_RELEASE). -if(?OTP_RELEASE >= 24). From 9e8fa791080e3fddfb9a6cd4ceea7f0ffc3bd4cc Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Thu, 29 Aug 2024 21:48:53 +0200 Subject: [PATCH 4/6] Optionally include jsx and rfc3339 in releases --- rebar.config.script | 8 ++++---- src/jesse.app.src.script | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 src/jesse.app.src.script diff --git a/rebar.config.script b/rebar.config.script index 8f2cec39..27f6df24 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -5,11 +5,11 @@ Deps = case list_to_integer(erlang:system_info(otp_release)) of N when N >= 27 -> - []; + Deps0; N when N >= 21 -> - [{jsx, "3.1.0"}]; + [{jsx, "3.1.0"} | Deps0]; _ -> - [{jsx, "3.1.0"}, {rfc3339, "0.9.0"}] + [{jsx, "3.1.0"}, {rfc3339, "0.9.0"} | Deps0] end, -[{deps, Deps ++ Deps0} | Config0]. +[{deps, Deps} | Config0]. diff --git a/src/jesse.app.src.script b/src/jesse.app.src.script new file mode 100644 index 00000000..520b4468 --- /dev/null +++ b/src/jesse.app.src.script @@ -0,0 +1,19 @@ +[{application, jesse, Config}] = CONFIG, + +{Apps0, Config0} = case lists:keytake(applications, 1, Config) of + false -> + {[], CONFIG}; + {value, {applications, A}, Cfg} -> + {A, Cfg} + end, + +Apps = case list_to_integer(erlang:system_info(otp_release)) of + N when N >= 27 -> + Apps0; + N when N >= 21 -> + [jsx | Apps0]; + _ -> + [jsx, rfc3339 | Apps0] + end, + +[{application, jesse, [{applications, Apps} | Config]}]. From 8b0daa24d12441e7db70dcebc3c3a36d8fe627ee Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Thu, 29 Aug 2024 23:00:22 +0200 Subject: [PATCH 5/6] Encapsulate json module (elvis error) --- src/jesse_cli.erl | 7 ++++--- src/jesse_database.erl | 4 ++-- src/jesse_error.erl | 2 +- src/jesse_lib.erl | 25 +++++++++++++++++++++++++ src/jesse_schema_validator.hrl | 14 -------------- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/jesse_cli.erl b/src/jesse_cli.erl index 7162a270..d8070845 100644 --- a/src/jesse_cli.erl +++ b/src/jesse_cli.erl @@ -75,7 +75,7 @@ run(Options, [Schema|_] = Schemata, [JsonInstance|JsonInstances]) -> undefined -> io:fwrite("~p\n\n", [Result]); true -> - io:fwrite("~s\n\n", [?JSON:encode(Result)]) + io:fwrite("~s\n\n", [jesse_lib:json_encode(Result)]) end, case JesseResult of {ok, _} -> @@ -86,11 +86,12 @@ run(Options, [Schema|_] = Schemata, [JsonInstance|JsonInstances]) -> halt(1) end. + jesse_run(JsonInstance, Schema, Schemata) -> {ok, _} = application:ensure_all_started(jesse), ok = add_schemata(Schemata), {ok, JsonInstanceBinary} = file:read_file(JsonInstance), - JsonInstanceJsx = ?JSON:decode(JsonInstanceBinary), + JsonInstanceJsx = jesse_lib:json_decode(JsonInstanceBinary), jesse:validate( Schema , JsonInstanceJsx ). @@ -99,7 +100,7 @@ add_schemata([]) -> ok; add_schemata([SchemaFile|Rest]) -> {ok, SchemaBin} = file:read_file(SchemaFile), - Schema0 = ?JSON:decode(SchemaBin), + Schema0 = jesse_lib:json_decode(SchemaBin), Schema = maybe_fill_schema_id(SchemaFile, Schema0), ok = jesse:add_schema(SchemaFile, Schema), add_schemata(Rest). diff --git a/src/jesse_database.erl b/src/jesse_database.erl index a11a4585..bec09f15 100644 --- a/src/jesse_database.erl +++ b/src/jesse_database.erl @@ -307,7 +307,7 @@ add_file_uri(Key0) -> "file://" ++ File = Key, {ok, SchemaBin} = file:read_file(File), {ok, #file_info{mtime = Mtime}} = file:read_file_info(File), - Schema = ?JSON:decode(SchemaBin), + Schema = jesse_lib:json_decode(SchemaBin), SchemaInfos = [{Key, Mtime, Schema}], ValidationFun = fun jesse_lib:is_json_object/1, store_schemas(SchemaInfos, ValidationFun). @@ -321,7 +321,7 @@ add_http_uri(Key0) -> , HttpOptions , [{body_format, binary}]), {{_Line, 200, _}, Headers, SchemaBin} = Response, - Schema = ?JSON:decode(SchemaBin), + Schema = jesse_lib:json_decode(SchemaBin), SchemaInfos = [{Key, get_http_mtime(Headers), Schema}], ValidationFun = fun jesse_lib:is_json_object/1, store_schemas(SchemaInfos, ValidationFun). diff --git a/src/jesse_error.erl b/src/jesse_error.erl index ebabf6e5..c756b2a6 100644 --- a/src/jesse_error.erl +++ b/src/jesse_error.erl @@ -106,7 +106,7 @@ handle_schema_invalid(Info, State) -> -spec to_json(Error :: error()) -> binary(). to_json({error, Reasons}) -> JsxReasons = lists:map(fun reason_to_jsx/1, Reasons), - ?JSON:encode([{reasons, JsxReasons}]). + jesse_lib:json_encode([{reasons, JsxReasons}]). %% @doc Convert an error reason to jsx structs -spec reason_to_jsx(Reason :: error_reason()) -> jesse:json_term(). diff --git a/src/jesse_lib.erl b/src/jesse_lib.erl index 6535266e..00f64b13 100644 --- a/src/jesse_lib.erl +++ b/src/jesse_lib.erl @@ -35,11 +35,36 @@ , get_schema_id_key/1 , get_schema_id/1 , get_schema_id/2 + , json_encode/1 + , json_decode/1 ]). %% Includes -include("jesse_schema_validator.hrl"). +%% Use new json library if available +-ifdef(OTP_RELEASE). + -if(?OTP_RELEASE >= 27). + %% OTP 27 or higher +json_decode(Bin) -> + json:decode(Bin). +json_encode(Bin) -> + json:encode(Bin). + -else. + %% OTP 26 to 21. +json_decode(Bin) -> + jsx:decode(Bin). +json_encode(Bin) -> + jsx:encode(Bin). + -endif. +-else. + %% OTP 20 or lower. +json_decode(Bin) -> + jsx:decode(Bin). +json_encode(Bin) -> + jsx:encode(Bin). +-endif. + %%% API %% @doc Returns an empty list if the given value is ?not_found. -spec empty_if_not_found(Value :: any()) -> any(). diff --git a/src/jesse_schema_validator.hrl b/src/jesse_schema_validator.hrl index db77c3b6..4ebcd5c1 100644 --- a/src/jesse_schema_validator.hrl +++ b/src/jesse_schema_validator.hrl @@ -28,20 +28,6 @@ -define(IF_MAPS(Exp), Exp). -endif. -%% Use new json library if available --ifdef(OTP_RELEASE). - -if(?OTP_RELEASE >= 27). - %% OTP 27 or higher - -define(JSON, json). - -else. - %% OTP 26 to 21. - -define(JSON, jsx). - -endif. --else. - %% OTP 20 or lower. - -define(JSON, jsx). --endif. - %% Use optimization for sets if available -ifdef(OTP_RELEASE). -if(?OTP_RELEASE >= 24). From 76345bc59b18b167948e10deeaae43007a7b640d Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Tue, 3 Sep 2024 17:35:21 +0200 Subject: [PATCH 6/6] Apply review comments --- rebar.config | 2 +- src/jesse_lib.erl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index 3db29c6b..6dceb92c 100644 --- a/rebar.config +++ b/rebar.config @@ -1,5 +1,5 @@ %%-*- mode: erlang -*- -{profiles, [{test, [{deps, [{jsx, "3.1.0"}, {proper, "1.4.0"}]}]}]}. +{profiles, [{test, [{deps, [{jsx, "3.1.0"}, {rfc3339, "0.9.0"}, {proper, "1.4.0"}]}]}]}. {erl_opts, [ {platform_define, "^R[0-9]+", erlang_deprecated_types} %% , warn_export_all , warn_export_vars diff --git a/src/jesse_lib.erl b/src/jesse_lib.erl index 00f64b13..f6b9371f 100644 --- a/src/jesse_lib.erl +++ b/src/jesse_lib.erl @@ -53,14 +53,14 @@ json_encode(Bin) -> -else. %% OTP 26 to 21. json_decode(Bin) -> - jsx:decode(Bin). + jsx:decode(Bin, [{return_maps, false}]). json_encode(Bin) -> jsx:encode(Bin). -endif. -else. %% OTP 20 or lower. json_decode(Bin) -> - jsx:decode(Bin). + jsx:decode(Bin, [{return_maps, false}]). json_encode(Bin) -> jsx:encode(Bin). -endif.