* fix(dialyzer): remove the dialyzer errors
This commit is contained in:
parent
0fe86341af
commit
0a44270932
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
-type selected_data() :: map().
|
-type selected_data() :: map().
|
||||||
-type env_vars() :: map().
|
-type env_vars() :: map().
|
||||||
-type bindings() :: list(#{atom() => term()}).
|
-type bindings() :: list({atom(), term()}).
|
||||||
|
|
||||||
-define(BINDING_KEYS, '__bindings__').
|
-define(BINDING_KEYS, '__bindings__').
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,8 @@
|
||||||
|
|
||||||
-type(hook() :: atom() | 'any').
|
-type(hook() :: atom() | 'any').
|
||||||
|
|
||||||
|
-type(topic() :: binary()).
|
||||||
|
|
||||||
-type(resource_status() :: #{ alive := boolean()
|
-type(resource_status() :: #{ alive := boolean()
|
||||||
, atom() => binary() | atom() | list(binary()|atom())
|
, atom() => binary() | atom() | list(binary()|atom())
|
||||||
}).
|
}).
|
||||||
|
@ -65,7 +67,7 @@
|
||||||
|
|
||||||
-record(rule,
|
-record(rule,
|
||||||
{ id :: rule_id()
|
{ id :: rule_id()
|
||||||
, for :: hook()
|
, for :: list(topic())
|
||||||
, rawsql :: binary()
|
, rawsql :: binary()
|
||||||
, is_foreach :: boolean()
|
, is_foreach :: boolean()
|
||||||
, fields :: list()
|
, fields :: list()
|
||||||
|
@ -110,9 +112,11 @@
|
||||||
|
|
||||||
-record(action_instance_params,
|
-record(action_instance_params,
|
||||||
{ id :: action_instance_id()
|
{ id :: action_instance_id()
|
||||||
, params :: #{} %% the params got after initializing the action
|
%% the params got after initializing the action
|
||||||
|
, params :: #{}
|
||||||
|
%% the Func/Bindings got after initializing the action
|
||||||
, apply :: fun((Data::map(), Envs::map()) -> any())
|
, apply :: fun((Data::map(), Envs::map()) -> any())
|
||||||
| {M::module(), F::atom(), Args::list()} %% the func got after initializing the action
|
| #{mod := module(), bindings := #{atom() => term()}}
|
||||||
}).
|
}).
|
||||||
|
|
||||||
%% Arithmetic operators
|
%% Arithmetic operators
|
||||||
|
|
|
@ -1,285 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2020 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.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
-define(COLUMNS(EVENT), [Key || {Key, _ExampleVal} <- ?EG_COLUMNS(EVENT)]).
|
|
||||||
|
|
||||||
-define(EG_COLUMNS(EVENT),
|
|
||||||
case EVENT of
|
|
||||||
'message.publish' ->
|
|
||||||
[ {<<"id">>, emqx_guid:to_hexstr(emqx_guid:gen())}
|
|
||||||
, {<<"clientid">>, <<"c_emqx">>}
|
|
||||||
, {<<"username">>, <<"u_emqx">>}
|
|
||||||
, {<<"payload">>, <<"{\"msg\": \"hello\"}">>}
|
|
||||||
, {<<"peerhost">>, <<"192.168.0.10">>}
|
|
||||||
, {<<"topic">>, <<"t/a">>}
|
|
||||||
, {<<"qos">>, 1}
|
|
||||||
, {<<"flags">>, #{}}
|
|
||||||
, {<<"headers">>, undefined}
|
|
||||||
, {<<"publish_received_at">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"node">>, node()}
|
|
||||||
];
|
|
||||||
'message.delivered' ->
|
|
||||||
[ {<<"event">>, 'message.delivered'}
|
|
||||||
, {<<"id">>, emqx_guid:to_hexstr(emqx_guid:gen())}
|
|
||||||
, {<<"from_clientid">>, <<"c_emqx_1">>}
|
|
||||||
, {<<"from_username">>, <<"u_emqx_1">>}
|
|
||||||
, {<<"clientid">>, <<"c_emqx_2">>}
|
|
||||||
, {<<"username">>, <<"u_emqx_2">>}
|
|
||||||
, {<<"payload">>, <<"{\"msg\": \"hello\"}">>}
|
|
||||||
, {<<"peerhost">>, <<"192.168.0.10">>}
|
|
||||||
, {<<"topic">>, <<"t/a">>}
|
|
||||||
, {<<"qos">>, 1}
|
|
||||||
, {<<"flags">>, #{}}
|
|
||||||
, {<<"publish_received_at">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"node">>, node()}
|
|
||||||
];
|
|
||||||
'message.acked' ->
|
|
||||||
[ {<<"event">>, 'message.acked'}
|
|
||||||
, {<<"id">>, emqx_guid:to_hexstr(emqx_guid:gen())}
|
|
||||||
, {<<"from_clientid">>, <<"c_emqx_1">>}
|
|
||||||
, {<<"from_username">>, <<"u_emqx_1">>}
|
|
||||||
, {<<"clientid">>, <<"c_emqx_2">>}
|
|
||||||
, {<<"username">>, <<"u_emqx_2">>}
|
|
||||||
, {<<"payload">>, <<"{\"msg\": \"hello\"}">>}
|
|
||||||
, {<<"peerhost">>, <<"192.168.0.10">>}
|
|
||||||
, {<<"topic">>, <<"t/a">>}
|
|
||||||
, {<<"qos">>, 1}
|
|
||||||
, {<<"flags">>, #{}}
|
|
||||||
, {<<"publish_received_at">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"node">>, node()}
|
|
||||||
];
|
|
||||||
'message.dropped' ->
|
|
||||||
[ {<<"event">>, 'message.dropped'}
|
|
||||||
, {<<"id">>, emqx_guid:to_hexstr(emqx_guid:gen())}
|
|
||||||
, {<<"reason">>, no_subscribers}
|
|
||||||
, {<<"clientid">>, <<"c_emqx">>}
|
|
||||||
, {<<"username">>, <<"u_emqx">>}
|
|
||||||
, {<<"payload">>, <<"{\"msg\": \"hello\"}">>}
|
|
||||||
, {<<"peerhost">>, <<"192.168.0.10">>}
|
|
||||||
, {<<"topic">>, <<"t/a">>}
|
|
||||||
, {<<"qos">>, 1}
|
|
||||||
, {<<"flags">>, #{}}
|
|
||||||
, {<<"publish_received_at">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"node">>, node()}
|
|
||||||
];
|
|
||||||
'client.connected' ->
|
|
||||||
[ {<<"event">>, 'client.connected'}
|
|
||||||
, {<<"clientid">>, <<"c_emqx">>}
|
|
||||||
, {<<"username">>, <<"u_emqx">>}
|
|
||||||
, {<<"mountpoint">>, undefined}
|
|
||||||
, {<<"peername">>, <<"192.168.0.10:56431">>}
|
|
||||||
, {<<"sockname">>, <<"0.0.0.0:1883">>}
|
|
||||||
, {<<"proto_name">>, <<"MQTT">>}
|
|
||||||
, {<<"proto_ver">>, 5}
|
|
||||||
, {<<"keepalive">>, 60}
|
|
||||||
, {<<"clean_start">>, true}
|
|
||||||
, {<<"expiry_interval">>, 3600}
|
|
||||||
, {<<"is_bridge">>, false}
|
|
||||||
, {<<"connected_at">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"node">>, node()}
|
|
||||||
];
|
|
||||||
'client.disconnected' ->
|
|
||||||
[ {<<"event">>, 'client.disconnected'}
|
|
||||||
, {<<"reason">>, normal}
|
|
||||||
, {<<"clientid">>, <<"c_emqx">>}
|
|
||||||
, {<<"username">>, <<"u_emqx">>}
|
|
||||||
, {<<"peername">>, <<"192.168.0.10:56431">>}
|
|
||||||
, {<<"sockname">>, <<"0.0.0.0:1883">>}
|
|
||||||
, {<<"disconnected_at">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"node">>, node()}
|
|
||||||
];
|
|
||||||
'session.subscribed' ->
|
|
||||||
[ {<<"event">>, 'session.subscribed'}
|
|
||||||
, {<<"clientid">>, <<"c_emqx">>}
|
|
||||||
, {<<"username">>, <<"u_emqx">>}
|
|
||||||
, {<<"peerhost">>, <<"192.168.0.10">>}
|
|
||||||
, {<<"topic">>, <<"t/a">>}
|
|
||||||
, {<<"qos">>, 1}
|
|
||||||
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"node">>, node()}
|
|
||||||
];
|
|
||||||
'session.unsubscribed' ->
|
|
||||||
[ {<<"event">>, 'session.unsubscribed'}
|
|
||||||
, {<<"clientid">>, <<"c_emqx">>}
|
|
||||||
, {<<"username">>, <<"u_emqx">>}
|
|
||||||
, {<<"peerhost">>, <<"192.168.0.10">>}
|
|
||||||
, {<<"topic">>, <<"t/a">>}
|
|
||||||
, {<<"qos">>, 1}
|
|
||||||
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
|
||||||
, {<<"node">>, node()}
|
|
||||||
];
|
|
||||||
RuleType ->
|
|
||||||
error({unknown_rule_type, RuleType})
|
|
||||||
end).
|
|
||||||
|
|
||||||
-define(TEST_COLUMNS_MESSGE,
|
|
||||||
[ {<<"clientid">>, <<"c_emqx">>}
|
|
||||||
, {<<"username">>, <<"u_emqx">>}
|
|
||||||
, {<<"topic">>, <<"t/a">>}
|
|
||||||
, {<<"qos">>, 1}
|
|
||||||
, {<<"payload">>, <<"{\"msg\": \"hello\"}">>}
|
|
||||||
]).
|
|
||||||
|
|
||||||
-define(TEST_COLUMNS_MESSGE_DELIVERED_ACKED,
|
|
||||||
[ {<<"from_clientid">>, <<"c_emqx_1">>}
|
|
||||||
, {<<"from_username">>, <<"u_emqx_1">>}
|
|
||||||
, {<<"clientid">>, <<"c_emqx_2">>}
|
|
||||||
, {<<"username">>, <<"u_emqx_2">>}
|
|
||||||
, {<<"topic">>, <<"t/a">>}
|
|
||||||
, {<<"qos">>, 1}
|
|
||||||
, {<<"payload">>, <<"{\"msg\": \"hello\"}">>}
|
|
||||||
]).
|
|
||||||
|
|
||||||
-define(TEST_COLUMNS(EVENT),
|
|
||||||
case EVENT of
|
|
||||||
'message.publish' -> ?TEST_COLUMNS_MESSGE;
|
|
||||||
'message.dropped' -> ?TEST_COLUMNS_MESSGE;
|
|
||||||
'message.delivered' -> ?TEST_COLUMNS_MESSGE_DELIVERED_ACKED;
|
|
||||||
'message.acked' -> ?TEST_COLUMNS_MESSGE_DELIVERED_ACKED;
|
|
||||||
'client.connected' ->
|
|
||||||
[ {<<"clientid">>, <<"c_emqx">>}
|
|
||||||
, {<<"username">>, <<"u_emqx">>}
|
|
||||||
, {<<"peername">>, <<"127.0.0.1:52918">>}
|
|
||||||
];
|
|
||||||
'client.disconnected' ->
|
|
||||||
[ {<<"clientid">>, <<"c_emqx">>}
|
|
||||||
, {<<"username">>, <<"u_emqx">>}
|
|
||||||
, {<<"reason">>, <<"normal">>}
|
|
||||||
];
|
|
||||||
'session.subscribed' ->
|
|
||||||
[ {<<"clientid">>, <<"c_emqx">>}
|
|
||||||
, {<<"username">>, <<"u_emqx">>}
|
|
||||||
, {<<"topic">>, <<"t/a">>}
|
|
||||||
, {<<"qos">>, 1}
|
|
||||||
];
|
|
||||||
'session.unsubscribed' ->
|
|
||||||
[ {<<"clientid">>, <<"c_emqx">>}
|
|
||||||
, {<<"username">>, <<"u_emqx">>}
|
|
||||||
, {<<"topic">>, <<"t/a">>}
|
|
||||||
, {<<"qos">>, 1}
|
|
||||||
];
|
|
||||||
RuleType ->
|
|
||||||
error({unknown_rule_type, RuleType})
|
|
||||||
end).
|
|
||||||
|
|
||||||
-define(EVENT_INFO_MESSAGE_PUBLISH,
|
|
||||||
#{ event => '$events/message_publish',
|
|
||||||
title => #{en => <<"message publish">>, zh => <<"消息发布"/utf8>>},
|
|
||||||
description => #{en => <<"message publish">>, zh => <<"消息发布"/utf8>>},
|
|
||||||
test_columns => ?TEST_COLUMNS('message.publish'),
|
|
||||||
columns => ?COLUMNS('message.publish'),
|
|
||||||
sql_example => <<"SELECT payload.msg as msg FROM \"t/#\" WHERE msg = 'hello'">>
|
|
||||||
}).
|
|
||||||
|
|
||||||
-define(EVENT_INFO_MESSAGE_DELIVER,
|
|
||||||
#{ event => '$events/message_delivered',
|
|
||||||
title => #{en => <<"message delivered">>, zh => <<"消息投递"/utf8>>},
|
|
||||||
description => #{en => <<"message delivered">>, zh => <<"消息投递"/utf8>>},
|
|
||||||
test_columns => ?TEST_COLUMNS('message.delivered'),
|
|
||||||
columns => ?COLUMNS('message.delivered'),
|
|
||||||
sql_example => <<"SELECT * FROM \"$events/message_delivered\" WHERE topic =~ 't/#'">>
|
|
||||||
}).
|
|
||||||
|
|
||||||
-define(EVENT_INFO_MESSAGE_ACKED,
|
|
||||||
#{ event => '$events/message_acked',
|
|
||||||
title => #{en => <<"message acked">>, zh => <<"消息应答"/utf8>>},
|
|
||||||
description => #{en => <<"message acked">>, zh => <<"消息应答"/utf8>>},
|
|
||||||
test_columns => ?TEST_COLUMNS('message.acked'),
|
|
||||||
columns => ?COLUMNS('message.acked'),
|
|
||||||
sql_example => <<"SELECT * FROM \"$events/message_acked\" WHERE topic =~ 't/#'">>
|
|
||||||
}).
|
|
||||||
|
|
||||||
-define(EVENT_INFO_MESSAGE_DROPPED,
|
|
||||||
#{ event => '$events/message_dropped',
|
|
||||||
title => #{en => <<"message dropped">>, zh => <<"消息丢弃"/utf8>>},
|
|
||||||
description => #{en => <<"message dropped">>, zh => <<"消息丢弃"/utf8>>},
|
|
||||||
test_columns => ?TEST_COLUMNS('message.dropped'),
|
|
||||||
columns => ?COLUMNS('message.dropped'),
|
|
||||||
sql_example => <<"SELECT * FROM \"$events/message_dropped\" WHERE topic =~ 't/#'">>
|
|
||||||
}).
|
|
||||||
|
|
||||||
-define(EVENT_INFO_CLIENT_CONNECTED,
|
|
||||||
#{ event => '$events/client_connected',
|
|
||||||
title => #{en => <<"client connected">>, zh => <<"连接建立"/utf8>>},
|
|
||||||
description => #{en => <<"client connected">>, zh => <<"连接建立"/utf8>>},
|
|
||||||
test_columns => ?TEST_COLUMNS('client.connected'),
|
|
||||||
columns => ?COLUMNS('client.connected'),
|
|
||||||
sql_example => <<"SELECT * FROM \"$events/client_connected\"">>
|
|
||||||
}).
|
|
||||||
|
|
||||||
-define(EVENT_INFO_CLIENT_DISCONNECTED,
|
|
||||||
#{ event => '$events/client_disconnected',
|
|
||||||
title => #{en => <<"client disconnected">>, zh => <<"连接断开"/utf8>>},
|
|
||||||
description => #{en => <<"client disconnected">>, zh => <<"连接断开"/utf8>>},
|
|
||||||
test_columns => ?TEST_COLUMNS('client.disconnected'),
|
|
||||||
columns => ?COLUMNS('client.disconnected'),
|
|
||||||
sql_example => <<"SELECT * FROM \"$events/client_disconnected\"">>
|
|
||||||
}).
|
|
||||||
|
|
||||||
-define(EVENT_INFO_SESSION_SUBSCRIBED,
|
|
||||||
#{ event => '$events/session_subscribed',
|
|
||||||
title => #{en => <<"session subscribed">>, zh => <<"会话订阅完成"/utf8>>},
|
|
||||||
description => #{en => <<"session subscribed">>, zh => <<"会话订阅完成"/utf8>>},
|
|
||||||
test_columns => ?TEST_COLUMNS('session.subscribed'),
|
|
||||||
columns => ?COLUMNS('session.subscribed'),
|
|
||||||
sql_example => <<"SELECT * FROM \"$events/session_subscribed\" WHERE topic =~ 't/#'">>
|
|
||||||
}).
|
|
||||||
|
|
||||||
-define(EVENT_INFO_SESSION_UNSUBSCRIBED,
|
|
||||||
#{ event => '$events/session_unsubscribed',
|
|
||||||
title => #{en => <<"session unsubscribed">>, zh => <<"会话取消订阅完成"/utf8>>},
|
|
||||||
description => #{en => <<"session unsubscribed">>, zh => <<"会话取消订阅完成"/utf8>>},
|
|
||||||
test_columns => ?TEST_COLUMNS('session.unsubscribed'),
|
|
||||||
columns => ?COLUMNS('session.unsubscribed'),
|
|
||||||
sql_example => <<"SELECT * FROM \"$events/session_unsubscribed\" WHERE topic =~ 't/#'">>
|
|
||||||
}).
|
|
||||||
|
|
||||||
-define(EVENT_INFO,
|
|
||||||
[ ?EVENT_INFO_MESSAGE_PUBLISH
|
|
||||||
, ?EVENT_INFO_MESSAGE_DELIVER
|
|
||||||
, ?EVENT_INFO_MESSAGE_ACKED
|
|
||||||
, ?EVENT_INFO_MESSAGE_DROPPED
|
|
||||||
, ?EVENT_INFO_CLIENT_CONNECTED
|
|
||||||
, ?EVENT_INFO_CLIENT_DISCONNECTED
|
|
||||||
, ?EVENT_INFO_SESSION_SUBSCRIBED
|
|
||||||
, ?EVENT_INFO_SESSION_UNSUBSCRIBED
|
|
||||||
]).
|
|
||||||
|
|
||||||
-define(EG_ENVS(EVENT_TOPIC),
|
|
||||||
case EVENT_TOPIC of
|
|
||||||
<<"$events/", _/binary>> ->
|
|
||||||
EventName = emqx_rule_events:event_name(EVENT_TOPIC),
|
|
||||||
emqx_rule_maps:atom_key_map(maps:from_list(?EG_COLUMNS(EventName)));
|
|
||||||
_PublishTopic ->
|
|
||||||
#{id => emqx_guid:to_hexstr(emqx_guid:gen()),
|
|
||||||
clientid => <<"c_emqx">>,
|
|
||||||
username => <<"u_emqx">>,
|
|
||||||
payload => <<"{\"id\": 1, \"name\": \"ha\"}">>,
|
|
||||||
peerhost => <<"127.0.0.1">>,
|
|
||||||
topic => <<"t/a">>,
|
|
||||||
qos => 1,
|
|
||||||
flags => #{sys => true, event => true},
|
|
||||||
publish_received_at => emqx_rule_utils:now_ms(),
|
|
||||||
timestamp => emqx_rule_utils:now_ms(),
|
|
||||||
node => node()
|
|
||||||
}
|
|
||||||
end).
|
|
|
@ -106,7 +106,6 @@
|
||||||
, on_action_do_nothing/2
|
, on_action_do_nothing/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
|
||||||
-spec(on_resource_create(binary(), map()) -> map()).
|
-spec(on_resource_create(binary(), map()) -> map()).
|
||||||
on_resource_create(_Name, Conf) ->
|
on_resource_create(_Name, Conf) ->
|
||||||
Conf.
|
Conf.
|
||||||
|
@ -114,8 +113,7 @@ on_resource_create(_Name, Conf) ->
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Action 'inspect'
|
%% Action 'inspect'
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec on_action_create_inspect(action_instance_id(), Params :: map())
|
-spec on_action_create_inspect(action_instance_id(), Params :: map()) -> {bindings(), NewParams :: map()}.
|
||||||
-> NewParams :: map().
|
|
||||||
on_action_create_inspect(Id, Params) ->
|
on_action_create_inspect(Id, Params) ->
|
||||||
Params.
|
Params.
|
||||||
|
|
||||||
|
@ -131,8 +129,7 @@ on_action_inspect(Selected, Envs) ->
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Action 'republish'
|
%% Action 'republish'
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec on_action_create_republish(action_instance_id(), Params :: map())
|
-spec on_action_create_republish(action_instance_id(), Params :: map()) -> {bindings(), NewParams :: map()}.
|
||||||
-> NewParams :: map().
|
|
||||||
on_action_create_republish(Id, Params = #{
|
on_action_create_republish(Id, Params = #{
|
||||||
<<"target_topic">> := TargetTopic,
|
<<"target_topic">> := TargetTopic,
|
||||||
<<"target_qos">> := TargetQoS,
|
<<"target_qos">> := TargetQoS,
|
||||||
|
@ -203,8 +200,7 @@ increase_and_publish(ActId, Msg) ->
|
||||||
emqx_rule_metrics:inc_actions_success(ActId),
|
emqx_rule_metrics:inc_actions_success(ActId),
|
||||||
emqx_metrics:inc_msg(Msg).
|
emqx_metrics:inc_msg(Msg).
|
||||||
|
|
||||||
-spec on_action_create_do_nothing(action_instance_id(), Params :: map())
|
-spec on_action_create_do_nothing(action_instance_id(), Params :: map()) -> {bindings(), NewParams :: map()}.
|
||||||
-> NewParams :: map().
|
|
||||||
on_action_create_do_nothing(ActId, Params) when is_binary(ActId) ->
|
on_action_create_do_nothing(ActId, Params) when is_binary(ActId) ->
|
||||||
Params.
|
Params.
|
||||||
|
|
||||||
|
|
|
@ -72,8 +72,9 @@
|
||||||
%% Load all providers .
|
%% Load all providers .
|
||||||
-spec(load_providers() -> ok).
|
-spec(load_providers() -> ok).
|
||||||
load_providers() ->
|
load_providers() ->
|
||||||
[load_provider(App) || App <- ignore_lib_apps(application:loaded_applications())],
|
lists:foreach(fun(App) ->
|
||||||
ok.
|
load_provider(App)
|
||||||
|
end, ignore_lib_apps(application:loaded_applications())).
|
||||||
|
|
||||||
-spec(load_provider(App :: atom()) -> ok).
|
-spec(load_provider(App :: atom()) -> ok).
|
||||||
load_provider(App) when is_atom(App) ->
|
load_provider(App) when is_atom(App) ->
|
||||||
|
@ -86,8 +87,9 @@ load_provider(App) when is_atom(App) ->
|
||||||
%% Load all providers .
|
%% Load all providers .
|
||||||
-spec(unload_providers() -> ok).
|
-spec(unload_providers() -> ok).
|
||||||
unload_providers() ->
|
unload_providers() ->
|
||||||
[unload_provider(App) || App <- ignore_lib_apps(application:loaded_applications())],
|
lists:foreach(fun(App) ->
|
||||||
ok.
|
unload_provider(App)
|
||||||
|
end, ignore_lib_apps(application:loaded_applications())).
|
||||||
|
|
||||||
%% @doc Unload a provider.
|
%% @doc Unload a provider.
|
||||||
-spec(unload_provider(App :: atom()) -> ok).
|
-spec(unload_provider(App :: atom()) -> ok).
|
||||||
|
@ -147,8 +149,7 @@ find_attrs(App, Def) ->
|
||||||
module_attributes(Module) ->
|
module_attributes(Module) ->
|
||||||
try Module:module_info(attributes)
|
try Module:module_info(attributes)
|
||||||
catch
|
catch
|
||||||
error:undef -> [];
|
error:undef -> []
|
||||||
error:Reason -> error(Reason)
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
@ -156,37 +157,52 @@ module_attributes(Module) ->
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
-dialyzer([{nowarn_function, [create_rule/1, rule_id/0]}]).
|
-dialyzer([{nowarn_function, [create_rule/1, rule_id/0]}]).
|
||||||
-spec(create_rule(#{}) -> {ok, rule()} | no_return()).
|
-spec create_rule(map()) -> {ok, rule()} | {error, term()}.
|
||||||
create_rule(Params = #{rawsql := Sql, actions := Actions}) ->
|
create_rule(Params = #{rawsql := Sql, actions := ActArgs}) ->
|
||||||
case emqx_rule_sqlparser:parse_select(Sql) of
|
case emqx_rule_sqlparser:parse_select(Sql) of
|
||||||
{ok, Select} ->
|
{ok, Select} ->
|
||||||
RuleId = maps:get(id, Params, rule_id()),
|
RuleId = maps:get(id, Params, rule_id()),
|
||||||
Enabled = maps:get(enabled, Params, true),
|
Enabled = maps:get(enabled, Params, true),
|
||||||
Rule = #rule{id = RuleId,
|
try prepare_actions(ActArgs, Enabled) of
|
||||||
rawsql = Sql,
|
Actions ->
|
||||||
for = emqx_rule_sqlparser:select_from(Select),
|
Rule = #rule{
|
||||||
is_foreach = emqx_rule_sqlparser:select_is_foreach(Select),
|
id = RuleId,
|
||||||
fields = emqx_rule_sqlparser:select_fields(Select),
|
rawsql = Sql,
|
||||||
doeach = emqx_rule_sqlparser:select_doeach(Select),
|
for = emqx_rule_sqlparser:select_from(Select),
|
||||||
incase = emqx_rule_sqlparser:select_incase(Select),
|
is_foreach = emqx_rule_sqlparser:select_is_foreach(Select),
|
||||||
conditions = emqx_rule_sqlparser:select_where(Select),
|
fields = emqx_rule_sqlparser:select_fields(Select),
|
||||||
on_action_failed = maps:get(on_action_failed, Params, continue),
|
doeach = emqx_rule_sqlparser:select_doeach(Select),
|
||||||
actions = prepare_actions(Actions, Enabled),
|
incase = emqx_rule_sqlparser:select_incase(Select),
|
||||||
enabled = Enabled,
|
conditions = emqx_rule_sqlparser:select_where(Select),
|
||||||
description = maps:get(description, Params, "")},
|
on_action_failed = maps:get(on_action_failed, Params, continue),
|
||||||
ok = emqx_rule_registry:add_rule(Rule),
|
actions = Actions,
|
||||||
ok = emqx_rule_metrics:create_rule_metrics(RuleId),
|
enabled = Enabled,
|
||||||
{ok, Rule};
|
description = maps:get(description, Params, "")
|
||||||
Error -> error(Error)
|
},
|
||||||
|
ok = emqx_rule_registry:add_rule(Rule),
|
||||||
|
ok = emqx_rule_metrics:create_rule_metrics(RuleId),
|
||||||
|
{ok, Rule}
|
||||||
|
catch
|
||||||
|
throw:{action_not_found, ActionName} ->
|
||||||
|
{error, {action_not_found, ActionName}};
|
||||||
|
throw:Reason ->
|
||||||
|
{error, Reason}
|
||||||
|
end;
|
||||||
|
Reason -> {error, Reason}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec(update_rule(#{id := binary(), _=>_}) -> {ok, rule()} | {error, {not_found, rule_id()}}).
|
-spec(update_rule(#{id := binary(), _=>_}) -> {ok, rule()} | {error, {not_found, rule_id()}}).
|
||||||
update_rule(Params = #{id := RuleId}) ->
|
update_rule(Params = #{id := RuleId}) ->
|
||||||
case emqx_rule_registry:get_rule(RuleId) of
|
case emqx_rule_registry:get_rule(RuleId) of
|
||||||
{ok, Rule0} ->
|
{ok, Rule0} ->
|
||||||
Rule = may_update_rule_params(Rule0, Params),
|
try may_update_rule_params(Rule0, Params) of
|
||||||
ok = emqx_rule_registry:add_rule(Rule),
|
Rule ->
|
||||||
{ok, Rule};
|
ok = emqx_rule_registry:add_rule(Rule),
|
||||||
|
{ok, Rule}
|
||||||
|
catch
|
||||||
|
throw:Reason ->
|
||||||
|
{error, Reason}
|
||||||
|
end;
|
||||||
not_found ->
|
not_found ->
|
||||||
{error, {not_found, RuleId}}
|
{error, {not_found, RuleId}}
|
||||||
end.
|
end.
|
||||||
|
@ -234,9 +250,12 @@ start_resource(ResId) ->
|
||||||
{ok, #resource{type = ResType, config = Config}} ->
|
{ok, #resource{type = ResType, config = Config}} ->
|
||||||
{ok, #resource_type{on_create = {Mod, Create}}}
|
{ok, #resource_type{on_create = {Mod, Create}}}
|
||||||
= emqx_rule_registry:find_resource_type(ResType),
|
= emqx_rule_registry:find_resource_type(ResType),
|
||||||
_ = init_resource(Mod, Create, ResId, Config),
|
try
|
||||||
refresh_actions_of_a_resource(ResId),
|
init_resource(Mod, Create, ResId, Config),
|
||||||
ok;
|
refresh_actions_of_a_resource(ResId)
|
||||||
|
catch
|
||||||
|
throw:Reason -> {error, Reason}
|
||||||
|
end;
|
||||||
not_found ->
|
not_found ->
|
||||||
{error, {resource_not_found, ResId}}
|
{error, {resource_not_found, ResId}}
|
||||||
end.
|
end.
|
||||||
|
@ -247,9 +266,12 @@ test_resource(#{type := Type, config := Config}) ->
|
||||||
{ok, #resource_type{on_create = {ModC,Create}, on_destroy = {ModD,Destroy}, params_spec = ParamSpec}} ->
|
{ok, #resource_type{on_create = {ModC,Create}, on_destroy = {ModD,Destroy}, params_spec = ParamSpec}} ->
|
||||||
ok = emqx_rule_validator:validate_params(Config, ParamSpec),
|
ok = emqx_rule_validator:validate_params(Config, ParamSpec),
|
||||||
ResId = resource_id(),
|
ResId = resource_id(),
|
||||||
cluster_call(init_resource, [ModC, Create, ResId, Config]),
|
try
|
||||||
cluster_call(clear_resource, [ModD, Destroy, ResId]),
|
cluster_call(init_resource, [ModC, Create, ResId, Config]),
|
||||||
ok;
|
cluster_call(clear_resource, [ModD, Destroy, ResId])
|
||||||
|
catch
|
||||||
|
throw:Reason -> {error, Reason}
|
||||||
|
end;
|
||||||
not_found ->
|
not_found ->
|
||||||
{error, {resource_type_not_found, Type}}
|
{error, {resource_type_not_found, Type}}
|
||||||
end.
|
end.
|
||||||
|
@ -299,36 +321,37 @@ ensure_resource_deleted(ResId) ->
|
||||||
|
|
||||||
-spec(refresh_resources() -> ok).
|
-spec(refresh_resources() -> ok).
|
||||||
refresh_resources() ->
|
refresh_resources() ->
|
||||||
[try refresh_resource(Res)
|
lists:foreach(fun(#resource{id = ResId} = Res) ->
|
||||||
catch _:Error:ST ->
|
try refresh_resource(Res)
|
||||||
logger:critical(
|
catch Error:Reason:ST ->
|
||||||
"Can not re-stablish resource ~p: ~0p. The resource is disconnected."
|
logger:critical(
|
||||||
"Fix the issue and establish it manually.\n"
|
"Can not re-stablish resource ~p: ~0p. The resource is disconnected."
|
||||||
"Stacktrace: ~0p",
|
"Fix the issue and establish it manually.\n"
|
||||||
[ResId, Error, ST])
|
"Stacktrace: ~0p",
|
||||||
end || Res = #resource{id = ResId}
|
[ResId, {Error,Reason}, ST])
|
||||||
<- emqx_rule_registry:get_resources()],
|
end
|
||||||
ok.
|
end, emqx_rule_registry:get_resources()).
|
||||||
|
|
||||||
refresh_resource(Type) when is_atom(Type) ->
|
refresh_resource(Type) when is_atom(Type) ->
|
||||||
[refresh_resource(Resource)
|
lists:foreach(fun(Resource) ->
|
||||||
|| Resource <- emqx_rule_registry:get_resources_by_type(Type)];
|
refresh_resource(Resource)
|
||||||
|
end, emqx_rule_registry:get_resources_by_type(Type));
|
||||||
refresh_resource(#resource{id = ResId, config = Config, type = Type}) ->
|
refresh_resource(#resource{id = ResId, config = Config, type = Type}) ->
|
||||||
{ok, #resource_type{on_create = {M, F}}} = emqx_rule_registry:find_resource_type(Type),
|
{ok, #resource_type{on_create = {M, F}}} = emqx_rule_registry:find_resource_type(Type),
|
||||||
cluster_call(init_resource, [M, F, ResId, Config]).
|
cluster_call(init_resource, [M, F, ResId, Config]).
|
||||||
|
|
||||||
-spec(refresh_rules() -> ok).
|
-spec(refresh_rules() -> ok).
|
||||||
refresh_rules() ->
|
refresh_rules() ->
|
||||||
[try refresh_rule(Rule)
|
lists:foreach(fun(#rule{id = RuleId} = Rule) ->
|
||||||
catch _:Error:ST ->
|
try refresh_rule(Rule)
|
||||||
logger:critical(
|
catch Error:Reason:ST ->
|
||||||
"Can not re-build rule ~p: ~0p. The rule is disabled."
|
logger:critical(
|
||||||
"Fix the issue and enable it manually.\n"
|
"Can not re-build rule ~p: ~0p. The rule is disabled."
|
||||||
"Stacktrace: ~0p",
|
"Fix the issue and enable it manually.\n"
|
||||||
[RuleId, Error, ST])
|
"Stacktrace: ~0p",
|
||||||
end || Rule = #rule{id = RuleId}
|
[RuleId, {Error,Reason}, ST])
|
||||||
<- emqx_rule_registry:get_rules()],
|
end
|
||||||
ok.
|
end, emqx_rule_registry:get_rules()).
|
||||||
|
|
||||||
refresh_rule(#rule{id = RuleId, actions = Actions}) ->
|
refresh_rule(#rule{id = RuleId, actions = Actions}) ->
|
||||||
ok = emqx_rule_metrics:create_rule_metrics(RuleId),
|
ok = emqx_rule_metrics:create_rule_metrics(RuleId),
|
||||||
|
@ -389,7 +412,7 @@ may_update_rule_params(Rule, Params = #{rawsql := SQL}) ->
|
||||||
conditions = emqx_rule_sqlparser:select_where(Select)
|
conditions = emqx_rule_sqlparser:select_where(Select)
|
||||||
},
|
},
|
||||||
maps:remove(rawsql, Params));
|
maps:remove(rawsql, Params));
|
||||||
Error -> error(Error)
|
Reason -> throw(Reason)
|
||||||
end;
|
end;
|
||||||
may_update_rule_params(Rule, Params = #{enabled := Enabled}) ->
|
may_update_rule_params(Rule, Params = #{enabled := Enabled}) ->
|
||||||
Enabled andalso refresh_rule(Rule),
|
Enabled andalso refresh_rule(Rule),
|
||||||
|
@ -443,7 +466,7 @@ cluster_call(Func, Args) ->
|
||||||
end;
|
end;
|
||||||
{ResL, BadNodes} ->
|
{ResL, BadNodes} ->
|
||||||
?LOG(error, "cluster_call bad nodes found: ~p, ResL: ~p", [BadNodes, ResL]),
|
?LOG(error, "cluster_call bad nodes found: ~p, ResL: ~p", [BadNodes, ResL]),
|
||||||
throw({func_fail(Func), {nodes_not_exist, BadNodes}})
|
throw({func_fail(Func), {failed_on_nodes, BadNodes}})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
init_resource(Module, OnCreate, ResId, Config) ->
|
init_resource(Module, OnCreate, ResId, Config) ->
|
||||||
|
|
|
@ -17,13 +17,11 @@
|
||||||
-module(emqx_rule_engine_api).
|
-module(emqx_rule_engine_api).
|
||||||
|
|
||||||
-include("rule_engine.hrl").
|
-include("rule_engine.hrl").
|
||||||
-include("rule_events.hrl").
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
|
||||||
-import(minirest, [return/1]).
|
-logger_header("[RuleEngineAPI]").
|
||||||
|
|
||||||
%% A lot of case clause no_match:es from rule_events.hrl
|
-import(minirest, [return/1]).
|
||||||
-dialyzer(no_match).
|
|
||||||
|
|
||||||
-rest_api(#{name => create_rule,
|
-rest_api(#{name => create_rule,
|
||||||
method => 'POST',
|
method => 'POST',
|
||||||
|
@ -173,16 +171,19 @@
|
||||||
-define(ERR_NO_RULE(ID), list_to_binary(io_lib:format("Rule ~s Not Found", [(ID)]))).
|
-define(ERR_NO_RULE(ID), list_to_binary(io_lib:format("Rule ~s Not Found", [(ID)]))).
|
||||||
-define(ERR_NO_ACTION(NAME), list_to_binary(io_lib:format("Action ~s Not Found", [(NAME)]))).
|
-define(ERR_NO_ACTION(NAME), list_to_binary(io_lib:format("Action ~s Not Found", [(NAME)]))).
|
||||||
-define(ERR_NO_RESOURCE(RESID), list_to_binary(io_lib:format("Resource ~s Not Found", [(RESID)]))).
|
-define(ERR_NO_RESOURCE(RESID), list_to_binary(io_lib:format("Resource ~s Not Found", [(RESID)]))).
|
||||||
-define(ERR_NO_HOOK(HOOK), list_to_binary(io_lib:format("Event ~s Not Found", [(HOOK)]))).
|
|
||||||
-define(ERR_NO_RESOURCE_TYPE(TYPE), list_to_binary(io_lib:format("Resource Type ~s Not Found", [(TYPE)]))).
|
-define(ERR_NO_RESOURCE_TYPE(TYPE), list_to_binary(io_lib:format("Resource Type ~s Not Found", [(TYPE)]))).
|
||||||
-define(ERR_UNKNOWN_COLUMN(COLUMN), list_to_binary(io_lib:format("Unknown Column: ~s", [(COLUMN)]))).
|
|
||||||
-define(ERR_START_RESOURCE(RESID), list_to_binary(io_lib:format("Start Resource ~s Failed", [(RESID)]))).
|
|
||||||
-define(ERR_BADARGS(REASON),
|
-define(ERR_BADARGS(REASON),
|
||||||
begin
|
begin
|
||||||
R0 = list_to_binary(io_lib:format("~0p", [REASON])),
|
R0 = list_to_binary(io_lib:format("~0p", [REASON])),
|
||||||
<<"Bad Arguments: ", R0/binary>>
|
<<"Bad Arguments: ", R0/binary>>
|
||||||
end).
|
end).
|
||||||
|
|
||||||
|
-dialyzer({nowarn_function, [create_rule/2,
|
||||||
|
test_rule_sql/1,
|
||||||
|
do_create_rule/1,
|
||||||
|
update_rule/2
|
||||||
|
]}).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Rules API
|
%% Rules API
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
@ -192,60 +193,31 @@ create_rule(_Bindings, Params) ->
|
||||||
Params).
|
Params).
|
||||||
|
|
||||||
test_rule_sql(Params) ->
|
test_rule_sql(Params) ->
|
||||||
try emqx_rule_sqltester:test(emqx_json:decode(emqx_json:encode(Params), [return_maps])) of
|
case emqx_rule_sqltester:test(emqx_json:decode(emqx_json:encode(Params), [return_maps])) of
|
||||||
{ok, Result} -> return({ok, Result});
|
{ok, Result} -> return({ok, Result});
|
||||||
{error, nomatch} -> return({error, 404, <<"SQL Not Match">>})
|
{error, nomatch} -> return({error, 404, <<"SQL Not Match">>});
|
||||||
catch
|
{error, Reason} ->
|
||||||
throw:{invalid_hook, Hook} ->
|
?LOG(error, "~p failed: ~0p", [?FUNCTION_NAME, Reason]),
|
||||||
return({error, 400, ?ERR_NO_HOOK(Hook)});
|
|
||||||
throw:Reason ->
|
|
||||||
return({error, 400, ?ERR_BADARGS(Reason)});
|
|
||||||
_:{parse_error,{unknown_column, Column}, _} ->
|
|
||||||
return({error, 400, ?ERR_UNKNOWN_COLUMN(Column)});
|
|
||||||
_Error:Reason:StackT ->
|
|
||||||
?LOG(error, "[RuleEngineAPI] ~p failed: ~0p", [?FUNCTION_NAME, {Reason, StackT}]),
|
|
||||||
return({error, 400, ?ERR_BADARGS(Reason)})
|
return({error, 400, ?ERR_BADARGS(Reason)})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
do_create_rule(Params) ->
|
do_create_rule(Params) ->
|
||||||
try emqx_rule_engine:create_rule(parse_rule_params(Params)) of
|
case emqx_rule_engine:create_rule(parse_rule_params(Params)) of
|
||||||
{ok, Rule} ->
|
{ok, Rule} -> return({ok, record_to_map(Rule)});
|
||||||
return({ok, record_to_map(Rule)});
|
|
||||||
{error, {action_not_found, ActionName}} ->
|
{error, {action_not_found, ActionName}} ->
|
||||||
return({error, 400, ?ERR_NO_ACTION(ActionName)})
|
return({error, 400, ?ERR_NO_ACTION(ActionName)});
|
||||||
catch
|
{error, Reason} ->
|
||||||
throw:{resource_not_found, ResId} ->
|
?LOG(error, "~p failed: ~0p", [?FUNCTION_NAME, Reason]),
|
||||||
return({error, 400, ?ERR_NO_RESOURCE(ResId)});
|
|
||||||
throw:{invalid_hook, Hook} ->
|
|
||||||
return({error, 400, ?ERR_NO_HOOK(Hook)});
|
|
||||||
throw:Reason ->
|
|
||||||
return({error, 400, ?ERR_BADARGS(Reason)});
|
|
||||||
_:{parse_error,{unknown_column, Column}} ->
|
|
||||||
return({error, 400, ?ERR_UNKNOWN_COLUMN(Column)});
|
|
||||||
_Error:Reason:StackT ->
|
|
||||||
?LOG(error, "[RuleEngineAPI] ~p failed: ~0p", [?FUNCTION_NAME, {Reason, StackT}]),
|
|
||||||
return({error, 400, ?ERR_BADARGS(Reason)})
|
return({error, 400, ?ERR_BADARGS(Reason)})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
update_rule(#{id := Id}, Params) ->
|
update_rule(#{id := Id}, Params) ->
|
||||||
try emqx_rule_engine:update_rule(parse_rule_params(Params, #{id => Id})) of
|
case emqx_rule_engine:update_rule(parse_rule_params(Params, #{id => Id})) of
|
||||||
{ok, Rule} ->
|
{ok, Rule} -> return({ok, record_to_map(Rule)});
|
||||||
return({ok, record_to_map(Rule)});
|
|
||||||
{error, {not_found, RuleId}} ->
|
{error, {not_found, RuleId}} ->
|
||||||
return({error, 400, ?ERR_NO_RULE(RuleId)});
|
return({error, 400, ?ERR_NO_RULE(RuleId)});
|
||||||
{error, {action_not_found, ActionName}} ->
|
{error, Reason} ->
|
||||||
return({error, 400, ?ERR_NO_ACTION(ActionName)})
|
?LOG(error, "~p failed: ~0p", [?FUNCTION_NAME, Reason]),
|
||||||
catch
|
|
||||||
throw:{resource_not_found, ResId} ->
|
|
||||||
return({error, 400, ?ERR_NO_RESOURCE(ResId)});
|
|
||||||
throw:{invalid_hook, Hook} ->
|
|
||||||
return({error, 400, ?ERR_NO_HOOK(Hook)});
|
|
||||||
throw:Reason ->
|
|
||||||
return({error, 400, ?ERR_BADARGS(Reason)});
|
|
||||||
_:{parse_error,{unknown_column, Column}} ->
|
|
||||||
return({error, 400, ?ERR_UNKNOWN_COLUMN(Column)});
|
|
||||||
_Error:Reason:StackT ->
|
|
||||||
?LOG(error, "[RuleEngineAPI] ~p failed: ~0p", [?FUNCTION_NAME, {Reason, StackT}]),
|
|
||||||
return({error, 400, ?ERR_BADARGS(Reason)})
|
return({error, 400, ?ERR_BADARGS(Reason)})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -264,7 +236,9 @@ delete_rule(#{id := Id}, _Params) ->
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
list_actions(#{}, _Params) ->
|
list_actions(#{}, _Params) ->
|
||||||
return_all(emqx_rule_registry:get_actions()).
|
return_all(
|
||||||
|
sort_by_title(action,
|
||||||
|
emqx_rule_registry:get_actions())).
|
||||||
|
|
||||||
show_action(#{name := Name}, _Params) ->
|
show_action(#{name := Name}, _Params) ->
|
||||||
reply_with(fun emqx_rule_registry:find_action/1, Name).
|
reply_with(fun emqx_rule_registry:find_action/1, Name).
|
||||||
|
@ -278,24 +252,15 @@ create_resource(#{}, Params) ->
|
||||||
Params).
|
Params).
|
||||||
|
|
||||||
do_create_resource(Create, Params) ->
|
do_create_resource(Create, Params) ->
|
||||||
try emqx_rule_engine:Create(parse_resource_params(Params)) of
|
case emqx_rule_engine:Create(parse_resource_params(Params)) of
|
||||||
ok ->
|
ok ->
|
||||||
return(ok);
|
return(ok);
|
||||||
{ok, Resource} ->
|
{ok, Resource} ->
|
||||||
return({ok, record_to_map(Resource)});
|
return({ok, record_to_map(Resource)});
|
||||||
{error, {resource_type_not_found, Type}} ->
|
{error, {resource_type_not_found, Type}} ->
|
||||||
return({error, 400, ?ERR_NO_RESOURCE_TYPE(Type)})
|
|
||||||
catch
|
|
||||||
throw:{resource_type_not_found, Type} ->
|
|
||||||
return({error, 400, ?ERR_NO_RESOURCE_TYPE(Type)});
|
return({error, 400, ?ERR_NO_RESOURCE_TYPE(Type)});
|
||||||
throw:{init_resource_failure, Reason} ->
|
{error, Reason} ->
|
||||||
%% only test_resource would throw exceptions, create_resource won't
|
?LOG(error, "~p failed: ~0p", [?FUNCTION_NAME, Reason]),
|
||||||
?LOG(error, "[RuleEngineAPI] test_resource_failure: ~p", [Reason]),
|
|
||||||
return({error, 500, <<"Test Creating Resource Failed">>});
|
|
||||||
throw:Reason ->
|
|
||||||
return({error, 400, ?ERR_BADARGS(Reason)});
|
|
||||||
_Error:Reason:StackT ->
|
|
||||||
?LOG(error, "[RuleEngineAPI] ~p failed: ~0p", [?FUNCTION_NAME, {Reason, StackT}]),
|
|
||||||
return({error, 400, ?ERR_BADARGS(Reason)})
|
return({error, 400, ?ERR_BADARGS(Reason)})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -337,35 +302,19 @@ get_resource_status(#{id := Id}, _Params) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
start_resource(#{id := Id}, _Params) ->
|
start_resource(#{id := Id}, _Params) ->
|
||||||
try emqx_rule_engine:start_resource(Id) of
|
case emqx_rule_engine:start_resource(Id) of
|
||||||
ok ->
|
ok ->
|
||||||
return(ok);
|
return(ok);
|
||||||
{error, {resource_not_found, ResId}} ->
|
{error, {resource_not_found, ResId}} ->
|
||||||
return({error, 400, ?ERR_NO_RESOURCE(ResId)})
|
return({error, 400, ?ERR_NO_RESOURCE(ResId)});
|
||||||
catch
|
{error, Reason} ->
|
||||||
throw:{{init_resource_failure, _}, Reason} ->
|
?LOG(error, "~p failed: ~0p", [?FUNCTION_NAME, Reason]),
|
||||||
?LOG(error, "[RuleEngineAPI] init_resource_failure: ~p", [Reason]),
|
|
||||||
return({error, 400, ?ERR_START_RESOURCE(Id)});
|
|
||||||
throw:Reason ->
|
|
||||||
return({error, 400, ?ERR_BADARGS(Reason)});
|
|
||||||
_Error:Reason:StackT ->
|
|
||||||
?LOG(error, "[RuleEngineAPI] ~p failed: ~0p", [?FUNCTION_NAME, {Reason, StackT}]),
|
|
||||||
return({error, 400, ?ERR_BADARGS(Reason)})
|
return({error, 400, ?ERR_BADARGS(Reason)})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
delete_resource(#{id := Id}, _Params) ->
|
delete_resource(#{id := Id}, _Params) ->
|
||||||
try
|
ok = emqx_rule_engine:ensure_resource_deleted(Id),
|
||||||
ok = emqx_rule_engine:ensure_resource_deleted(Id),
|
return(ok).
|
||||||
return(ok)
|
|
||||||
catch
|
|
||||||
_Error:{throw,Reason} ->
|
|
||||||
return({error, 400, ?ERR_BADARGS(Reason)});
|
|
||||||
throw:Reason ->
|
|
||||||
return({error, 400, ?ERR_BADARGS(Reason)});
|
|
||||||
_Error:Reason:StackT ->
|
|
||||||
?LOG(error, "[RuleEngineAPI] ~p failed: ~0p", [?FUNCTION_NAME, {Reason, StackT}]),
|
|
||||||
return({error, 400, ?ERR_BADARGS(Reason)})
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Resource Types API
|
%% Resource Types API
|
||||||
|
@ -385,7 +334,7 @@ show_resource_type(#{name := Name}, _Params) ->
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
list_events(#{}, _Params) ->
|
list_events(#{}, _Params) ->
|
||||||
return({ok, ?EVENT_INFO}).
|
return({ok, emqx_rule_events:event_info()}).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
|
|
|
@ -91,7 +91,7 @@ unload() ->
|
||||||
%%-----------------------------------------------------------------------------
|
%%-----------------------------------------------------------------------------
|
||||||
%% 'rules' command
|
%% 'rules' command
|
||||||
%%-----------------------------------------------------------------------------
|
%%-----------------------------------------------------------------------------
|
||||||
|
-dialyzer([{nowarn_function, [rules/1]}]).
|
||||||
rules(["list"]) ->
|
rules(["list"]) ->
|
||||||
print_all(emqx_rule_registry:get_rules());
|
print_all(emqx_rule_registry:get_rules());
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,11 @@
|
||||||
, on_message_acked/3
|
, on_message_acked/3
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
-export([ event_info/0
|
||||||
|
, columns/1
|
||||||
|
, columns_with_exam/1
|
||||||
|
]).
|
||||||
|
|
||||||
-define(SUPPORTED_HOOK,
|
-define(SUPPORTED_HOOK,
|
||||||
[ 'client.connected'
|
[ 'client.connected'
|
||||||
, 'client.disconnected'
|
, 'client.disconnected'
|
||||||
|
@ -309,6 +314,239 @@ make_msg(QoS, Topic, Payload) ->
|
||||||
emqx_message:set_flags(#{sys => true, event => true},
|
emqx_message:set_flags(#{sys => true, event => true},
|
||||||
emqx_message:make(emqx_events, QoS, Topic, iolist_to_binary(Payload))).
|
emqx_message:make(emqx_events, QoS, Topic, iolist_to_binary(Payload))).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Columns
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
columns(Event) ->
|
||||||
|
[Key || {Key, _ExampleVal} <- columns_with_exam(Event)].
|
||||||
|
|
||||||
|
event_info() ->
|
||||||
|
[ event_info_message_publish()
|
||||||
|
, event_info_message_deliver()
|
||||||
|
, event_info_message_acked()
|
||||||
|
, event_info_message_dropped()
|
||||||
|
, event_info_client_connected()
|
||||||
|
, event_info_client_disconnected()
|
||||||
|
, event_info_session_subscribed()
|
||||||
|
, event_info_session_unsubscribed()
|
||||||
|
].
|
||||||
|
|
||||||
|
event_info_message_publish() ->
|
||||||
|
event_info_common(
|
||||||
|
'message.publish',
|
||||||
|
{<<"message publish">>, <<"消息发布"/utf8>>},
|
||||||
|
{<<"message publish">>, <<"消息发布"/utf8>>},
|
||||||
|
<<"SELECT payload.msg as msg FROM \"t/#\" WHERE msg = 'hello'">>
|
||||||
|
).
|
||||||
|
event_info_message_deliver() ->
|
||||||
|
event_info_common(
|
||||||
|
'message.delivered',
|
||||||
|
{<<"message delivered">>, <<"消息已投递"/utf8>>},
|
||||||
|
{<<"message delivered">>, <<"消息已投递"/utf8>>},
|
||||||
|
<<"SELECT * FROM \"$events/message_delivered\" WHERE topic =~ 't/#'">>
|
||||||
|
).
|
||||||
|
event_info_message_acked() ->
|
||||||
|
event_info_common(
|
||||||
|
'message.acked',
|
||||||
|
{<<"message acked">>, <<"消息应答"/utf8>>},
|
||||||
|
{<<"message acked">>, <<"消息应答"/utf8>>},
|
||||||
|
<<"SELECT * FROM \"$events/message_acked\" WHERE topic =~ 't/#'">>
|
||||||
|
).
|
||||||
|
event_info_message_dropped() ->
|
||||||
|
event_info_common(
|
||||||
|
'message.dropped',
|
||||||
|
{<<"message dropped">>, <<"消息丢弃"/utf8>>},
|
||||||
|
{<<"message dropped">>, <<"消息丢弃"/utf8>>},
|
||||||
|
<<"SELECT * FROM \"$events/message_dropped\" WHERE topic =~ 't/#'">>
|
||||||
|
).
|
||||||
|
event_info_client_connected() ->
|
||||||
|
event_info_common(
|
||||||
|
'client.connected',
|
||||||
|
{<<"client connected">>, <<"连接建立"/utf8>>},
|
||||||
|
{<<"client connected">>, <<"连接建立"/utf8>>},
|
||||||
|
<<"SELECT * FROM \"$events/client_connected\"">>
|
||||||
|
).
|
||||||
|
event_info_client_disconnected() ->
|
||||||
|
event_info_common(
|
||||||
|
'client.disconnected',
|
||||||
|
{<<"client disconnected">>, <<"连接断开"/utf8>>},
|
||||||
|
{<<"client disconnected">>, <<"连接断开"/utf8>>},
|
||||||
|
<<"SELECT * FROM \"$events/client_disconnected\" WHERE topic =~ 't/#'">>
|
||||||
|
).
|
||||||
|
event_info_session_subscribed() ->
|
||||||
|
event_info_common(
|
||||||
|
'session.subscribed',
|
||||||
|
{<<"session subscribed">>, <<"会话订阅完成"/utf8>>},
|
||||||
|
{<<"session subscribed">>, <<"会话订阅完成"/utf8>>},
|
||||||
|
<<"SELECT * FROM \"$events/session_subscribed\" WHERE topic =~ 't/#'">>
|
||||||
|
).
|
||||||
|
event_info_session_unsubscribed() ->
|
||||||
|
event_info_common(
|
||||||
|
'session.unsubscribed',
|
||||||
|
{<<"session unsubscribed">>, <<"会话取消订阅完成"/utf8>>},
|
||||||
|
{<<"session unsubscribed">>, <<"会话取消订阅完成"/utf8>>},
|
||||||
|
<<"SELECT * FROM \"$events/session_unsubscribed\" WHERE topic =~ 't/#'">>
|
||||||
|
).
|
||||||
|
|
||||||
|
event_info_common(Event, {TitleEN, TitleZH}, {DescrEN, DescrZH}, SqlExam) ->
|
||||||
|
#{event => event_topic(Event),
|
||||||
|
title => #{en => TitleEN, zh => TitleZH},
|
||||||
|
description => #{en => DescrEN, zh => DescrZH},
|
||||||
|
test_columns => test_columns(Event),
|
||||||
|
columns => columns(Event),
|
||||||
|
sql_example => SqlExam
|
||||||
|
}.
|
||||||
|
|
||||||
|
test_columns('message.dropped') ->
|
||||||
|
test_columns('message.publish');
|
||||||
|
test_columns('message.publish') ->
|
||||||
|
[ {<<"clientid">>, <<"c_emqx">>}
|
||||||
|
, {<<"username">>, <<"u_emqx">>}
|
||||||
|
, {<<"topic">>, <<"t/a">>}
|
||||||
|
, {<<"qos">>, 1}
|
||||||
|
, {<<"payload">>, <<"{\"msg\": \"hello\"}">>}
|
||||||
|
];
|
||||||
|
test_columns('message.acked') ->
|
||||||
|
test_columns('message.delivered');
|
||||||
|
test_columns('message.delivered') ->
|
||||||
|
[ {<<"from_clientid">>, <<"c_emqx_1">>}
|
||||||
|
, {<<"from_username">>, <<"u_emqx_1">>}
|
||||||
|
, {<<"clientid">>, <<"c_emqx_2">>}
|
||||||
|
, {<<"username">>, <<"u_emqx_2">>}
|
||||||
|
, {<<"topic">>, <<"t/a">>}
|
||||||
|
, {<<"qos">>, 1}
|
||||||
|
, {<<"payload">>, <<"{\"msg\": \"hello\"}">>}
|
||||||
|
];
|
||||||
|
test_columns('client.connected') ->
|
||||||
|
[ {<<"clientid">>, <<"c_emqx">>}
|
||||||
|
, {<<"username">>, <<"u_emqx">>}
|
||||||
|
, {<<"peername">>, <<"127.0.0.1:52918">>}
|
||||||
|
];
|
||||||
|
test_columns('client.disconnected') ->
|
||||||
|
[ {<<"clientid">>, <<"c_emqx">>}
|
||||||
|
, {<<"username">>, <<"u_emqx">>}
|
||||||
|
, {<<"reason">>, <<"normal">>}
|
||||||
|
];
|
||||||
|
test_columns('session.unsubscribed') ->
|
||||||
|
test_columns('session.subscribed');
|
||||||
|
test_columns('session.subscribed') ->
|
||||||
|
[ {<<"clientid">>, <<"c_emqx">>}
|
||||||
|
, {<<"username">>, <<"u_emqx">>}
|
||||||
|
, {<<"topic">>, <<"t/a">>}
|
||||||
|
, {<<"qos">>, 1}
|
||||||
|
].
|
||||||
|
|
||||||
|
columns_with_exam('message.publish') ->
|
||||||
|
[ {<<"event">>, 'message.publish'}
|
||||||
|
, {<<"id">>, emqx_guid:to_hexstr(emqx_guid:gen())}
|
||||||
|
, {<<"clientid">>, <<"c_emqx">>}
|
||||||
|
, {<<"username">>, <<"u_emqx">>}
|
||||||
|
, {<<"payload">>, <<"{\"msg\": \"hello\"}">>}
|
||||||
|
, {<<"peerhost">>, <<"192.168.0.10">>}
|
||||||
|
, {<<"topic">>, <<"t/a">>}
|
||||||
|
, {<<"qos">>, 1}
|
||||||
|
, {<<"flags">>, #{}}
|
||||||
|
, {<<"headers">>, undefined}
|
||||||
|
, {<<"publish_received_at">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"node">>, node()}
|
||||||
|
];
|
||||||
|
columns_with_exam('message.delivered') ->
|
||||||
|
[ {<<"event">>, 'message.delivered'}
|
||||||
|
, {<<"id">>, emqx_guid:to_hexstr(emqx_guid:gen())}
|
||||||
|
, {<<"from_clientid">>, <<"c_emqx_1">>}
|
||||||
|
, {<<"from_username">>, <<"u_emqx_1">>}
|
||||||
|
, {<<"clientid">>, <<"c_emqx_2">>}
|
||||||
|
, {<<"username">>, <<"u_emqx_2">>}
|
||||||
|
, {<<"payload">>, <<"{\"msg\": \"hello\"}">>}
|
||||||
|
, {<<"peerhost">>, <<"192.168.0.10">>}
|
||||||
|
, {<<"topic">>, <<"t/a">>}
|
||||||
|
, {<<"qos">>, 1}
|
||||||
|
, {<<"flags">>, #{}}
|
||||||
|
, {<<"publish_received_at">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"node">>, node()}
|
||||||
|
];
|
||||||
|
columns_with_exam('message.acked') ->
|
||||||
|
[ {<<"event">>, 'message.acked'}
|
||||||
|
, {<<"id">>, emqx_guid:to_hexstr(emqx_guid:gen())}
|
||||||
|
, {<<"from_clientid">>, <<"c_emqx_1">>}
|
||||||
|
, {<<"from_username">>, <<"u_emqx_1">>}
|
||||||
|
, {<<"clientid">>, <<"c_emqx_2">>}
|
||||||
|
, {<<"username">>, <<"u_emqx_2">>}
|
||||||
|
, {<<"payload">>, <<"{\"msg\": \"hello\"}">>}
|
||||||
|
, {<<"peerhost">>, <<"192.168.0.10">>}
|
||||||
|
, {<<"topic">>, <<"t/a">>}
|
||||||
|
, {<<"qos">>, 1}
|
||||||
|
, {<<"flags">>, #{}}
|
||||||
|
, {<<"publish_received_at">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"node">>, node()}
|
||||||
|
];
|
||||||
|
columns_with_exam('message.dropped') ->
|
||||||
|
[ {<<"event">>, 'message.dropped'}
|
||||||
|
, {<<"id">>, emqx_guid:to_hexstr(emqx_guid:gen())}
|
||||||
|
, {<<"reason">>, no_subscribers}
|
||||||
|
, {<<"clientid">>, <<"c_emqx">>}
|
||||||
|
, {<<"username">>, <<"u_emqx">>}
|
||||||
|
, {<<"payload">>, <<"{\"msg\": \"hello\"}">>}
|
||||||
|
, {<<"peerhost">>, <<"192.168.0.10">>}
|
||||||
|
, {<<"topic">>, <<"t/a">>}
|
||||||
|
, {<<"qos">>, 1}
|
||||||
|
, {<<"flags">>, #{}}
|
||||||
|
, {<<"publish_received_at">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"node">>, node()}
|
||||||
|
];
|
||||||
|
columns_with_exam('client.connected') ->
|
||||||
|
[ {<<"event">>, 'client.connected'}
|
||||||
|
, {<<"clientid">>, <<"c_emqx">>}
|
||||||
|
, {<<"username">>, <<"u_emqx">>}
|
||||||
|
, {<<"mountpoint">>, undefined}
|
||||||
|
, {<<"peername">>, <<"192.168.0.10:56431">>}
|
||||||
|
, {<<"sockname">>, <<"0.0.0.0:1883">>}
|
||||||
|
, {<<"proto_name">>, <<"MQTT">>}
|
||||||
|
, {<<"proto_ver">>, 5}
|
||||||
|
, {<<"keepalive">>, 60}
|
||||||
|
, {<<"clean_start">>, true}
|
||||||
|
, {<<"expiry_interval">>, 3600}
|
||||||
|
, {<<"is_bridge">>, false}
|
||||||
|
, {<<"connected_at">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"node">>, node()}
|
||||||
|
];
|
||||||
|
columns_with_exam('client.disconnected') ->
|
||||||
|
[ {<<"event">>, 'client.disconnected'}
|
||||||
|
, {<<"reason">>, normal}
|
||||||
|
, {<<"clientid">>, <<"c_emqx">>}
|
||||||
|
, {<<"username">>, <<"u_emqx">>}
|
||||||
|
, {<<"peername">>, <<"192.168.0.10:56431">>}
|
||||||
|
, {<<"sockname">>, <<"0.0.0.0:1883">>}
|
||||||
|
, {<<"disconnected_at">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"node">>, node()}
|
||||||
|
];
|
||||||
|
columns_with_exam('session.subscribed') ->
|
||||||
|
[ {<<"event">>, 'session.subscribed'}
|
||||||
|
, {<<"clientid">>, <<"c_emqx">>}
|
||||||
|
, {<<"username">>, <<"u_emqx">>}
|
||||||
|
, {<<"peerhost">>, <<"192.168.0.10">>}
|
||||||
|
, {<<"topic">>, <<"t/a">>}
|
||||||
|
, {<<"qos">>, 1}
|
||||||
|
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"node">>, node()}
|
||||||
|
];
|
||||||
|
columns_with_exam('session.unsubscribed') ->
|
||||||
|
[ {<<"event">>, 'session.unsubscribed'}
|
||||||
|
, {<<"clientid">>, <<"c_emqx">>}
|
||||||
|
, {<<"username">>, <<"u_emqx">>}
|
||||||
|
, {<<"peerhost">>, <<"192.168.0.10">>}
|
||||||
|
, {<<"topic">>, <<"t/a">>}
|
||||||
|
, {<<"qos">>, 1}
|
||||||
|
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"node">>, node()}
|
||||||
|
].
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Helper functions
|
%% Helper functions
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -354,7 +592,8 @@ event_topic('session.subscribed') -> <<"$events/session_subscribed">>;
|
||||||
event_topic('session.unsubscribed') -> <<"$events/session_unsubscribed">>;
|
event_topic('session.unsubscribed') -> <<"$events/session_unsubscribed">>;
|
||||||
event_topic('message.delivered') -> <<"$events/message_delivered">>;
|
event_topic('message.delivered') -> <<"$events/message_delivered">>;
|
||||||
event_topic('message.acked') -> <<"$events/message_acked">>;
|
event_topic('message.acked') -> <<"$events/message_acked">>;
|
||||||
event_topic('message.dropped') -> <<"$events/message_dropped">>.
|
event_topic('message.dropped') -> <<"$events/message_dropped">>;
|
||||||
|
event_topic('message.publish') -> <<"$events/message_publish">>.
|
||||||
|
|
||||||
printable_maps(undefined) -> #{};
|
printable_maps(undefined) -> #{};
|
||||||
printable_maps(Headers) ->
|
printable_maps(Headers) ->
|
||||||
|
|
|
@ -106,11 +106,11 @@
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% APIs
|
%% APIs
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec(create_rule_metrics(rule_id()) -> Ref :: counters:counters_ref()).
|
-spec(create_rule_metrics(rule_id()) -> ok).
|
||||||
create_rule_metrics(Id) ->
|
create_rule_metrics(Id) ->
|
||||||
gen_server:call(?MODULE, {create_rule_metrics, Id}).
|
gen_server:call(?MODULE, {create_rule_metrics, Id}).
|
||||||
|
|
||||||
-spec(create_metrics(rule_id()) -> Ref :: counters:counters_ref()).
|
-spec(create_metrics(rule_id()) -> ok).
|
||||||
create_metrics(Id) ->
|
create_metrics(Id) ->
|
||||||
gen_server:call(?MODULE, {create_metrics, Id}).
|
gen_server:call(?MODULE, {create_metrics, Id}).
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
-include("rule_engine.hrl").
|
-include("rule_engine.hrl").
|
||||||
-include("rule_events.hrl").
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
|
||||||
-export([start_link/0]).
|
-export([start_link/0]).
|
||||||
|
@ -166,14 +165,10 @@ start_link() ->
|
||||||
get_rules() ->
|
get_rules() ->
|
||||||
get_all_records(?RULE_TAB).
|
get_all_records(?RULE_TAB).
|
||||||
|
|
||||||
%% TODO: emqx_rule_utils:can_topic_match_oneof(Topic::any(), For::atom())
|
|
||||||
%% will never return since it differs in the 2nd argument from the success
|
|
||||||
%% typing arguments: (any(), [binary() | ['' | '#' | '+' | binary()]])
|
|
||||||
-dialyzer([{nowarn_function, get_rules_for/1}]).
|
|
||||||
-spec(get_rules_for(Topic :: binary()) -> list(emqx_rule_engine:rule())).
|
-spec(get_rules_for(Topic :: binary()) -> list(emqx_rule_engine:rule())).
|
||||||
get_rules_for(Topic) ->
|
get_rules_for(Topic) ->
|
||||||
[Rule || Rule = #rule{for = For} <- get_rules(),
|
[Rule || Rule = #rule{for = For} <- get_rules(),
|
||||||
Topic =:= For orelse emqx_rule_utils:can_topic_match_oneof(Topic, For)].
|
emqx_rule_utils:can_topic_match_oneof(Topic, For)].
|
||||||
|
|
||||||
-spec(get_rule(Id :: rule_id()) -> {ok, emqx_rule_engine:rule()} | not_found).
|
-spec(get_rule(Id :: rule_id()) -> {ok, emqx_rule_engine:rule()} | not_found).
|
||||||
get_rule(Id) ->
|
get_rule(Id) ->
|
||||||
|
@ -333,10 +328,14 @@ remove_resource_params(ResId) ->
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
delete_resource(ResId) ->
|
delete_resource(ResId) ->
|
||||||
%% TODO, change to foreache:s
|
lists:foreach(fun(#rule{id = Id, actions = Actions}) ->
|
||||||
_ = [[ResId =:= ResId1 andalso throw({dependency_exists, {rule, Id}})
|
lists:foreach(
|
||||||
|| #action_instance{args = #{<<"$resource">> := ResId1}} <- Actions]
|
fun (#action_instance{args = #{<<"$resource">> := ResId1}})
|
||||||
|| #rule{id = Id, actions = Actions} <- get_rules()],
|
when ResId =:= ResId1 ->
|
||||||
|
throw({dependency_exists, {rule, Id}});
|
||||||
|
(_) -> ok
|
||||||
|
end, Actions)
|
||||||
|
end, get_rules()),
|
||||||
mnesia:delete(?RES_TAB, ResId, write).
|
mnesia:delete(?RES_TAB, ResId, write).
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
|
|
|
@ -71,9 +71,6 @@ apply_rules([Rule = #rule{id = RuleID}|More], Input) ->
|
||||||
apply_rules(More, Input).
|
apply_rules(More, Input).
|
||||||
|
|
||||||
apply_rule_discard_result(Rule, Input) ->
|
apply_rule_discard_result(Rule, Input) ->
|
||||||
%% TODO check if below two clauses are ok to discard:
|
|
||||||
%% {'error','nomatch'}
|
|
||||||
%% {'ok',[any()]}
|
|
||||||
_ = apply_rule(Rule, Input),
|
_ = apply_rule(Rule, Input),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
-module(emqx_rule_sqlparser).
|
-module(emqx_rule_sqlparser).
|
||||||
|
|
||||||
-include("rule_engine.hrl").
|
-include("rule_engine.hrl").
|
||||||
-include("rule_events.hrl").
|
|
||||||
|
|
||||||
-export([parse_select/1]).
|
-export([parse_select/1]).
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
-module(emqx_rule_sqltester).
|
-module(emqx_rule_sqltester).
|
||||||
|
|
||||||
-include("rule_engine.hrl").
|
-include("rule_engine.hrl").
|
||||||
-include("rule_events.hrl").
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
|
||||||
-export([ test/1
|
-export([ test/1
|
||||||
|
@ -27,10 +26,11 @@
|
||||||
test_rule/4,
|
test_rule/4,
|
||||||
flatten/1,
|
flatten/1,
|
||||||
sql_test_action/0,
|
sql_test_action/0,
|
||||||
fill_default_values/2
|
fill_default_values/2,
|
||||||
|
envs_examp/1
|
||||||
]}).
|
]}).
|
||||||
|
|
||||||
-spec(test(#{}) -> {ok, Result::map()} | no_return()).
|
-spec(test(#{}) -> {ok, map() | list()} | {error, term()}).
|
||||||
test(#{<<"rawsql">> := Sql, <<"ctx">> := Context}) ->
|
test(#{<<"rawsql">> := Sql, <<"ctx">> := Context}) ->
|
||||||
{ok, Select} = emqx_rule_sqlparser:parse_select(Sql),
|
{ok, Select} = emqx_rule_sqlparser:parse_select(Sql),
|
||||||
InTopic = maps:get(<<"topic">>, Context, <<>>),
|
InTopic = maps:get(<<"topic">>, Context, <<>>),
|
||||||
|
@ -96,4 +96,23 @@ sql_test_action() ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
fill_default_values(Event, Context) ->
|
fill_default_values(Event, Context) ->
|
||||||
maps:merge(?EG_ENVS(Event), Context).
|
maps:merge(envs_examp(Event), Context).
|
||||||
|
|
||||||
|
envs_examp(<<"$events/", _/binary>> = EVENT_TOPIC) ->
|
||||||
|
EventName = emqx_rule_events:event_name(EVENT_TOPIC),
|
||||||
|
emqx_rule_maps:atom_key_map(
|
||||||
|
maps:from_list(
|
||||||
|
emqx_rule_events:columns_with_exam(EventName)));
|
||||||
|
envs_examp(_) ->
|
||||||
|
#{id => emqx_guid:to_hexstr(emqx_guid:gen()),
|
||||||
|
clientid => <<"c_emqx">>,
|
||||||
|
username => <<"u_emqx">>,
|
||||||
|
payload => <<"{\"id\": 1, \"name\": \"ha\"}">>,
|
||||||
|
peerhost => <<"127.0.0.1">>,
|
||||||
|
topic => <<"t/a">>,
|
||||||
|
qos => 1,
|
||||||
|
flags => #{sys => true, event => true},
|
||||||
|
publish_received_at => emqx_rule_utils:now_ms(),
|
||||||
|
timestamp => emqx_rule_utils:now_ms(),
|
||||||
|
node => node()
|
||||||
|
}.
|
||||||
|
|
|
@ -67,9 +67,7 @@
|
||||||
|
|
||||||
-type(tmpl_cmd() :: list(tmpl_token())).
|
-type(tmpl_cmd() :: list(tmpl_token())).
|
||||||
|
|
||||||
-type(prepare_statement() :: binary()).
|
-type(prepare_statement_key() :: binary()).
|
||||||
|
|
||||||
-type(prepare_params() :: fun((binary()) -> list())).
|
|
||||||
|
|
||||||
%% preprocess template string with place holders
|
%% preprocess template string with place holders
|
||||||
-spec(preproc_tmpl(binary()) -> tmpl_token()).
|
-spec(preproc_tmpl(binary()) -> tmpl_token()).
|
||||||
|
@ -94,6 +92,7 @@ put_head(Type, Term, List) ->
|
||||||
proc_tmpl(Tokens, Data) ->
|
proc_tmpl(Tokens, Data) ->
|
||||||
proc_tmpl(Tokens, Data, #{return => full_binary}).
|
proc_tmpl(Tokens, Data, #{return => full_binary}).
|
||||||
|
|
||||||
|
-spec(proc_tmpl(tmpl_token(), map(), map()) -> binary() | list()).
|
||||||
proc_tmpl(Tokens, Data, Opts = #{return := full_binary}) ->
|
proc_tmpl(Tokens, Data, Opts = #{return := full_binary}) ->
|
||||||
Trans = maps:get(var_trans, Opts, fun bin/1),
|
Trans = maps:get(var_trans, Opts, fun bin/1),
|
||||||
list_to_binary(
|
list_to_binary(
|
||||||
|
@ -115,19 +114,21 @@ preproc_cmd(Str) ->
|
||||||
SubStrList = re:split(Str, ?EX_WITHE_CHARS, [{return,binary},trim]),
|
SubStrList = re:split(Str, ?EX_WITHE_CHARS, [{return,binary},trim]),
|
||||||
[preproc_tmpl(SubStr) || SubStr <- SubStrList].
|
[preproc_tmpl(SubStr) || SubStr <- SubStrList].
|
||||||
|
|
||||||
|
-spec(proc_cmd([tmpl_token()], map()) -> binary() | list()).
|
||||||
proc_cmd(Tokens, Data) ->
|
proc_cmd(Tokens, Data) ->
|
||||||
proc_cmd(Tokens, Data, #{return => full_binary}).
|
proc_cmd(Tokens, Data, #{return => full_binary}).
|
||||||
|
-spec(proc_cmd([tmpl_token()], map(), map()) -> list()).
|
||||||
proc_cmd(Tokens, Data, Opts) ->
|
proc_cmd(Tokens, Data, Opts) ->
|
||||||
[proc_tmpl(Tks, Data, Opts) || Tks <- Tokens].
|
[proc_tmpl(Tks, Data, Opts) || Tks <- Tokens].
|
||||||
|
|
||||||
%% preprocess SQL with place holders
|
%% preprocess SQL with place holders
|
||||||
-spec(preproc_sql(Sql::binary()) -> {prepare_statement(), prepare_params()}).
|
-spec(preproc_sql(Sql::binary()) -> {prepare_statement_key(), tmpl_token()}).
|
||||||
-dialyzer({nowarn_function,preproc_sql/1}).
|
|
||||||
preproc_sql(Sql) ->
|
preproc_sql(Sql) ->
|
||||||
preproc_sql(Sql, '?').
|
preproc_sql(Sql, '?').
|
||||||
|
|
||||||
-spec(preproc_sql(Sql::binary(), ReplaceWith :: '?' | '$n') -> {prepare_statement(), prepare_params()}).
|
-spec(preproc_sql(Sql::binary(), ReplaceWith :: '?' | '$n')
|
||||||
-dialyzer({nowarn_function,preproc_sql/2}).
|
-> {prepare_statement_key(), tmpl_token()}).
|
||||||
|
|
||||||
preproc_sql(Sql, ReplaceWith) ->
|
preproc_sql(Sql, ReplaceWith) ->
|
||||||
case re:run(Sql, ?EX_PLACE_HOLDER, [{capture, all_but_first, binary}, global]) of
|
case re:run(Sql, ?EX_PLACE_HOLDER, [{capture, all_but_first, binary}, global]) of
|
||||||
{match, PlaceHolders} ->
|
{match, PlaceHolders} ->
|
||||||
|
@ -330,5 +331,6 @@ now_ms() ->
|
||||||
erlang:system_time(millisecond).
|
erlang:system_time(millisecond).
|
||||||
|
|
||||||
can_topic_match_oneof(Topic, Filters) ->
|
can_topic_match_oneof(Topic, Filters) ->
|
||||||
MatchedFilters = [Fltr || Fltr <- Filters, emqx_topic:match(Topic, Fltr)],
|
lists:any(fun(Fltr) ->
|
||||||
length(MatchedFilters) > 0.
|
emqx_topic:match(Topic, Fltr)
|
||||||
|
end, Filters).
|
||||||
|
|
|
@ -2378,7 +2378,7 @@ start_apps() ->
|
||||||
[start_apps(App, SchemaFile, ConfigFile) ||
|
[start_apps(App, SchemaFile, ConfigFile) ||
|
||||||
{App, SchemaFile, ConfigFile}
|
{App, SchemaFile, ConfigFile}
|
||||||
<- [{emqx, deps_path(emqx, "priv/emqx.schema"),
|
<- [{emqx, deps_path(emqx, "priv/emqx.schema"),
|
||||||
deps_path(emqx, "etc/gen.emqx.conf")},
|
deps_path(emqx, "etc/emqx.conf")},
|
||||||
{emqx_rule_engine, local_path("priv/emqx_rule_engine.schema"),
|
{emqx_rule_engine, local_path("priv/emqx_rule_engine.schema"),
|
||||||
local_path("etc/emqx_rule_engine.conf")}]].
|
local_path("etc/emqx_rule_engine.conf")}]].
|
||||||
|
|
||||||
|
@ -2424,6 +2424,7 @@ set_special_configs(_App) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
print_mock() ->
|
print_mock() ->
|
||||||
|
catch meck:unload(emqx_ctl),
|
||||||
meck:new(emqx_ctl, [non_strict, passthrough]),
|
meck:new(emqx_ctl, [non_strict, passthrough]),
|
||||||
meck:expect(emqx_ctl, print, fun(Arg) -> emqx_ctl:format(Arg) end),
|
meck:expect(emqx_ctl, print, fun(Arg) -> emqx_ctl:format(Arg) end),
|
||||||
meck:expect(emqx_ctl, print, fun(Msg, Arg) -> emqx_ctl:format(Msg, Arg) end),
|
meck:expect(emqx_ctl, print, fun(Msg, Arg) -> emqx_ctl:format(Msg, Arg) end),
|
||||||
|
|
|
@ -121,12 +121,8 @@
|
||||||
zh => <<"将数据转发给 Web 服务"/utf8>>}
|
zh => <<"将数据转发给 Web 服务"/utf8>>}
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-type(action_fun() :: fun((Data :: map(), Envs :: map()) -> Result :: any())).
|
|
||||||
|
|
||||||
-type(url() :: binary()).
|
-type(url() :: binary()).
|
||||||
|
|
||||||
-export_type([action_fun/0]).
|
|
||||||
|
|
||||||
-export([ on_resource_create/2
|
-export([ on_resource_create/2
|
||||||
, on_get_resource_status/2
|
, on_get_resource_status/2
|
||||||
, on_resource_destroy/2
|
, on_resource_destroy/2
|
||||||
|
@ -166,7 +162,7 @@ on_resource_destroy(_ResId, _Params) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%% An action that forwards publish messages to a remote web server.
|
%% An action that forwards publish messages to a remote web server.
|
||||||
-spec(on_action_create_data_to_webserver(Id::binary(), #{url() := string()}) -> action_fun()).
|
-spec(on_action_create_data_to_webserver(Id::binary(), #{url() := string()}) -> {bindings(), NewParams :: map()}).
|
||||||
on_action_create_data_to_webserver(Id, Params) ->
|
on_action_create_data_to_webserver(Id, Params) ->
|
||||||
#{url := Url, headers := Headers, method := Method, content_type := ContentType, payload_tmpl := PayloadTmpl, path := Path}
|
#{url := Url, headers := Headers, method := Method, content_type := ContentType, payload_tmpl := PayloadTmpl, path := Path}
|
||||||
= parse_action_params(Params),
|
= parse_action_params(Params),
|
||||||
|
|
Loading…
Reference in New Issue