Skip to content

Commit ab3ca9e

Browse files
authored
feat: update plugin to new Nova format (#16)
* feat: replace thoas with Erlang/OTP json module Use json:encode/1 and json:decode/1 directly instead of the configurable json_lib approach via nova:get_env. The json_lib config option was removed from Nova. * feat: update plugin callbacks to 4-arity Nova API Update pre_request/post_request to 4-arity callbacks matching the current nova_plugin behaviour. Update plugin_info to return a map instead of a tuple. * feat: add init/0 callback to start jesse database and preload schemas * fix: update rebar.config for new plugin format - Bump nova dep to 0.13.7 (matches 4-arity plugin API) - Add xref_ignores for init/0, pre_request/4, post_request/4 - Apply erlfmt formatting
1 parent 8c2d88e commit ab3ca9e

File tree

3 files changed

+61
-56
lines changed

3 files changed

+61
-56
lines changed

rebar.config

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
{erl_opts, [debug_info]}.
22
{deps, [
3-
{nova, "0.12.1"},
3+
{nova, "0.13.7"},
44
{jesse, "1.8.1"}
55
]}.
66

77
{xref_ignores, [
8-
{nova_json_schemas, load_local_schemas, 0}
8+
{nova_json_schemas, init, 0},
9+
{nova_json_schemas, load_local_schemas, 0},
10+
{nova_json_schemas, pre_request, 4},
11+
{nova_json_schemas, post_request, 4}
912
]}.
1013

1114
{dialyzer, [

rebar.lock

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.16.0">>},2},
44
{<<"erlydtl">>,{pkg,<<"erlydtl">>,<<"0.14.0">>},1},
55
{<<"jesse">>,{pkg,<<"jesse">>,<<"1.8.1">>},0},
6-
{<<"jhn_stdlib">>,{pkg,<<"jhn_stdlib">>,<<"5.3.3">>},1},
7-
{<<"nova">>,{pkg,<<"nova">>,<<"0.12.1">>},0},
6+
{<<"jhn_stdlib">>,{pkg,<<"jhn_stdlib">>,<<"5.4.0">>},1},
7+
{<<"nova">>,{pkg,<<"nova">>,<<"0.13.7">>},0},
88
{<<"ranch">>,{pkg,<<"ranch">>,<<"2.2.0">>},2},
99
{<<"routing_tree">>,{pkg,<<"routing_tree">>,<<"1.0.11">>},1},
1010
{<<"thoas">>,{pkg,<<"thoas">>,<<"1.2.1">>},1}]}.
@@ -14,8 +14,8 @@
1414
{<<"cowlib">>, <<"54592074EBBBB92EE4746C8A8846E5605052F29309D3A873468D76CDF932076F">>},
1515
{<<"erlydtl">>, <<"964B2DC84F8C17ACFAA69C59BA129EF26AC45D2BA898C3C6AD9B5BDC8BA13CED">>},
1616
{<<"jesse">>, <<"C9E3670C7EE40F719734E3BC716578143AABA93FC7525A02A7D5CB300B3AD71E">>},
17-
{<<"jhn_stdlib">>, <<"3E50C560334A85EE5B6C645D2E1BCD35E7BE667E5FCB62AA364F00737C2ADC61">>},
18-
{<<"nova">>, <<"78B60221F632C5C7D1B0EEEABCB7370764304C32C757C0343513DA4108326BD3">>},
17+
{<<"jhn_stdlib">>, <<"FAC6F19B35351278F1CB156E23A5B2A6047A9DD5AB1FD9E1189A7918006DF7ED">>},
18+
{<<"nova">>, <<"C37A2161EA1EE643635739282D1DF9880D20D9521B6AD3ABE6A71C6A6867C842">>},
1919
{<<"ranch">>, <<"25528F82BC8D7C6152C57666CA99EC716510FE0925CB188172F41CE93117B1B0">>},
2020
{<<"routing_tree">>, <<"72ACEF2095F0EC804F7AFD07EF781DDE5009425A1CA0A28F0706B1DB334A4812">>},
2121
{<<"thoas">>, <<"19A25F31177A17E74004D4840F66D791D4298C5738790FA2CC73731EB911F195">>}]},
@@ -24,8 +24,8 @@
2424
{<<"cowlib">>, <<"7F478D80D66B747344F0EA7708C187645CFCC08B11AA424632F78E25BF05DB51">>},
2525
{<<"erlydtl">>, <<"D80EC044CD8F58809C19D29AC5605BE09E955040911B644505E31E9DD8143431">>},
2626
{<<"jesse">>, <<"0EDED3F18623FDA2F25989804A06CF518B4ACF2E9365B18C8E8C013D7E3C906F">>},
27-
{<<"jhn_stdlib">>, <<"2CB184C505397B62A842AB3DE13F21B83ADF62364BD35A572191629E30E0258E">>},
28-
{<<"nova">>, <<"C43DC05F0B0F31FD3CF3B0415F2772BFD9FFD75DFEBFFE0B806DE85B6581F4C7">>},
27+
{<<"jhn_stdlib">>, <<"7EABD1B01D2DEFF495BF7C5CA1DBA4D3FA0B84DC3AF03CA85F31D52EBB03C6FC">>},
28+
{<<"nova">>, <<"22F4C3FFDC08DB8568F3DA198647823D63123520F69C7D718A8DB468F4C7A1EA">>},
2929
{<<"ranch">>, <<"FA0B99A1780C80218A4197A59EA8D3BDAE32FBFF7E88527D7D8A4787EFF4F8E7">>},
3030
{<<"routing_tree">>, <<"85982C7AC502892C5179CD2A591331003BACD2D2A71723640BA7D23F45408E6E">>},
3131
{<<"thoas">>, <<"E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A">>}]}

