fix(rule): check request body for /rule_test crashes

This commit is contained in:
Shawn 2022-03-11 18:13:12 +08:00
parent f924b9389d
commit 590fa1b375
4 changed files with 38 additions and 31 deletions

View File

@ -16,8 +16,9 @@
%% Bad Request %% Bad Request
-define(BAD_REQUEST, 'BAD_REQUEST'). -define(BAD_REQUEST, 'BAD_REQUEST').
-define(NOT_MATCH, 'NOT_MATCH').
-define(ALREADY_EXISTS, 'ALREADY_EXISTS'). -define(ALREADY_EXISTS, 'ALREADY_EXISTS').
-define(BAD_CONFIG_SCHEMA, 'BAD_CONFIG_SCHEMA'). -define(BAD_CONFIG_SCHEMA, 'BAD_CONFIG_SCHEMA').
-define(BAD_LISTENER_ID, 'BAD_LISTENER_ID'). -define(BAD_LISTENER_ID, 'BAD_LISTENER_ID').
-define(BAD_NODE_NAME, 'BAD_NODE_NAME'). -define(BAD_NODE_NAME, 'BAD_NODE_NAME').
@ -49,7 +50,8 @@
%% All codes %% All codes
-define(ERROR_CODES, -define(ERROR_CODES,
[ {'BAD_REQUEST', <<"Request parameters are not legal">>} [ {'BAD_REQUEST', <<"Request parameters are not legal">>}
, {'ALREADY_EXISTS', <<"Resource already existed">>} , {'NOT_MATCH', <<"Conditions are not matched">>}
, {'ALREADY_EXISTS', <<"Resource already existed">>}
, {'BAD_CONFIG_SCHEMA', <<"Configuration data is not legal">>} , {'BAD_CONFIG_SCHEMA', <<"Configuration data is not legal">>}
, {'BAD_LISTENER_ID', <<"Bad listener ID">>} , {'BAD_LISTENER_ID', <<"Bad listener ID">>}
, {'BAD_NODE_NAME', <<"Bad Node Name">>} , {'BAD_NODE_NAME', <<"Bad Node Name">>}

View File

@ -15,10 +15,11 @@
-spec check_params(map(), tag()) -> {ok, map()} | {error, term()}. -spec check_params(map(), tag()) -> {ok, map()} | {error, term()}.
check_params(Params, Tag) -> check_params(Params, Tag) ->
BTag = atom_to_binary(Tag), BTag = atom_to_binary(Tag),
case emqx_hocon:check(?MODULE, #{BTag => Params}) of Opts = #{atom_key => true, required => false},
{ok, #{Tag := Checked}} -> try hocon_tconf:check_plain(?MODULE, #{BTag => Params}, Opts, [Tag]) of
{ok, Checked}; #{Tag := Checked} -> {ok, Checked}
{error, Reason} -> catch
throw : Reason ->
?SLOG(error, #{msg => "check_rule_params_failed", ?SLOG(error, #{msg => "check_rule_params_failed",
reason => Reason reason => Reason
}), }),

View File

@ -41,7 +41,7 @@
{ok, CheckedParams} -> {ok, CheckedParams} ->
EXPR; EXPR;
{error, REASON} -> {error, REASON} ->
{400, #{code => 'BAD_ARGS', message => ?ERR_BADARGS(REASON)}} {400, #{code => 'BAD_REQUEST', message => ?ERR_BADARGS(REASON)}}
end). end).
-define(METRICS(MATCH, PASS, FAIL, FAIL_EX, FAIL_NORES, O_TOTAL, O_FAIL, O_FAIL_OOS, -define(METRICS(MATCH, PASS, FAIL, FAIL_EX, FAIL_NORES, O_TOTAL, O_FAIL, O_FAIL_OOS,
O_FAIL_UNKNOWN, O_SUCC, RATE, RATE_MAX, RATE_5), O_FAIL_UNKNOWN, O_SUCC, RATE, RATE_MAX, RATE_5),
@ -85,10 +85,8 @@ api_spec() ->
paths() -> ["/rule_events", "/rule_test", "/rules", "/rules/:id"]. paths() -> ["/rule_events", "/rule_test", "/rules", "/rules/:id"].
error_schema(Code, Message) -> error_schema(Code, Message) when is_atom(Code) ->
[ {code, mk(string(), #{example => Code})} emqx_dashboard_swagger:error_codes([Code], Message).
, {message, mk(string(), #{example => Message})}
].
rule_creation_schema() -> rule_creation_schema() ->
ref(emqx_rule_api_schema, "rule_creation"). ref(emqx_rule_api_schema, "rule_creation").
@ -115,7 +113,7 @@ schema("/rules") ->
summary => <<"Create a Rule">>, summary => <<"Create a Rule">>,
requestBody => rule_creation_schema(), requestBody => rule_creation_schema(),
responses => #{ responses => #{
400 => error_schema('BAD_ARGS', "Invalid Parameters"), 400 => error_schema('BAD_REQUEST', "Invalid Parameters"),
201 => rule_info_schema() 201 => rule_info_schema()
}} }}
}; };
@ -153,7 +151,7 @@ schema("/rules/:id") ->
parameters => param_path_id(), parameters => param_path_id(),
requestBody => rule_creation_schema(), requestBody => rule_creation_schema(),
responses => #{ responses => #{
400 => error_schema('BAD_ARGS', "Invalid Parameters"), 400 => error_schema('BAD_REQUEST', "Invalid Parameters"),
200 => rule_info_schema() 200 => rule_info_schema()
} }
}, },
@ -177,7 +175,7 @@ schema("/rule_test") ->
summary => <<"Test a Rule">>, summary => <<"Test a Rule">>,
requestBody => rule_test_schema(), requestBody => rule_test_schema(),
responses => #{ responses => #{
400 => error_schema('BAD_ARGS', "Invalid Parameters"), 400 => error_schema('BAD_REQUEST', "Invalid Parameters"),
412 => error_schema('NOT_MATCH', "SQL Not Match"), 412 => error_schema('NOT_MATCH', "SQL Not Match"),
200 => <<"Rule Test Pass">> 200 => <<"Rule Test Pass">>
} }
@ -201,13 +199,13 @@ param_path_id() ->
'/rules'(post, #{body := Params0}) -> '/rules'(post, #{body := Params0}) ->
case maps:get(<<"id">>, Params0, list_to_binary(emqx_misc:gen_id(8))) of case maps:get(<<"id">>, Params0, list_to_binary(emqx_misc:gen_id(8))) of
<<>> -> <<>> ->
{400, #{code => 'BAD_ARGS', message => <<"empty rule id is not allowed">>}}; {400, #{code => 'BAD_REQUEST', message => <<"empty rule id is not allowed">>}};
Id -> Id ->
Params = filter_out_request_body(Params0), Params = filter_out_request_body(Params0),
ConfPath = emqx_rule_engine:config_key_path() ++ [Id], ConfPath = emqx_rule_engine:config_key_path() ++ [Id],
case emqx_rule_engine:get_rule(Id) of case emqx_rule_engine:get_rule(Id) of
{ok, _Rule} -> {ok, _Rule} ->
{400, #{code => 'BAD_ARGS', message => <<"rule id already exists">>}}; {400, #{code => 'BAD_REQUEST', message => <<"rule id already exists">>}};
not_found -> not_found ->
case emqx_conf:update(ConfPath, Params, #{}) of case emqx_conf:update(ConfPath, Params, #{}) of
{ok, #{post_config_update := #{emqx_rule_engine := AllRules}}} -> {ok, #{post_config_update := #{emqx_rule_engine := AllRules}}} ->
@ -216,7 +214,7 @@ param_path_id() ->
{error, Reason} -> {error, Reason} ->
?SLOG(error, #{msg => "create_rule_failed", ?SLOG(error, #{msg => "create_rule_failed",
id => Id, reason => Reason}), id => Id, reason => Reason}),
{400, #{code => 'BAD_ARGS', message => ?ERR_BADARGS(Reason)}} {400, #{code => 'BAD_REQUEST', message => ?ERR_BADARGS(Reason)}}
end end
end end
end. end.
@ -224,6 +222,8 @@ param_path_id() ->
'/rule_test'(post, #{body := Params}) -> '/rule_test'(post, #{body := Params}) ->
?CHECK_PARAMS(Params, rule_test, case emqx_rule_sqltester:test(CheckedParams) of ?CHECK_PARAMS(Params, rule_test, case emqx_rule_sqltester:test(CheckedParams) of
{ok, Result} -> {200, Result}; {ok, Result} -> {200, Result};
{error, {parse_error, Reason}} ->
{400, #{code => 'BAD_REQUEST', message => err_msg(Reason)}};
{error, nomatch} -> {412, #{code => 'NOT_MATCH', message => <<"SQL Not Match">>}} {error, nomatch} -> {412, #{code => 'NOT_MATCH', message => <<"SQL Not Match">>}}
end). end).
@ -245,7 +245,7 @@ param_path_id() ->
{error, Reason} -> {error, Reason} ->
?SLOG(error, #{msg => "update_rule_failed", ?SLOG(error, #{msg => "update_rule_failed",
id => Id, reason => Reason}), id => Id, reason => Reason}),
{400, #{code => 'BAD_ARGS', message => ?ERR_BADARGS(Reason)}} {400, #{code => 'BAD_REQUEST', message => ?ERR_BADARGS(Reason)}}
end; end;
'/rules/:id'(delete, #{bindings := #{id := Id}}) -> '/rules/:id'(delete, #{bindings := #{id := Id}}) ->
@ -255,7 +255,7 @@ param_path_id() ->
{error, Reason} -> {error, Reason} ->
?SLOG(error, #{msg => "delete_rule_failed", ?SLOG(error, #{msg => "delete_rule_failed",
id => Id, reason => Reason}), id => Id, reason => Reason}),
{500, #{code => 'BAD_ARGS', message => ?ERR_BADARGS(Reason)}} {500, #{code => 'INTERNAL_ERROR', message => ?ERR_BADARGS(Reason)}}
end. end.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------

View File

@ -24,19 +24,23 @@
-spec test(#{sql := binary(), context := map()}) -> {ok, map() | list()} | {error, nomatch}. -spec test(#{sql := binary(), context := map()}) -> {ok, map() | list()} | {error, nomatch}.
test(#{sql := Sql, context := Context}) -> test(#{sql := Sql, context := Context}) ->
{ok, Select} = emqx_rule_sqlparser:parse(Sql), case emqx_rule_sqlparser:parse(Sql) of
InTopic = maps:get(topic, Context, <<>>), {ok, Select} ->
EventTopics = emqx_rule_sqlparser:select_from(Select), InTopic = maps:get(topic, Context, <<>>),
case lists:all(fun is_publish_topic/1, EventTopics) of EventTopics = emqx_rule_sqlparser:select_from(Select),
true -> case lists:all(fun is_publish_topic/1, EventTopics) of
%% test if the topic matches the topic filters in the rule true ->
case emqx_plugin_libs_rule:can_topic_match_oneof(InTopic, EventTopics) of %% test if the topic matches the topic filters in the rule
true -> test_rule(Sql, Select, Context, EventTopics); case emqx_plugin_libs_rule:can_topic_match_oneof(InTopic, EventTopics) of
false -> {error, nomatch} true -> test_rule(Sql, Select, Context, EventTopics);
false -> {error, nomatch}
end;
false ->
%% the rule is for both publish and events, test it directly
test_rule(Sql, Select, Context, EventTopics)
end; end;
false -> {error, Reason} ->
%% the rule is for both publish and events, test it directly {error, Reason}
test_rule(Sql, Select, Context, EventTopics)
end. end.
test_rule(Sql, Select, Context, EventTopics) -> test_rule(Sql, Select, Context, EventTopics) ->