feat: avoid mixing request with and without the stop_after_render flag
Previously a batch of requests that was sent to a connector could contain both requests with the stop_after_rendering flag and requests without this flag. When this happened a warning message was generated and the stop_after_render flags for the batch would be ignored. This commit fixes so that a mixed batch is never created so there is no longer any need for a warning message or ignoring flags.
This commit is contained in:
parent
b4198185bc
commit
cf56050759
|
@ -41,6 +41,9 @@
|
||||||
]).
|
]).
|
||||||
-export([clean_cache/0]).
|
-export([clean_cache/0]).
|
||||||
|
|
||||||
|
%% For tests
|
||||||
|
-export([hard_coded_test_action_info_modules/0]).
|
||||||
|
|
||||||
-callback bridge_v1_type_name() ->
|
-callback bridge_v1_type_name() ->
|
||||||
atom()
|
atom()
|
||||||
| {
|
| {
|
||||||
|
@ -128,8 +131,13 @@ hard_coded_action_info_modules_common() ->
|
||||||
emqx_bridge_mqtt_pubsub_action_info
|
emqx_bridge_mqtt_pubsub_action_info
|
||||||
].
|
].
|
||||||
|
|
||||||
|
%% This exists so that it can be mocked for test cases
|
||||||
|
hard_coded_test_action_info_modules() -> [].
|
||||||
|
|
||||||
hard_coded_action_info_modules() ->
|
hard_coded_action_info_modules() ->
|
||||||
hard_coded_action_info_modules_common() ++ hard_coded_action_info_modules_ee().
|
hard_coded_action_info_modules_common() ++
|
||||||
|
hard_coded_action_info_modules_ee() ++
|
||||||
|
?MODULE:hard_coded_test_action_info_modules().
|
||||||
|
|
||||||
%% ====================================================================
|
%% ====================================================================
|
||||||
%% API
|
%% API
|
||||||
|
|
|
@ -31,6 +31,9 @@
|
||||||
|
|
||||||
-export([clean_cache/0]).
|
-export([clean_cache/0]).
|
||||||
|
|
||||||
|
%% For tests
|
||||||
|
-export([hard_coded_test_connector_info_modules/0]).
|
||||||
|
|
||||||
%% The type name for the conncector
|
%% The type name for the conncector
|
||||||
-callback type_name() -> atom().
|
-callback type_name() -> atom().
|
||||||
|
|
||||||
|
@ -117,8 +120,13 @@ hard_coded_connector_info_modules_common() ->
|
||||||
emqx_bridge_mqtt_pubsub_connector_info
|
emqx_bridge_mqtt_pubsub_connector_info
|
||||||
].
|
].
|
||||||
|
|
||||||
|
%% This exists so that it can be mocked for test cases
|
||||||
|
hard_coded_test_connector_info_modules() -> [].
|
||||||
|
|
||||||
hard_coded_connector_info_modules() ->
|
hard_coded_connector_info_modules() ->
|
||||||
hard_coded_connector_info_modules_common() ++ hard_coded_connector_info_modules_ee().
|
hard_coded_connector_info_modules_common() ++
|
||||||
|
hard_coded_connector_info_modules_ee() ++
|
||||||
|
?MODULE:hard_coded_test_connector_info_modules().
|
||||||
|
|
||||||
%% --------------------------------------------------------------------
|
%% --------------------------------------------------------------------
|
||||||
%% Atom macros to avoid typos
|
%% Atom macros to avoid typos
|
||||||
|
|
|
@ -583,7 +583,11 @@ flush(Data0) ->
|
||||||
{keep_state, Data1};
|
{keep_state, Data1};
|
||||||
{_, false} ->
|
{_, false} ->
|
||||||
?tp(buffer_worker_flush_before_pop, #{}),
|
?tp(buffer_worker_flush_before_pop, #{}),
|
||||||
{Q1, QAckRef, Batch} = replayq:pop(Q0, #{count_limit => BatchSize}),
|
PopOpts = #{
|
||||||
|
count_limit => BatchSize,
|
||||||
|
stop_before => {fun stop_before_mixed_stop_after_render/2, initial_state}
|
||||||
|
},
|
||||||
|
{Q1, QAckRef, Batch} = replayq:pop(Q0, PopOpts),
|
||||||
Data2 = Data1#{queue := Q1},
|
Data2 = Data1#{queue := Q1},
|
||||||
?tp(buffer_worker_flush_before_sieve_expired, #{}),
|
?tp(buffer_worker_flush_before_sieve_expired, #{}),
|
||||||
Now = now_(),
|
Now = now_(),
|
||||||
|
@ -619,6 +623,73 @@ flush(Data0) ->
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
stop_before_mixed_stop_after_render(
|
||||||
|
?QUERY(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
#{stop_action_after_render := true} = _TraceCtx
|
||||||
|
),
|
||||||
|
initial_state
|
||||||
|
) ->
|
||||||
|
stop_action_after_render;
|
||||||
|
stop_before_mixed_stop_after_render(
|
||||||
|
?QUERY(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_TraceCtx
|
||||||
|
),
|
||||||
|
initial_state
|
||||||
|
) ->
|
||||||
|
no_stop_action_after_render;
|
||||||
|
stop_before_mixed_stop_after_render(
|
||||||
|
?QUERY(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
#{stop_action_after_render := true} = _TraceCtx
|
||||||
|
),
|
||||||
|
no_stop_action_after_render
|
||||||
|
) ->
|
||||||
|
true;
|
||||||
|
stop_before_mixed_stop_after_render(
|
||||||
|
?QUERY(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
#{stop_action_after_render := true} = _TraceCtx
|
||||||
|
),
|
||||||
|
stop_action_after_render
|
||||||
|
) ->
|
||||||
|
stop_action_after_render;
|
||||||
|
stop_before_mixed_stop_after_render(
|
||||||
|
?QUERY(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_TraceCtx
|
||||||
|
),
|
||||||
|
stop_action_after_render
|
||||||
|
) ->
|
||||||
|
true;
|
||||||
|
stop_before_mixed_stop_after_render(
|
||||||
|
?QUERY(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_TraceCtx
|
||||||
|
),
|
||||||
|
State
|
||||||
|
) ->
|
||||||
|
State.
|
||||||
|
|
||||||
-spec do_flush(data(), #{
|
-spec do_flush(data(), #{
|
||||||
is_batch := boolean(),
|
is_batch := boolean(),
|
||||||
batch := [queue_query()],
|
batch := [queue_query()],
|
||||||
|
@ -1119,25 +1190,13 @@ set_rule_id_trace_meta_data(Requests) when is_list(Requests) ->
|
||||||
%% Get the rule ids from requests
|
%% Get the rule ids from requests
|
||||||
RuleIDs = lists:foldl(fun collect_rule_id/2, #{}, Requests),
|
RuleIDs = lists:foldl(fun collect_rule_id/2, #{}, Requests),
|
||||||
ClientIDs = lists:foldl(fun collect_client_id/2, #{}, Requests),
|
ClientIDs = lists:foldl(fun collect_client_id/2, #{}, Requests),
|
||||||
StopAfterRender = lists:foldl(fun collect_stop_after_render/2, no_info, Requests),
|
|
||||||
StopAfterRenderVal =
|
StopAfterRenderVal =
|
||||||
case StopAfterRender of
|
case Requests of
|
||||||
only_true ->
|
%% We know that the batch is not mixed since we prevent this by
|
||||||
logger:update_process_metadata(#{stop_action_after_render => false}),
|
%% using a stop_after function in the replayq:pop call
|
||||||
|
[?QUERY(_, _, _, _, #{stop_action_after_render := true}) | _] ->
|
||||||
true;
|
true;
|
||||||
only_false ->
|
[?QUERY(_, _, _, _, _TraceCTX) | _] ->
|
||||||
false;
|
|
||||||
mixed ->
|
|
||||||
?TRACE(
|
|
||||||
warning,
|
|
||||||
"ACTION",
|
|
||||||
"mixed_stop_action_after_render_batch "
|
|
||||||
"(A batch will be sent to connector where some but "
|
|
||||||
"not all requests has stop_action_after_render set. "
|
|
||||||
"The batch will get assigned "
|
|
||||||
"stop_action_after_render = false)",
|
|
||||||
#{rule_ids => RuleIDs, client_ids => ClientIDs}
|
|
||||||
),
|
|
||||||
false
|
false
|
||||||
end,
|
end,
|
||||||
logger:update_process_metadata(#{
|
logger:update_process_metadata(#{
|
||||||
|
@ -1158,21 +1217,6 @@ collect_client_id(?QUERY(_, _, _, _, #{clientid := ClientId}), Acc) ->
|
||||||
collect_client_id(?QUERY(_, _, _, _, _), Acc) ->
|
collect_client_id(?QUERY(_, _, _, _, _), Acc) ->
|
||||||
Acc.
|
Acc.
|
||||||
|
|
||||||
collect_stop_after_render(?QUERY(_, _, _, _, #{stop_action_after_render := true}), no_info) ->
|
|
||||||
only_true;
|
|
||||||
collect_stop_after_render(?QUERY(_, _, _, _, #{stop_action_after_render := true}), only_true) ->
|
|
||||||
only_true;
|
|
||||||
collect_stop_after_render(?QUERY(_, _, _, _, #{stop_action_after_render := true}), only_false) ->
|
|
||||||
mixed;
|
|
||||||
collect_stop_after_render(?QUERY(_, _, _, _, _), no_info) ->
|
|
||||||
only_false;
|
|
||||||
collect_stop_after_render(?QUERY(_, _, _, _, _), only_true) ->
|
|
||||||
mixed;
|
|
||||||
collect_stop_after_render(?QUERY(_, _, _, _, _), only_false) ->
|
|
||||||
only_false;
|
|
||||||
collect_stop_after_render(?QUERY(_, _, _, _, _), mixed) ->
|
|
||||||
mixed.
|
|
||||||
|
|
||||||
unset_rule_id_trace_meta_data() ->
|
unset_rule_id_trace_meta_data() ->
|
||||||
logger:update_process_metadata(#{
|
logger:update_process_metadata(#{
|
||||||
rule_ids => #{}, client_ids => #{}, stop_action_after_render => false
|
rule_ids => #{}, client_ids => #{}, stop_action_after_render => false
|
||||||
|
|
|
@ -94,9 +94,6 @@ basic_apply_rule_test_helper(Config, TraceType, StopAfterRender) ->
|
||||||
%% ===================================
|
%% ===================================
|
||||||
%% Create trace for RuleId
|
%% Create trace for RuleId
|
||||||
%% ===================================
|
%% ===================================
|
||||||
Now = erlang:system_time(second) - 10,
|
|
||||||
Start = Now,
|
|
||||||
End = Now + 60,
|
|
||||||
TraceName = atom_to_binary(?FUNCTION_NAME),
|
TraceName = atom_to_binary(?FUNCTION_NAME),
|
||||||
TraceValue =
|
TraceValue =
|
||||||
case TraceType of
|
case TraceType of
|
||||||
|
@ -105,16 +102,7 @@ basic_apply_rule_test_helper(Config, TraceType, StopAfterRender) ->
|
||||||
clientid ->
|
clientid ->
|
||||||
ClientId
|
ClientId
|
||||||
end,
|
end,
|
||||||
Trace = #{
|
create_trace(TraceName, TraceType, TraceValue),
|
||||||
name => TraceName,
|
|
||||||
type => TraceType,
|
|
||||||
TraceType => TraceValue,
|
|
||||||
start_at => Start,
|
|
||||||
end_at => End
|
|
||||||
},
|
|
||||||
emqx_trace_SUITE:reload(),
|
|
||||||
ok = emqx_trace:clear(),
|
|
||||||
{ok, _} = emqx_trace:create(Trace),
|
|
||||||
%% ===================================
|
%% ===================================
|
||||||
Context = #{
|
Context = #{
|
||||||
clientid => ClientId,
|
clientid => ClientId,
|
||||||
|
@ -125,13 +113,12 @@ basic_apply_rule_test_helper(Config, TraceType, StopAfterRender) ->
|
||||||
username => <<"u_emqx">>
|
username => <<"u_emqx">>
|
||||||
},
|
},
|
||||||
Params = #{
|
Params = #{
|
||||||
% body => #{
|
|
||||||
<<"context">> => Context,
|
<<"context">> => Context,
|
||||||
<<"stop_action_after_template_rendering">> => StopAfterRender
|
<<"stop_action_after_template_rendering">> => StopAfterRender
|
||||||
% }
|
|
||||||
},
|
},
|
||||||
emqx_trace:check(),
|
emqx_trace:check(),
|
||||||
ok = emqx_trace_handler_SUITE:filesync(TraceName, TraceType),
|
ok = emqx_trace_handler_SUITE:filesync(TraceName, TraceType),
|
||||||
|
Now = erlang:system_time(second) - 10,
|
||||||
{ok, _} = file:read_file(emqx_trace:log_file(TraceName, Now)),
|
{ok, _} = file:read_file(emqx_trace:log_file(TraceName, Now)),
|
||||||
?assertMatch({ok, _}, call_apply_rule_api(RuleId, Params)),
|
?assertMatch({ok, _}, call_apply_rule_api(RuleId, Params)),
|
||||||
?retry(
|
?retry(
|
||||||
|
@ -173,14 +160,175 @@ basic_apply_rule_test_helper(Config, TraceType, StopAfterRender) ->
|
||||||
emqx_trace:delete(TraceName),
|
emqx_trace:delete(TraceName),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
create_trace(TraceName, TraceType, TraceValue) ->
|
||||||
|
Now = erlang:system_time(second) - 10,
|
||||||
|
Start = Now,
|
||||||
|
End = Now + 60,
|
||||||
|
Trace = #{
|
||||||
|
name => TraceName,
|
||||||
|
type => TraceType,
|
||||||
|
TraceType => TraceValue,
|
||||||
|
start_at => Start,
|
||||||
|
end_at => End
|
||||||
|
},
|
||||||
|
emqx_trace_SUITE:reload(),
|
||||||
|
ok = emqx_trace:clear(),
|
||||||
|
{ok, _} = emqx_trace:create(Trace).
|
||||||
|
|
||||||
|
t_apply_rule_test_batch_separation_stop_after_render(_Config) ->
|
||||||
|
MeckOpts = [passthrough, no_link, no_history, non_strict],
|
||||||
|
catch meck:new(emqx_connector_info, MeckOpts),
|
||||||
|
meck:expect(
|
||||||
|
emqx_connector_info,
|
||||||
|
hard_coded_test_connector_info_modules,
|
||||||
|
0,
|
||||||
|
[emqx_rule_engine_test_connector_info]
|
||||||
|
),
|
||||||
|
emqx_connector_info:clean_cache(),
|
||||||
|
catch meck:new(emqx_action_info, MeckOpts),
|
||||||
|
meck:expect(
|
||||||
|
emqx_action_info,
|
||||||
|
hard_coded_test_action_info_modules,
|
||||||
|
0,
|
||||||
|
[emqx_rule_engine_test_action_info]
|
||||||
|
),
|
||||||
|
emqx_action_info:clean_cache(),
|
||||||
|
{ok, _} = emqx_connector:create(rule_engine_test, ?FUNCTION_NAME, #{}),
|
||||||
|
Name = atom_to_binary(?FUNCTION_NAME),
|
||||||
|
ActionConf =
|
||||||
|
#{
|
||||||
|
<<"connector">> => Name,
|
||||||
|
<<"parameters">> =>
|
||||||
|
#{
|
||||||
|
<<"values">> =>
|
||||||
|
#{
|
||||||
|
<<"send_to_pid">> => emqx_utils:bin_to_hexstr(
|
||||||
|
term_to_binary(self()), upper
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
<<"resource_opts">> => #{
|
||||||
|
<<"batch_size">> => 1000,
|
||||||
|
<<"batch_time">> => 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ok, _} = emqx_bridge_v2:create(
|
||||||
|
rule_engine_test,
|
||||||
|
?FUNCTION_NAME,
|
||||||
|
ActionConf
|
||||||
|
),
|
||||||
|
SQL = <<"SELECT payload.is_stop_after_render as stop_after_render FROM \"", Name/binary, "\"">>,
|
||||||
|
{ok, RuleID} = create_rule_with_action(
|
||||||
|
rule_engine_test,
|
||||||
|
?FUNCTION_NAME,
|
||||||
|
SQL
|
||||||
|
),
|
||||||
|
create_trace(Name, ruleid, RuleID),
|
||||||
|
emqx_trace:check(),
|
||||||
|
ok = emqx_trace_handler_SUITE:filesync(Name, ruleid),
|
||||||
|
Now = erlang:system_time(second) - 10,
|
||||||
|
%% Stop
|
||||||
|
ParmsStopAfterRender = apply_rule_parms(true, Name),
|
||||||
|
ParmsNoStopAfterRender = apply_rule_parms(false, Name),
|
||||||
|
%% Check that batching is working
|
||||||
|
Count = 400,
|
||||||
|
CountMsgFun =
|
||||||
|
fun
|
||||||
|
CountMsgFunRec(0 = _CurCount, GotBatchWithAtLeastTwo) ->
|
||||||
|
GotBatchWithAtLeastTwo;
|
||||||
|
CountMsgFunRec(CurCount, GotBatchWithAtLeastTwo) ->
|
||||||
|
receive
|
||||||
|
List ->
|
||||||
|
Len = length(List),
|
||||||
|
CountMsgFunRec(CurCount - Len, GotBatchWithAtLeastTwo orelse (Len > 1))
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
lists:foreach(
|
||||||
|
fun(_) ->
|
||||||
|
{ok, _} = call_apply_rule_api(RuleID, ParmsStopAfterRender)
|
||||||
|
end,
|
||||||
|
lists:seq(1, Count)
|
||||||
|
),
|
||||||
|
%% We should get the messages and at least one batch with more than 1
|
||||||
|
true = CountMsgFun(Count, false),
|
||||||
|
%% We should check that we don't get any mixed batch
|
||||||
|
CheckBatchesFun =
|
||||||
|
fun
|
||||||
|
CheckBatchesFunRec(0 = _CurCount) ->
|
||||||
|
ok;
|
||||||
|
CheckBatchesFunRec(CurCount) ->
|
||||||
|
receive
|
||||||
|
[{_, #{<<"stop_after_render">> := StopValue}} | _] = List ->
|
||||||
|
[
|
||||||
|
?assertMatch(#{<<"stop_after_render">> := StopValue}, Msg)
|
||||||
|
|| {_, Msg} <- List
|
||||||
|
],
|
||||||
|
Len = length(List),
|
||||||
|
CheckBatchesFunRec(CurCount - Len)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
lists:foreach(
|
||||||
|
fun(_) ->
|
||||||
|
case rand:normal() < 0 of
|
||||||
|
true ->
|
||||||
|
{ok, _} = call_apply_rule_api(RuleID, ParmsStopAfterRender);
|
||||||
|
false ->
|
||||||
|
{ok, _} = call_apply_rule_api(RuleID, ParmsNoStopAfterRender)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
lists:seq(1, Count)
|
||||||
|
),
|
||||||
|
CheckBatchesFun(Count),
|
||||||
|
%% Just check that the log file is created as expected
|
||||||
|
?retry(
|
||||||
|
_Interval0 = 200,
|
||||||
|
_NAttempts0 = 20,
|
||||||
|
begin
|
||||||
|
Bin = read_rule_trace_file(Name, ruleid, Now),
|
||||||
|
?assertNotEqual(nomatch, binary:match(Bin, [<<"action_success">>]))
|
||||||
|
end
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
apply_rule_parms(StopAfterRender, Name) ->
|
||||||
|
Payload = #{<<"is_stop_after_render">> => StopAfterRender},
|
||||||
|
Context = #{
|
||||||
|
clientid => Name,
|
||||||
|
event_type => message_publish,
|
||||||
|
payload => emqx_utils_json:encode(Payload),
|
||||||
|
qos => 1,
|
||||||
|
topic => Name,
|
||||||
|
username => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
<<"context">> => Context,
|
||||||
|
<<"stop_action_after_template_rendering">> => StopAfterRender
|
||||||
|
}.
|
||||||
|
|
||||||
|
create_rule_with_action(ActionType, ActionName, SQL) ->
|
||||||
|
BridgeId = emqx_bridge_resource:bridge_id(ActionType, ActionName),
|
||||||
|
Params = #{
|
||||||
|
enable => true,
|
||||||
|
sql => SQL,
|
||||||
|
actions => [BridgeId]
|
||||||
|
},
|
||||||
|
Path = emqx_mgmt_api_test_util:api_path(["rules"]),
|
||||||
|
AuthHeader = emqx_mgmt_api_test_util:auth_header_(),
|
||||||
|
ct:pal("rule action params: ~p", [Params]),
|
||||||
|
case emqx_mgmt_api_test_util:request_api(post, Path, "", AuthHeader, Params) of
|
||||||
|
{ok, Res0} ->
|
||||||
|
#{<<"id">> := RuleId} = emqx_utils_json:decode(Res0, [return_maps]),
|
||||||
|
{ok, RuleId};
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
%% Helper Functions
|
%% Helper Functions
|
||||||
|
|
||||||
call_apply_rule_api(RuleId, Params) ->
|
call_apply_rule_api(RuleId, Params) ->
|
||||||
Method = post,
|
Method = post,
|
||||||
Path = emqx_mgmt_api_test_util:api_path(["rules", RuleId, "test"]),
|
Path = emqx_mgmt_api_test_util:api_path(["rules", RuleId, "test"]),
|
||||||
ct:pal("sql test (http):\n ~p", [Params]),
|
|
||||||
Res = request(Method, Path, Params),
|
Res = request(Method, Path, Params),
|
||||||
ct:pal("sql test (http) result:\n ~p", [Res]),
|
|
||||||
Res.
|
Res.
|
||||||
|
|
||||||
request(Method, Path, Params) ->
|
request(Method, Path, Params) ->
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2022-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_rule_engine_test_action_info).
|
||||||
|
|
||||||
|
-behaviour(emqx_action_info).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
bridge_v1_type_name/0,
|
||||||
|
action_type_name/0,
|
||||||
|
connector_type_name/0,
|
||||||
|
schema_module/0
|
||||||
|
]).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
namespace/0,
|
||||||
|
roots/0,
|
||||||
|
fields/1,
|
||||||
|
desc/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
-define(CONNECTOR_TYPE, rule_engine_test).
|
||||||
|
-define(ACTION_TYPE, ?CONNECTOR_TYPE).
|
||||||
|
|
||||||
|
bridge_v1_type_name() -> ?ACTION_TYPE.
|
||||||
|
|
||||||
|
action_type_name() -> ?ACTION_TYPE.
|
||||||
|
|
||||||
|
connector_type_name() -> ?ACTION_TYPE.
|
||||||
|
|
||||||
|
schema_module() -> emqx_rule_engine_test_action_info.
|
||||||
|
|
||||||
|
%% -------------------------------------------------------------------------------------------------
|
||||||
|
%% Hocon Schema Definitions
|
||||||
|
|
||||||
|
namespace() -> "bridge_test_action_info".
|
||||||
|
|
||||||
|
roots() -> [].
|
||||||
|
|
||||||
|
fields(Field) when
|
||||||
|
Field == "get_connector";
|
||||||
|
Field == "put_connector";
|
||||||
|
Field == "post_connector"
|
||||||
|
->
|
||||||
|
Fields =
|
||||||
|
fields(connector_fields) ++
|
||||||
|
emqx_connector_schema:resource_opts_ref(?MODULE, connector_resource_opts),
|
||||||
|
emqx_connector_schema:api_fields(Field, ?CONNECTOR_TYPE, Fields);
|
||||||
|
fields(Field) when
|
||||||
|
Field == "get_bridge_v2";
|
||||||
|
Field == "post_bridge_v2";
|
||||||
|
Field == "put_bridge_v2"
|
||||||
|
->
|
||||||
|
emqx_bridge_v2_schema:api_fields(Field, ?ACTION_TYPE, fields(rule_engine_test_action));
|
||||||
|
fields(action) ->
|
||||||
|
{?ACTION_TYPE,
|
||||||
|
hoconsc:mk(
|
||||||
|
hoconsc:map(name, hoconsc:ref(?MODULE, rule_engine_test_action)),
|
||||||
|
#{
|
||||||
|
desc => <<"Test Action Config">>,
|
||||||
|
required => false
|
||||||
|
}
|
||||||
|
)};
|
||||||
|
fields(rule_engine_test_action) ->
|
||||||
|
emqx_bridge_v2_schema:make_producer_action_schema(
|
||||||
|
hoconsc:mk(
|
||||||
|
hoconsc:ref(?MODULE, action_parameters),
|
||||||
|
#{
|
||||||
|
required => true,
|
||||||
|
desc => undefined
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
fields(action_parameters) ->
|
||||||
|
[
|
||||||
|
{values,
|
||||||
|
hoconsc:mk(
|
||||||
|
typerefl:map(),
|
||||||
|
#{desc => undefined, default => #{}}
|
||||||
|
)}
|
||||||
|
];
|
||||||
|
fields("config_connector") ->
|
||||||
|
emqx_connector_schema:common_fields() ++
|
||||||
|
fields(connector_fields) ++
|
||||||
|
emqx_connector_schema:resource_opts_ref(?MODULE, connector_resource_opts);
|
||||||
|
fields(connector_resource_opts) ->
|
||||||
|
emqx_connector_schema:resource_opts_fields();
|
||||||
|
fields("config") ->
|
||||||
|
emqx_resource_schema:fields("resource_opts") ++
|
||||||
|
fields(connector_fields);
|
||||||
|
fields(connector_fields) ->
|
||||||
|
[
|
||||||
|
{values,
|
||||||
|
hoconsc:mk(
|
||||||
|
typerefl:map(),
|
||||||
|
#{desc => undefined, default => #{}}
|
||||||
|
)}
|
||||||
|
].
|
||||||
|
desc(_) ->
|
||||||
|
undefined.
|
|
@ -0,0 +1,99 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2020-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%
|
||||||
|
%% 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.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
-module(emqx_rule_engine_test_connector).
|
||||||
|
|
||||||
|
-include_lib("emqx_connector/include/emqx_connector.hrl").
|
||||||
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
-include_lib("hocon/include/hoconsc.hrl").
|
||||||
|
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
||||||
|
|
||||||
|
-behaviour(emqx_resource).
|
||||||
|
|
||||||
|
%% callbacks of behaviour emqx_resource
|
||||||
|
-export([
|
||||||
|
callback_mode/0,
|
||||||
|
on_start/2,
|
||||||
|
on_stop/2,
|
||||||
|
on_query/3,
|
||||||
|
on_batch_query/3,
|
||||||
|
on_get_status/2,
|
||||||
|
on_add_channel/4,
|
||||||
|
on_remove_channel/3,
|
||||||
|
on_get_channels/1,
|
||||||
|
on_get_channel_status/3
|
||||||
|
]).
|
||||||
|
|
||||||
|
%% ===================================================================
|
||||||
|
callback_mode() -> always_sync.
|
||||||
|
|
||||||
|
on_start(
|
||||||
|
_InstId,
|
||||||
|
_Config
|
||||||
|
) ->
|
||||||
|
{ok, #{installed_channels => #{}}}.
|
||||||
|
|
||||||
|
on_stop(_InstId, _State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
on_add_channel(
|
||||||
|
_InstId,
|
||||||
|
#{
|
||||||
|
installed_channels := InstalledChannels
|
||||||
|
} = OldState,
|
||||||
|
ChannelId,
|
||||||
|
ChannelConfig
|
||||||
|
) ->
|
||||||
|
NewInstalledChannels = maps:put(ChannelId, ChannelConfig, InstalledChannels),
|
||||||
|
NewState = OldState#{installed_channels => NewInstalledChannels},
|
||||||
|
{ok, NewState}.
|
||||||
|
|
||||||
|
on_remove_channel(
|
||||||
|
_InstId,
|
||||||
|
OldState,
|
||||||
|
_ChannelId
|
||||||
|
) ->
|
||||||
|
{ok, OldState}.
|
||||||
|
|
||||||
|
on_get_channel_status(
|
||||||
|
_ResId,
|
||||||
|
_ChannelId,
|
||||||
|
_State
|
||||||
|
) ->
|
||||||
|
connected.
|
||||||
|
|
||||||
|
on_get_channels(ResId) ->
|
||||||
|
emqx_bridge_v2:get_channels_for_connector(ResId).
|
||||||
|
|
||||||
|
on_query(
|
||||||
|
_InstId,
|
||||||
|
_Query,
|
||||||
|
_State
|
||||||
|
) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
on_batch_query(
|
||||||
|
_InstId,
|
||||||
|
[{ChannelId, _Req} | _] = Msg,
|
||||||
|
#{installed_channels := Channels} = _State
|
||||||
|
) ->
|
||||||
|
#{parameters := #{values := #{send_to_pid := PidBin}}} = maps:get(ChannelId, Channels),
|
||||||
|
Pid = binary_to_term(emqx_utils:hexstr_to_bin(PidBin)),
|
||||||
|
Pid ! Msg,
|
||||||
|
ok.
|
||||||
|
|
||||||
|
on_get_status(_InstId, _State) ->
|
||||||
|
connected.
|
|
@ -0,0 +1,43 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2022-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_rule_engine_test_connector_info).
|
||||||
|
|
||||||
|
-behaviour(emqx_connector_info).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
type_name/0,
|
||||||
|
bridge_types/0,
|
||||||
|
resource_callback_module/0,
|
||||||
|
config_schema/0,
|
||||||
|
schema_module/0,
|
||||||
|
api_schema/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
type_name() ->
|
||||||
|
rule_engine_test.
|
||||||
|
|
||||||
|
bridge_types() ->
|
||||||
|
[rule_engine_test].
|
||||||
|
|
||||||
|
resource_callback_module() ->
|
||||||
|
emqx_rule_engine_test_connector.
|
||||||
|
|
||||||
|
config_schema() ->
|
||||||
|
{rule_engine_test,
|
||||||
|
hoconsc:mk(
|
||||||
|
hoconsc:map(name, hoconsc:ref(emqx_rule_engine_test_action_info, "config_connector")),
|
||||||
|
#{
|
||||||
|
desc => <<"Test Connector Config">>,
|
||||||
|
required => false
|
||||||
|
}
|
||||||
|
)}.
|
||||||
|
|
||||||
|
schema_module() ->
|
||||||
|
emqx_rule_engine_test_action_info.
|
||||||
|
|
||||||
|
api_schema(Method) ->
|
||||||
|
emqx_connector_schema:api_ref(
|
||||||
|
?MODULE, <<"rule_engine_test">>, Method ++ "_connector"
|
||||||
|
).
|
2
mix.exs
2
mix.exs
|
@ -60,7 +60,7 @@ defmodule EMQXUmbrella.MixProject do
|
||||||
{:grpc, github: "emqx/grpc-erl", tag: "0.6.12", override: true},
|
{:grpc, github: "emqx/grpc-erl", tag: "0.6.12", override: true},
|
||||||
{:minirest, github: "emqx/minirest", tag: "1.4.0", override: true},
|
{:minirest, github: "emqx/minirest", tag: "1.4.0", override: true},
|
||||||
{:ecpool, github: "emqx/ecpool", tag: "0.5.7", override: true},
|
{:ecpool, github: "emqx/ecpool", tag: "0.5.7", override: true},
|
||||||
{:replayq, github: "emqx/replayq", tag: "0.3.7", override: true},
|
{:replayq, github: "emqx/replayq", tag: "0.3.8", override: true},
|
||||||
{:pbkdf2, github: "emqx/erlang-pbkdf2", tag: "2.0.4", override: true},
|
{:pbkdf2, github: "emqx/erlang-pbkdf2", tag: "2.0.4", override: true},
|
||||||
# maybe forbid to fetch quicer
|
# maybe forbid to fetch quicer
|
||||||
{:emqtt,
|
{:emqtt,
|
||||||
|
|
|
@ -88,7 +88,7 @@
|
||||||
{grpc, {git, "https://github.com/emqx/grpc-erl", {tag, "0.6.12"}}},
|
{grpc, {git, "https://github.com/emqx/grpc-erl", {tag, "0.6.12"}}},
|
||||||
{minirest, {git, "https://github.com/emqx/minirest", {tag, "1.4.0"}}},
|
{minirest, {git, "https://github.com/emqx/minirest", {tag, "1.4.0"}}},
|
||||||
{ecpool, {git, "https://github.com/emqx/ecpool", {tag, "0.5.7"}}},
|
{ecpool, {git, "https://github.com/emqx/ecpool", {tag, "0.5.7"}}},
|
||||||
{replayq, {git, "https://github.com/emqx/replayq.git", {tag, "0.3.7"}}},
|
{replayq, {git, "https://github.com/emqx/replayq.git", {tag, "0.3.8"}}},
|
||||||
{pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {tag, "2.0.4"}}},
|
{pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {tag, "2.0.4"}}},
|
||||||
{emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.10.1"}}},
|
{emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.10.1"}}},
|
||||||
{rulesql, {git, "https://github.com/emqx/rulesql", {tag, "0.2.0"}}},
|
{rulesql, {git, "https://github.com/emqx/rulesql", {tag, "0.2.0"}}},
|
||||||
|
|
Loading…
Reference in New Issue