src/nova_json_schemas.erl

Lines changed: 50 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@
22
-behaviour(nova_plugin).
33

44
-export([
5+
init/0,
56
load_local_schemas/0,
6-
pre_request/2,
7-
post_request/2,
7+
pre_request/4,
8+
post_request/4,
89
plugin_info/0
910
]).
1011

1112
-include_lib("kernel/include/logger.hrl").
1213

14+
init() ->
15+
jesse_database:load_all(),
16+
load_local_schemas(),
17+
#{}.
18+
1319
%%--------------------------------------------------------------------
1420
%% @doc
1521
%% Load all local JSON schemas from the main application's
@@ -34,88 +40,85 @@ load_local_schemas() ->
3440
%% Pre-request callback
3541
%% @end
3642
%%--------------------------------------------------------------------
37-
-spec pre_request(Req :: cowboy_req:req(), Options :: map()) ->
38-
{ok, Req0 :: cowboy_req:req()}
39-
| {stop, Req0 :: cowboy_req:req()}
43+
-spec pre_request(Req :: cowboy_req:req(), Env :: any(), Options :: map(), State :: any()) ->
44+
{ok, Req0 :: cowboy_req:req(), NewState :: any()}
45+
| {stop, Req0 :: cowboy_req:req(), NewState :: any()}
4046
| {error, Reason :: term()}.
4147
pre_request(
42-
Req = #{extra_state := #{json_schema := SchemaLocation} = Extra, json := JSON}, Options
48+
Req = #{extra_state := #{json_schema := SchemaLocation} = Extra, json := JSON},
49+
_Env,
50+
Options,
51+
State
4352
) ->
4453
JesseOpts = maps:get(jesse_options, Extra, []),
45-
%% JSON have already been parsed so we can just continue with the validation
4654
case validate_json(SchemaLocation, JSON, JesseOpts) of
4755
ok ->
4856
?LOG_DEBUG("Schema validation on JSON body successful"),
49-
{ok, Req};
57+
{ok, Req, State};
5058
{error, Errors} ->
5159
?LOG_DEBUG("Got validation-errors on JSON body. Errors: ~p", [Errors]),
5260
case maps:get(render_errors, Options, false) of
5361
true ->
5462
?LOG_DEBUG("Rendering validation-errors and send back to requester"),
55-
JsonLib = nova:get_env(json_lib, thoas),
5663
Req0 = cowboy_req:set_resp_headers(
5764
#{<<"content-type">> => <<"application/json">>}, Req
5865
),
5966
ErrorStruct = render_error(Errors),
60-
ErrorJson = erlang:apply(JsonLib, encode, [ErrorStruct]),
67+
ErrorJson = json:encode(ErrorStruct),
6168
Req1 = cowboy_req:set_resp_body(ErrorJson, Req0),
6269
Req2 = cowboy_req:reply(400, Req1),
63-
{stop, Req2};
70+
{stop, Req2, State};
6471
_ ->
6572
?LOG_DEBUG(
6673
"render_errors-option not set for plugin nova_json_schemas - returning plain 400-status to requester"
6774
),
6875
Req0 = cowboy_req:reply(400, Req),
69-
{stop, Req0}
76+
{stop, Req0, State}
7077
end
7178
end;
72-
pre_request(#{extra_state := #{json_schema := _SchemaLocation}}, _Options) ->
73-
%% The body have not been parsed. Log and error and stop
79+
pre_request(#{extra_state := #{json_schema := _SchemaLocation}}, _Env, _Options, _State) ->
7480
?LOG_ERROR(
7581
"JSON Schema is set in 'extra_state' but body have not yet been parsed - rearrange your plugins so that JSON plugin is ran before this.."
7682
),
7783
{error, body_not_parsed};
78-
pre_request(Req, _Options) ->
79-
%% 'json_schema' is not set or 'extra_state' is completly missing. Just continue.
80-
HasBody = cowboy_req:has_body(Req),
81-
if
82-
HasBody ->
83-
?LOG_DEBUG("No schema is set for this route so will continue executing");
84-
true ->
85-
ok
86-
end,
87-
{ok, Req}.
84+
pre_request(Req, _Env, _Options, State) ->
85+
{ok, Req, State}.
8886

8987
%%--------------------------------------------------------------------
9088
%% @doc
9189
%% Post-request callback
9290
%% @end
9391
%%--------------------------------------------------------------------
94-
-spec post_request(Req :: cowboy_req:req(), Options :: map()) ->
95-
{ok, Req0 :: cowboy_req:req()}
96-
| {stop, Req0 :: cowboy_req:req()}
97-
| {error, Reason :: term()}.
98-
post_request(Req, _Options) ->
99-
{ok, Req}.
92+
-spec post_request(Req :: cowboy_req:req(), Env :: any(), Options :: map(), State :: any()) ->
93+
{ok, Req0 :: cowboy_req:req(), NewState :: any()}.
94+
post_request(Req, _Env, _Options, State) ->
95+
{ok, Req, State}.
10096

10197
%%--------------------------------------------------------------------
10298
%% @doc
10399
%% nova_plugin callback. Returns information about the plugin.
104100
%% @end
105101
%%--------------------------------------------------------------------
106102
-spec plugin_info() ->
107-
{Title :: binary(), Version :: binary(), Author :: binary(), Description :: binary(), [
108-
{Key :: atom(), OptionDescription :: binary()}
109-
]}.
103+
#{
104+
title := binary(),
105+
version := binary(),
106+
url := binary(),
107+
authors := [binary()],
108+
description := binary(),
109+
options := [{Key :: atom(), OptionDescription :: binary()}]
110+
}.
110111
plugin_info() ->
111-
{ok, Vsn} = application:get_key(nova_json_schemas, vsn),
112-
{ok, Desc} = application:get_key(nova_json_schemas, description),
113-
114-
{<<"JSON schema plugin">>, list_to_binary(Vsn), <<"Niclas Axelsson <niclas@burbas.se">>,
115-
list_to_binary(Desc), [
116-
{render_errors, <<"If this is set, validation-errors is returned to the requester">>}
117-
%% Options is specified as {Key, Description}
118-
]}.
112+
#{
113+
title => <<"Nova JSON Schema plugin">>,
114+
version => <<"0.2.0">>,
115+
url => <<"https://github.com/novaframework/nova_json_schemas">>,
116+
authors => [<<"Niclas Axelsson <niclas@burbas.se>">>],
117+
description => <<"Validates JSON request bodies against JSON schemas using jesse">>,
118+
options => [
119+
{render_errors, <<"If true, validation errors are returned as JSON to the requester">>}
120+
]
121+
}.
119122

120123
validate_json(SchemaLocation, Json, JesseOpts) ->
121124
case jesse:validate(SchemaLocation, Json, JesseOpts) of
@@ -125,8 +128,7 @@ validate_json(SchemaLocation, Json, JesseOpts) ->
125128
PrivDir = code:priv_dir(MainApp),
126129
SchemaLocation0 = filename:join([PrivDir, SchemaLocation]),
127130
{ok, Filecontent} = file:read_file(SchemaLocation0),
128-
JsonLib = nova:get_env(json_lib, thoas),
129-
{ok, Schema} = erlang:apply(JsonLib, decode, [Filecontent]),
131+
Schema = json:decode(Filecontent),
130132
jesse:add_schema(SchemaLocation, Schema),
131133
validate_json(SchemaLocation, Json, JesseOpts);
132134
{error, ValidationError} ->
@@ -178,12 +180,12 @@ load_schemas_from_dir(Dir, RelativePrefix) ->
178180
load_schema_file(FilePath, RelativePath) ->
179181
case file:read_file(FilePath) of
180182
{ok, FileContent} ->
181-
JsonLib = nova:get_env(json_lib, thoas),
182-
case erlang:apply(JsonLib, decode, [FileContent]) of
183-
{ok, Schema} ->
183+
try json:decode(FileContent) of
184+
Schema ->
184185
jesse:add_schema(RelativePath, Schema),
185-
?LOG_DEBUG("Loaded JSON schema: ~s", [RelativePath]);
186-
{error, Reason} ->
186+
?LOG_DEBUG("Loaded JSON schema: ~s", [RelativePath])
187+
catch
188+
error:Reason ->
187189
?LOG_ERROR("Failed to decode JSON schema ~s: ~p", [FilePath, Reason])
188190
end;
189191
{error, Reason} ->

0 commit comments

Comments
 (0)