chore(hooks): validate hookpoints and document hook callbacks
Co-authored-by: Thales Macedo Garitezi <thalesmg@gmail.com>
This commit is contained in:
parent
17206f8c75
commit
14983ec14a
|
@ -53,12 +53,6 @@
|
|||
subscribed/2
|
||||
]).
|
||||
|
||||
%% Hooks API
|
||||
-export([
|
||||
run_hook/2,
|
||||
run_fold_hook/3
|
||||
]).
|
||||
|
||||
%% Configs APIs
|
||||
-export([
|
||||
get_config/1,
|
||||
|
@ -172,15 +166,8 @@ subscribed(SubId, Topic) when is_atom(SubId); is_binary(SubId) ->
|
|||
emqx_broker:subscribed(SubId, iolist_to_binary(Topic)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Hooks API
|
||||
%% Config API
|
||||
%%--------------------------------------------------------------------
|
||||
-spec run_hook(emqx_hooks:hookpoint(), list(any())) -> ok | stop.
|
||||
run_hook(HookPoint, Args) ->
|
||||
emqx_hooks:run(HookPoint, Args).
|
||||
|
||||
-spec run_fold_hook(emqx_hooks:hookpoint(), list(any()), any()) -> any().
|
||||
run_fold_hook(HookPoint, Args, Acc) ->
|
||||
emqx_hooks:run_fold(HookPoint, Args, Acc).
|
||||
|
||||
-spec get_config(emqx_utils_maps:config_key_path()) -> term().
|
||||
get_config(KeyPath) ->
|
||||
|
|
|
@ -138,7 +138,7 @@ check_authorization_cache(ClientInfo, Action, Topic) ->
|
|||
emqx_authz_cache:put_authz_cache(Action, Topic, AuthzResult),
|
||||
AuthzResult;
|
||||
AuthzResult ->
|
||||
emqx:run_hook(
|
||||
emqx_hooks:run(
|
||||
'client.check_authz_complete',
|
||||
[ClientInfo, Action, Topic, AuthzResult, cache]
|
||||
),
|
||||
|
@ -152,7 +152,7 @@ do_authorize(ClientInfo, Action, Topic) ->
|
|||
case run_hooks('client.authorize', [ClientInfo, Action, Topic], Default) of
|
||||
AuthzResult = #{result := Result} when Result == allow; Result == deny ->
|
||||
From = maps:get(from, AuthzResult, unknown),
|
||||
emqx:run_hook(
|
||||
emqx_hooks:run(
|
||||
'client.check_authz_complete',
|
||||
[ClientInfo, Action, Topic, Result, From]
|
||||
),
|
||||
|
@ -163,7 +163,7 @@ do_authorize(ClientInfo, Action, Topic) ->
|
|||
expected_example => "#{result => allow, from => default}",
|
||||
got => Other
|
||||
}),
|
||||
emqx:run_hook(
|
||||
emqx_hooks:run(
|
||||
'client.check_authz_complete',
|
||||
[ClientInfo, Action, Topic, deny, unknown_return_format]
|
||||
),
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
]
|
||||
).
|
||||
|
||||
-export_type([channel/0, opts/0, conn_state/0]).
|
||||
-export_type([channel/0, opts/0, conn_state/0, reply/0, replies/0]).
|
||||
|
||||
-record(channel, {
|
||||
%% MQTT ConnInfo
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2017-2023 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_hookpoints).
|
||||
|
||||
-type callback_result() :: stop | any().
|
||||
-type fold_callback_result(Acc) :: {stop, Acc} | {ok, Acc} | stop | any().
|
||||
|
||||
-export_type([
|
||||
fold_callback_result/1,
|
||||
callback_result/0
|
||||
]).
|
||||
|
||||
-export([
|
||||
default_hookpoints/0,
|
||||
register_hookpoints/0,
|
||||
register_hookpoints/1,
|
||||
verify_hookpoint/1
|
||||
]).
|
||||
|
||||
%%-----------------------------------------------------------------------------
|
||||
%% Hookpoints
|
||||
%%-----------------------------------------------------------------------------
|
||||
|
||||
-define(HOOKPOINTS, [
|
||||
'client.connect',
|
||||
'client.connack',
|
||||
'client.connected',
|
||||
'client.disconnected',
|
||||
'client.authorize',
|
||||
'client.check_authz_complete',
|
||||
'client.authenticate',
|
||||
'client.subscribe',
|
||||
'client.unsubscribe',
|
||||
'client.timeout',
|
||||
'client.monitored_process_down',
|
||||
'session.created',
|
||||
'session.subscribed',
|
||||
'session.unsubscribed',
|
||||
'session.resumed',
|
||||
'session.discarded',
|
||||
'session.takenover',
|
||||
'session.terminated',
|
||||
'message.publish',
|
||||
'message.puback',
|
||||
'message.dropped',
|
||||
'message.delivered',
|
||||
'message.acked',
|
||||
'delivery.dropped',
|
||||
'delivery.completed',
|
||||
'cm.channel.unregistered',
|
||||
'tls_handshake.psk_lookup',
|
||||
|
||||
%% This is a deprecated hookpoint renamed to 'client.authorize'
|
||||
%% However, our template plugin used this hookpoint before its 5.1.0 version,
|
||||
%% so we keep it here
|
||||
'client.check_acl'
|
||||
]).
|
||||
|
||||
%%-----------------------------------------------------------------------------
|
||||
%% Callbacks
|
||||
%%-----------------------------------------------------------------------------
|
||||
|
||||
%% Callback definitions are given for documentation purposes.
|
||||
%% Each hook callback implementation can also accept any number of custom arguments
|
||||
%% after the mandatory ones.
|
||||
%%
|
||||
%% By default, callbacks are executed in the channel process context.
|
||||
|
||||
-callback 'client.connect'(emqx_types:conninfo(), Props) ->
|
||||
fold_callback_result(Props)
|
||||
when
|
||||
Props :: emqx_types:properties().
|
||||
|
||||
-callback 'client.connack'(emqx_types:conninfo(), _Reason :: atom(), Props) ->
|
||||
fold_callback_result(Props)
|
||||
when
|
||||
Props :: emqx_types:properties().
|
||||
|
||||
-callback 'client.connected'(emqx_types:clientinfo(), emqx_types:conninfo()) -> callback_result().
|
||||
|
||||
-callback 'client.disconnected'(emqx_types:clientinfo(), _Reason :: atom(), emqx_types:conninfo()) ->
|
||||
callback_result().
|
||||
|
||||
-callback 'client.authorize'(
|
||||
emqx_types:clientinfo(), emqx_types:pubsub(), emqx_types:topic(), allow | deny
|
||||
) ->
|
||||
fold_callback_result(#{result := allow | deny, from => term()}).
|
||||
|
||||
-callback 'client.check_authz_complete'(
|
||||
emqx_types:clientinfo(), emqx_types:pubsub(), emqx_types:topic(), allow | deny, _From :: term()
|
||||
) ->
|
||||
callback_result().
|
||||
|
||||
-callback 'client.authenticate'(emqx_types:clientinfo(), ignore) ->
|
||||
fold_callback_result(
|
||||
ignore
|
||||
| ok
|
||||
| {ok, map()}
|
||||
| {ok, map(), binary()}
|
||||
| {continue, map()}
|
||||
| {continue, binary(), map()}
|
||||
| {error, term()}
|
||||
).
|
||||
|
||||
-callback 'client.subscribe'(emqx_types:clientinfo(), emqx_types:properties(), TopicFilters) ->
|
||||
fold_callback_result(TopicFilters)
|
||||
when
|
||||
TopicFilters :: list({emqx_topic:topic(), map()}).
|
||||
|
||||
-callback 'client.unsubscribe'(emqx_types:clientinfo(), emqx_types:properties(), TopicFilters) ->
|
||||
fold_callback_result(TopicFilters)
|
||||
when
|
||||
TopicFilters :: list({emqx_topic:topic(), map()}).
|
||||
|
||||
-callback 'client.timeout'(_TimerReference :: reference(), _Msg :: term(), Replies) ->
|
||||
fold_callback_result(Replies)
|
||||
when
|
||||
Replies :: emqx_channel:replies().
|
||||
|
||||
-callback 'client.monitored_process_down'(
|
||||
_MonitorRef :: reference(), _Pid :: pid(), _Reason :: term(), Replies
|
||||
) ->
|
||||
fold_callback_result(Replies)
|
||||
when
|
||||
Replies :: emqx_channel:replies().
|
||||
|
||||
-callback 'session.created'(emqx_types:clientinfo(), _SessionInfo :: emqx_types:infos()) ->
|
||||
callback_result().
|
||||
|
||||
-callback 'session.subscribed'(emqx_types:clientinfo(), emqx_types:topic(), emqx_types:subopts()) ->
|
||||
callback_result().
|
||||
|
||||
-callback 'session.unsubscribed'(emqx_types:clientinfo(), emqx_types:topic(), emqx_types:subopts()) ->
|
||||
callback_result().
|
||||
|
||||
-callback 'session.resumed'(emqx_types:clientinfo(), _SessionInfo :: emqx_types:infos()) ->
|
||||
callback_result().
|
||||
|
||||
-callback 'session.discarded'(emqx_types:clientinfo(), _SessionInfo :: emqx_types:infos()) ->
|
||||
callback_result().
|
||||
|
||||
-callback 'session.takenover'(emqx_types:clientinfo(), _SessionInfo :: emqx_types:infos()) ->
|
||||
callback_result().
|
||||
|
||||
-callback 'session.terminated'(
|
||||
emqx_types:clientinfo(), _Reason :: atom(), _SessionInfo :: emqx_types:infos()
|
||||
) -> callback_result().
|
||||
|
||||
-callback 'message.publish'(Msg) ->
|
||||
fold_callback_result(Msg)
|
||||
when
|
||||
Msg :: emqx_types:message().
|
||||
|
||||
-callback 'message.puback'(
|
||||
emqx_types:packet_id(),
|
||||
emqx_types:message(),
|
||||
emqx_types:publish_result(),
|
||||
emqx_types:reason_code()
|
||||
) ->
|
||||
fold_callback_result(undefined | emqx_types:reason_code()).
|
||||
|
||||
-callback 'message.dropped'(emqx_types:message(), #{node => node()}, _Reason :: atom()) ->
|
||||
callback_result().
|
||||
|
||||
-callback 'message.delivered'(emqx_types:clientinfo(), Msg) -> fold_callback_result(Msg) when
|
||||
Msg :: emqx_types:message().
|
||||
|
||||
-callback 'message.acked'(emqx_types:clientinfo(), emqx_types:message()) -> callback_result().
|
||||
|
||||
-callback 'delivery.dropped'(emqx_types:clientinfo(), emqx_types:message(), _Reason :: atom()) ->
|
||||
callback_result().
|
||||
|
||||
-callback 'delivery.completed'(emqx_types:message(), #{
|
||||
session_birth_time := emqx_utils_calendar:epoch_millisecond(), clientid := emqx_types:clientid()
|
||||
}) ->
|
||||
callback_result().
|
||||
|
||||
%% NOTE
|
||||
%% Executed out of channel process context
|
||||
-callback 'cm.channel.unregistered'(_ChanPid :: pid()) -> callback_result().
|
||||
|
||||
%% NOTE
|
||||
%% Executed out of channel process context
|
||||
-callback 'tls_handshake.psk_lookup'(emqx_tls_psk:psk_identity(), normal) ->
|
||||
fold_callback_result(
|
||||
{ok, _SharedSecret :: binary()}
|
||||
| {error, term()}
|
||||
| normal
|
||||
).
|
||||
|
||||
%%-----------------------------------------------------------------------------
|
||||
%% API
|
||||
%%-----------------------------------------------------------------------------
|
||||
|
||||
default_hookpoints() ->
|
||||
?HOOKPOINTS.
|
||||
|
||||
register_hookpoints() ->
|
||||
register_hookpoints(default_hookpoints()).
|
||||
|
||||
register_hookpoints(HookPoints) ->
|
||||
persistent_term:put(?MODULE, maps:from_keys(HookPoints, true)).
|
||||
|
||||
verify_hookpoint(HookPoint) when is_binary(HookPoint) -> ok;
|
||||
verify_hookpoint(HookPoint) ->
|
||||
case maps:is_key(HookPoint, registered_hookpoints()) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
error({invalid_hookpoint, HookPoint})
|
||||
end.
|
||||
|
||||
%%-----------------------------------------------------------------------------
|
||||
%% Internal API
|
||||
%%-----------------------------------------------------------------------------
|
||||
|
||||
registered_hookpoints() ->
|
||||
persistent_term:get(?MODULE, #{}).
|
|
@ -131,6 +131,7 @@ add(HookPoint, Action, Priority, Filter) when is_integer(Priority) ->
|
|||
do_add(HookPoint, #callback{action = Action, filter = Filter, priority = Priority}).
|
||||
|
||||
do_add(HookPoint, Callback) ->
|
||||
ok = emqx_hookpoints:verify_hookpoint(HookPoint),
|
||||
gen_server:call(?SERVER, {add, HookPoint, Callback}, infinity).
|
||||
|
||||
%% @doc `put/3,4` updates the existing hook, add it if not exists.
|
||||
|
@ -143,6 +144,7 @@ put(HookPoint, Action, Priority, Filter) when is_integer(Priority) ->
|
|||
do_put(HookPoint, #callback{action = Action, filter = Filter, priority = Priority}).
|
||||
|
||||
do_put(HookPoint, Callback) ->
|
||||
ok = emqx_hookpoints:verify_hookpoint(HookPoint),
|
||||
case do_add(HookPoint, Callback) of
|
||||
ok -> ok;
|
||||
{error, already_exists} -> gen_server:call(?SERVER, {put, HookPoint, Callback}, infinity)
|
||||
|
@ -156,11 +158,13 @@ del(HookPoint, Action) ->
|
|||
%% @doc Run hooks.
|
||||
-spec run(hookpoint(), list(Arg :: term())) -> ok.
|
||||
run(HookPoint, Args) ->
|
||||
ok = emqx_hookpoints:verify_hookpoint(HookPoint),
|
||||
do_run(lookup(HookPoint), Args).
|
||||
|
||||
%% @doc Run hooks with Accumulator.
|
||||
-spec run_fold(hookpoint(), list(Arg :: term()), Acc :: term()) -> Acc :: term().
|
||||
run_fold(HookPoint, Args, Acc) ->
|
||||
ok = emqx_hookpoints:verify_hookpoint(HookPoint),
|
||||
do_run_fold(lookup(HookPoint), Args, Acc).
|
||||
|
||||
do_run([#callback{action = Action, filter = Filter} | Callbacks], Args) ->
|
||||
|
@ -230,6 +234,7 @@ lookup(HookPoint) ->
|
|||
|
||||
init([]) ->
|
||||
ok = emqx_utils_ets:new(?TAB, [{keypos, #hook.name}, {read_concurrency, true}]),
|
||||
ok = emqx_hookpoints:register_hookpoints(),
|
||||
{ok, #{}}.
|
||||
|
||||
handle_call({add, HookPoint, Callback = #callback{action = {M, F, _}}}, _From, State) ->
|
||||
|
|
|
@ -496,7 +496,7 @@ stats(Session) ->
|
|||
%%--------------------------------------------------------------------
|
||||
|
||||
on_delivery_completed(Msg, #{clientid := ClientId}, Session) ->
|
||||
emqx:run_hook(
|
||||
emqx_hooks:run(
|
||||
'delivery.completed',
|
||||
[
|
||||
Msg,
|
||||
|
|
|
@ -95,59 +95,6 @@ t_emqx_pubsub_api(_) ->
|
|||
ct:sleep(20),
|
||||
?assertEqual([], emqx:topics()).
|
||||
|
||||
t_hook_unhook(_) ->
|
||||
ok = emqx_hooks:add(test_hook, {?MODULE, hook_fun1, []}, 0),
|
||||
ok = emqx_hooks:add(test_hook, {?MODULE, hook_fun2, []}, 0),
|
||||
?assertEqual(
|
||||
{error, already_exists},
|
||||
emqx_hooks:add(test_hook, {?MODULE, hook_fun2, []}, 0)
|
||||
),
|
||||
ok = emqx_hooks:del(test_hook, {?MODULE, hook_fun1}),
|
||||
ok = emqx_hooks:del(test_hook, {?MODULE, hook_fun2}),
|
||||
|
||||
ok = emqx_hooks:add(emqx_hook, {?MODULE, hook_fun8, []}, 8),
|
||||
ok = emqx_hooks:add(emqx_hook, {?MODULE, hook_fun2, []}, 2),
|
||||
ok = emqx_hooks:add(emqx_hook, {?MODULE, hook_fun10, []}, 10),
|
||||
ok = emqx_hooks:add(emqx_hook, {?MODULE, hook_fun9, []}, 9),
|
||||
ok = emqx_hooks:del(emqx_hook, {?MODULE, hook_fun2, []}),
|
||||
ok = emqx_hooks:del(emqx_hook, {?MODULE, hook_fun8, []}),
|
||||
ok = emqx_hooks:del(emqx_hook, {?MODULE, hook_fun9, []}),
|
||||
ok = emqx_hooks:del(emqx_hook, {?MODULE, hook_fun10, []}).
|
||||
|
||||
t_run_hook(_) ->
|
||||
ok = emqx_hooks:add(foldl_hook, {?MODULE, hook_fun3, [init]}, 0),
|
||||
ok = emqx_hooks:add(foldl_hook, {?MODULE, hook_fun4, [init]}, 0),
|
||||
ok = emqx_hooks:add(foldl_hook, {?MODULE, hook_fun5, [init]}, 0),
|
||||
[r5, r4] = emqx:run_fold_hook(foldl_hook, [arg1, arg2], []),
|
||||
[] = emqx:run_fold_hook(unknown_hook, [], []),
|
||||
|
||||
ok = emqx_hooks:add(foldl_hook2, {?MODULE, hook_fun9, []}, 0),
|
||||
ok = emqx_hooks:add(foldl_hook2, {?MODULE, hook_fun10, []}, 0),
|
||||
[r10] = emqx:run_fold_hook(foldl_hook2, [arg], []),
|
||||
|
||||
ok = emqx_hooks:add(foreach_hook, {?MODULE, hook_fun6, [initArg]}, 0),
|
||||
{error, already_exists} = emqx_hooks:add(foreach_hook, {?MODULE, hook_fun6, [initArg]}, 0),
|
||||
ok = emqx_hooks:add(foreach_hook, {?MODULE, hook_fun7, [initArg]}, 0),
|
||||
ok = emqx_hooks:add(foreach_hook, {?MODULE, hook_fun8, [initArg]}, 0),
|
||||
ok = emqx:run_hook(foreach_hook, [arg]),
|
||||
|
||||
ok = emqx_hooks:add(
|
||||
foreach_filter1_hook, {?MODULE, hook_fun1, []}, 0, {?MODULE, hook_filter1, []}
|
||||
),
|
||||
%% filter passed
|
||||
?assertEqual(ok, emqx:run_hook(foreach_filter1_hook, [arg])),
|
||||
%% filter failed
|
||||
?assertEqual(ok, emqx:run_hook(foreach_filter1_hook, [arg1])),
|
||||
|
||||
ok = emqx_hooks:add(
|
||||
foldl_filter2_hook, {?MODULE, hook_fun2, []}, 0, {?MODULE, hook_filter2, [init_arg]}
|
||||
),
|
||||
ok = emqx_hooks:add(
|
||||
foldl_filter2_hook, {?MODULE, hook_fun2_1, []}, 0, {?MODULE, hook_filter2_1, [init_arg]}
|
||||
),
|
||||
?assertEqual(3, emqx:run_fold_hook(foldl_filter2_hook, [arg], 1)),
|
||||
?assertEqual(2, emqx:run_fold_hook(foldl_filter2_hook, [arg1], 1)).
|
||||
|
||||
t_cluster_nodes(_) ->
|
||||
Expected = [node()],
|
||||
?assertEqual(Expected, emqx:running_nodes()),
|
||||
|
@ -169,36 +116,3 @@ t_get_config_default_2(_) ->
|
|||
NonAtomPathRes = emqx:get_config(["doesnotexist", <<"db_backend">>], undefined),
|
||||
?assertEqual(undefined, NonAtomPathRes),
|
||||
?assertEqual(undefined, AtomPathRes).
|
||||
%%--------------------------------------------------------------------
|
||||
%% Hook fun
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
hook_fun1(arg) -> ok;
|
||||
hook_fun1(_) -> error.
|
||||
|
||||
hook_fun2(arg) -> ok;
|
||||
hook_fun2(_) -> error.
|
||||
|
||||
hook_fun2(_, Acc) -> {ok, Acc + 1}.
|
||||
hook_fun2_1(_, Acc) -> {ok, Acc + 1}.
|
||||
|
||||
hook_fun3(arg1, arg2, _Acc, init) -> ok.
|
||||
hook_fun4(arg1, arg2, Acc, init) -> {ok, [r4 | Acc]}.
|
||||
hook_fun5(arg1, arg2, Acc, init) -> {ok, [r5 | Acc]}.
|
||||
|
||||
hook_fun6(arg, initArg) -> ok.
|
||||
hook_fun7(arg, initArg) -> ok.
|
||||
hook_fun8(arg, initArg) -> ok.
|
||||
|
||||
hook_fun9(arg, Acc) -> {stop, [r9 | Acc]}.
|
||||
hook_fun10(arg, Acc) -> {stop, [r10 | Acc]}.
|
||||
|
||||
hook_filter1(arg) -> true;
|
||||
hook_filter1(_) -> false.
|
||||
|
||||
hook_filter2(arg, _Acc, init_arg) -> true;
|
||||
hook_filter2(_, _Acc, _IntArg) -> false.
|
||||
|
||||
hook_filter2_1(arg, _Acc, init_arg) -> true;
|
||||
hook_filter2_1(arg1, _Acc, init_arg) -> true;
|
||||
hook_filter2_1(_, _Acc, _IntArg) -> false.
|
||||
|
|
|
@ -38,7 +38,7 @@ t_trans(_) ->
|
|||
ok = emqx_cm_locker:trans(undefined, fun(_) -> ok end, []),
|
||||
ok = emqx_cm_locker:trans(<<"clientid">>, fun(_) -> ok end).
|
||||
|
||||
t_lock_unlocak(_) ->
|
||||
t_lock_unlock(_) ->
|
||||
{true, _} = emqx_cm_locker:lock(<<"clientid">>),
|
||||
{true, _} = emqx_cm_locker:lock(<<"clientid">>),
|
||||
{true, _} = emqx_cm_locker:unlock(<<"clientid">>),
|
||||
|
|
|
@ -25,26 +25,24 @@
|
|||
all() -> emqx_common_test_helpers:all(?MODULE).
|
||||
|
||||
init_per_testcase(_, Config) ->
|
||||
{ok, _} = emqx_hooks:start_link(),
|
||||
ok = emqx_hookpoints:register_hookpoints(
|
||||
[
|
||||
test_hook,
|
||||
emqx_hook,
|
||||
foldl_hook,
|
||||
foldl_hook2,
|
||||
foreach_hook,
|
||||
foreach_filter1_hook,
|
||||
foldl_filter2_hook
|
||||
]
|
||||
),
|
||||
Config.
|
||||
|
||||
end_per_testcase(_) ->
|
||||
ok = emqx_hookpoints:register_hookpoints(),
|
||||
catch emqx_hooks:stop().
|
||||
|
||||
% t_lookup(_) ->
|
||||
% error('TODO').
|
||||
|
||||
% t_run_fold(_) ->
|
||||
% error('TODO').
|
||||
|
||||
% t_run(_) ->
|
||||
% error('TODO').
|
||||
|
||||
% t_del(_) ->
|
||||
% error('TODO').
|
||||
|
||||
% t_add(_) ->
|
||||
% error('TODO').
|
||||
|
||||
t_add_hook_order(_) ->
|
||||
?assert(
|
||||
proper:quickcheck(
|
||||
|
@ -65,7 +63,8 @@ add_hook_order_prop() ->
|
|||
Hooks,
|
||||
hooks(),
|
||||
try
|
||||
{ok, _} = emqx_hooks:start_link(),
|
||||
_ = emqx_hooks:start_link(),
|
||||
ok = emqx_hookpoints:register_hookpoints([prop_hook]),
|
||||
[ok = emqx_hooks:add(prop_hook, {M, F, []}, Prio) || {Prio, M, F} <- Hooks],
|
||||
Callbacks = emqx_hooks:lookup(prop_hook),
|
||||
Order = [{Prio, M, F} || {callback, {M, F, _}, _Filter, Prio} <- Callbacks],
|
||||
|
@ -92,7 +91,6 @@ hooks() ->
|
|||
).
|
||||
|
||||
t_add_put_del_hook(_) ->
|
||||
{ok, _} = emqx_hooks:start_link(),
|
||||
ok = emqx_hooks:add(test_hook, {?MODULE, hook_fun1, []}, 0),
|
||||
ok = emqx_hooks:add(test_hook, {?MODULE, hook_fun2, []}, 0),
|
||||
?assertEqual(
|
||||
|
@ -150,31 +148,40 @@ t_add_put_del_hook(_) ->
|
|||
?assertEqual([], emqx_hooks:lookup(emqx_hook)).
|
||||
|
||||
t_run_hooks(_) ->
|
||||
{ok, _} = emqx_hooks:start_link(),
|
||||
ok = emqx_hooks:add(foldl_hook, {?MODULE, hook_fun3, [init]}, 0),
|
||||
ok = emqx_hooks:add(foldl_hook, {?MODULE, hook_fun4, [init]}, 0),
|
||||
ok = emqx_hooks:add(foldl_hook, {?MODULE, hook_fun5, [init]}, 0),
|
||||
[r5, r4] = emqx:run_fold_hook(foldl_hook, [arg1, arg2], []),
|
||||
[] = emqx:run_fold_hook(unknown_hook, [], []),
|
||||
[r5, r4] = emqx_hooks:run_fold(foldl_hook, [arg1, arg2], []),
|
||||
|
||||
?assertException(
|
||||
error,
|
||||
{invalid_hookpoint, unknown_hook},
|
||||
emqx_hooks:run_fold(unknown_hook, [], [])
|
||||
),
|
||||
?assertException(
|
||||
error,
|
||||
{invalid_hookpoint, unknown_hook},
|
||||
emqx_hooks:add(unknown_hook, {?MODULE, hook_fun5, [init]}, 0)
|
||||
),
|
||||
|
||||
ok = emqx_hooks:add(foldl_hook2, {?MODULE, hook_fun9, []}, 0),
|
||||
ok = emqx_hooks:add(foldl_hook2, {?MODULE, hook_fun10, []}, 0),
|
||||
%% Note: 10 is _less_ than 9 per lexicographic order
|
||||
[r10] = emqx:run_fold_hook(foldl_hook2, [arg], []),
|
||||
[r10] = emqx_hooks:run_fold(foldl_hook2, [arg], []),
|
||||
|
||||
ok = emqx_hooks:add(foreach_hook, {?MODULE, hook_fun6, [initArg]}, 0),
|
||||
{error, already_exists} = emqx_hooks:add(foreach_hook, {?MODULE, hook_fun6, [initArg]}, 0),
|
||||
ok = emqx_hooks:add(foreach_hook, {?MODULE, hook_fun7, [initArg]}, 0),
|
||||
ok = emqx_hooks:add(foreach_hook, {?MODULE, hook_fun8, [initArg]}, 0),
|
||||
ok = emqx:run_hook(foreach_hook, [arg]),
|
||||
ok = emqx_hooks:run(foreach_hook, [arg]),
|
||||
|
||||
ok = emqx_hooks:add(
|
||||
foreach_filter1_hook, {?MODULE, hook_fun1, []}, 0, {?MODULE, hook_filter1, []}
|
||||
),
|
||||
%% filter passed
|
||||
?assertEqual(ok, emqx:run_hook(foreach_filter1_hook, [arg])),
|
||||
?assertEqual(ok, emqx_hooks:run(foreach_filter1_hook, [arg])),
|
||||
%% filter failed
|
||||
?assertEqual(ok, emqx:run_hook(foreach_filter1_hook, [arg1])),
|
||||
?assertEqual(ok, emqx_hooks:run(foreach_filter1_hook, [arg1])),
|
||||
|
||||
ok = emqx_hooks:add(
|
||||
foldl_filter2_hook, {?MODULE, hook_fun2, []}, 0, {?MODULE, hook_filter2, [init_arg]}
|
||||
|
@ -182,11 +189,10 @@ t_run_hooks(_) ->
|
|||
ok = emqx_hooks:add(
|
||||
foldl_filter2_hook, {?MODULE, hook_fun2_1, []}, 0, {?MODULE, hook_filter2_1, [init_arg]}
|
||||
),
|
||||
?assertEqual(3, emqx:run_fold_hook(foldl_filter2_hook, [arg], 1)),
|
||||
?assertEqual(2, emqx:run_fold_hook(foldl_filter2_hook, [arg1], 1)).
|
||||
?assertEqual(3, emqx_hooks:run_fold(foldl_filter2_hook, [arg], 1)),
|
||||
?assertEqual(2, emqx_hooks:run_fold(foldl_filter2_hook, [arg1], 1)).
|
||||
|
||||
t_uncovered_func(_) ->
|
||||
{ok, _} = emqx_hooks:start_link(),
|
||||
Pid = erlang:whereis(emqx_hooks),
|
||||
gen_server:call(Pid, test),
|
||||
gen_server:cast(Pid, test),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{application, emqx_bridge_gcp_pubsub, [
|
||||
{description, "EMQX Enterprise GCP Pub/Sub Bridge"},
|
||||
{vsn, "0.1.8"},
|
||||
{vsn, "0.1.9"},
|
||||
{registered, []},
|
||||
{applications, [
|
||||
kernel,
|
||||
|
|
|
@ -749,7 +749,7 @@ handle_message(State, #{<<"ackId">> := AckId, <<"message">> := InnerMsg} = _Mess
|
|||
Payload = render(FullMessage, PayloadTemplate),
|
||||
MQTTMessage = emqx_message:make(InstanceId, MQTTQoS, MQTTTopic, Payload),
|
||||
_ = emqx:publish(MQTTMessage),
|
||||
emqx:run_hook(Hookpoint, [FullMessage]),
|
||||
emqx_hooks:run(Hookpoint, [FullMessage]),
|
||||
emqx_resource_metrics:received_inc(InstanceId),
|
||||
ok
|
||||
end
|
||||
|
|
|
@ -252,7 +252,7 @@ do_handle_message(Message, State) ->
|
|||
MQTTTopic = render(FullMessage, MQTTTopicTemplate),
|
||||
MQTTMessage = emqx_message:make(ResourceId, MQTTQoS, MQTTTopic, Payload),
|
||||
_ = emqx:publish(MQTTMessage),
|
||||
emqx:run_hook(Hookpoint, [FullMessage]),
|
||||
emqx_hooks:run(Hookpoint, [FullMessage]),
|
||||
emqx_resource_metrics:received_inc(ResourceId),
|
||||
%% note: just `ack' does not commit the offset to the
|
||||
%% kafka consumer group.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
%% -*- mode: erlang -*-
|
||||
{application, emqx_bridge_mqtt, [
|
||||
{description, "EMQX MQTT Broker Bridge"},
|
||||
{vsn, "0.1.3"},
|
||||
{vsn, "0.1.4"},
|
||||
{registered, []},
|
||||
{applications, [
|
||||
kernel,
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
%% if the bridge received msgs from the remote broker.
|
||||
on_message_received(Msg, HookPoint, ResId) ->
|
||||
emqx_resource_metrics:received_inc(ResId),
|
||||
emqx:run_hook(HookPoint, [Msg]).
|
||||
emqx_hooks:run(HookPoint, [Msg]).
|
||||
|
||||
%% ===================================================================
|
||||
callback_mode() -> async_if_possible.
|
||||
|
|
|
@ -286,7 +286,7 @@ handle_call(
|
|||
ok = emqx_broker:unsubscribe(MountedTopic),
|
||||
_ = run_hooks(
|
||||
Ctx,
|
||||
'session.unsubscribe',
|
||||
'session.unsubscribed',
|
||||
[ClientInfo, MountedTopic, #{}]
|
||||
),
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{application, emqx_gateway_lwm2m, [
|
||||
{description, "LwM2M Gateway"},
|
||||
{vsn, "0.1.2"},
|
||||
{vsn, "0.1.3"},
|
||||
{registered, []},
|
||||
{applications, [kernel, stdlib, emqx, emqx_gateway, emqx_gateway_coap]},
|
||||
{env, []},
|
||||
|
|
|
@ -264,7 +264,7 @@ handle_call(
|
|||
ok = emqx_broker:unsubscribe(MountedTopic),
|
||||
_ = run_hooks(
|
||||
Ctx,
|
||||
'session.unsubscribe',
|
||||
'session.unsubscribed',
|
||||
[ClientInfo, MountedTopic, #{}]
|
||||
),
|
||||
%% modify session state
|
||||
|
@ -331,7 +331,7 @@ terminate(Reason, #channel{
|
|||
session = Session
|
||||
}) ->
|
||||
MountedTopic = emqx_lwm2m_session:on_close(Session),
|
||||
_ = run_hooks(Ctx, 'session.unsubscribe', [ClientInfo, MountedTopic, #{}]),
|
||||
_ = run_hooks(Ctx, 'session.unsubscribed', [ClientInfo, MountedTopic, #{}]),
|
||||
run_hooks(Ctx, 'session.terminated', [ClientInfo, Reason, Session]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{application, emqx_gateway_stomp, [
|
||||
{description, "Stomp Gateway"},
|
||||
{vsn, "0.1.2"},
|
||||
{vsn, "0.1.3"},
|
||||
{registered, []},
|
||||
{applications, [kernel, stdlib, emqx, emqx_gateway]},
|
||||
{env, []},
|
||||
|
|
|
@ -549,7 +549,7 @@ handle_in(
|
|||
ok = emqx_broker:unsubscribe(MountedTopic),
|
||||
_ = run_hooks(
|
||||
Ctx,
|
||||
'session.unsubscribe',
|
||||
'session.unsubscribed',
|
||||
[ClientInfo, MountedTopic, #{}]
|
||||
),
|
||||
{ok, Channel#channel{subscriptions = lists:keydelete(SubId, 1, Subs)}};
|
||||
|
@ -869,7 +869,7 @@ handle_call(
|
|||
ok = emqx_broker:unsubscribe(MountedTopic),
|
||||
_ = run_hooks(
|
||||
Ctx,
|
||||
'session.unsubscribe',
|
||||
'session.unsubscribed',
|
||||
[ClientInfo, MountedTopic, #{}]
|
||||
),
|
||||
reply(
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-define(EMQX_PLUGIN_TEMPLATE_NAME, "emqx_plugin_template").
|
||||
-define(EMQX_PLUGIN_TEMPLATE_VSN, "5.0.0").
|
||||
-define(EMQX_PLUGIN_TEMPLATE_NAME, "my_emqx_plugin").
|
||||
-define(EMQX_PLUGIN_TEMPLATE_VSN, "5.1.0").
|
||||
-define(PACKAGE_SUFFIX, ".tar.gz").
|
||||
|
||||
-define(CLUSTER_API_SERVER(PORT), ("http://127.0.0.1:" ++ (integer_to_list(PORT)))).
|
||||
|
|
|
@ -22,12 +22,13 @@
|
|||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-define(EMQX_PLUGIN_TEMPLATE_RELEASE_NAME, "emqx_plugin_template").
|
||||
-define(EMQX_PLUGIN_APP_NAME, my_emqx_plugin).
|
||||
-define(EMQX_PLUGIN_TEMPLATE_RELEASE_NAME, atom_to_list(?EMQX_PLUGIN_APP_NAME)).
|
||||
-define(EMQX_PLUGIN_TEMPLATE_URL,
|
||||
"https://github.com/emqx/emqx-plugin-template/releases/download/"
|
||||
).
|
||||
-define(EMQX_PLUGIN_TEMPLATE_VSN, "5.0.0").
|
||||
-define(EMQX_PLUGIN_TEMPLATE_TAG, "5.0.0").
|
||||
-define(EMQX_PLUGIN_TEMPLATE_VSN, "5.1.0").
|
||||
-define(EMQX_PLUGIN_TEMPLATE_TAG, "5.1.0").
|
||||
-define(EMQX_ELIXIR_PLUGIN_TEMPLATE_RELEASE_NAME, "elixir_plugin_template").
|
||||
-define(EMQX_ELIXIR_PLUGIN_TEMPLATE_URL,
|
||||
"https://github.com/emqx/emqx-elixir-plugin/releases/download/"
|
||||
|
@ -153,11 +154,11 @@ t_demo_install_start_stop_uninstall(Config) ->
|
|||
?assertEqual([maps:without([readme], Info)], emqx_plugins:list()),
|
||||
%% start
|
||||
ok = emqx_plugins:ensure_started(NameVsn),
|
||||
ok = assert_app_running(emqx_plugin_template, true),
|
||||
ok = assert_app_running(?EMQX_PLUGIN_APP_NAME, true),
|
||||
ok = assert_app_running(map_sets, true),
|
||||
%% start (idempotent)
|
||||
ok = emqx_plugins:ensure_started(bin(NameVsn)),
|
||||
ok = assert_app_running(emqx_plugin_template, true),
|
||||
ok = assert_app_running(?EMQX_PLUGIN_APP_NAME, true),
|
||||
ok = assert_app_running(map_sets, true),
|
||||
|
||||
%% running app can not be un-installed
|
||||
|
@ -168,11 +169,11 @@ t_demo_install_start_stop_uninstall(Config) ->
|
|||
|
||||
%% stop
|
||||
ok = emqx_plugins:ensure_stopped(NameVsn),
|
||||
ok = assert_app_running(emqx_plugin_template, false),
|
||||
ok = assert_app_running(?EMQX_PLUGIN_APP_NAME, false),
|
||||
ok = assert_app_running(map_sets, false),
|
||||
%% stop (idempotent)
|
||||
ok = emqx_plugins:ensure_stopped(bin(NameVsn)),
|
||||
ok = assert_app_running(emqx_plugin_template, false),
|
||||
ok = assert_app_running(?EMQX_PLUGIN_APP_NAME, false),
|
||||
ok = assert_app_running(map_sets, false),
|
||||
%% still listed after stopped
|
||||
ReleaseNameBin = list_to_binary(ReleaseName),
|
||||
|
@ -257,9 +258,9 @@ t_start_restart_and_stop(Config) ->
|
|||
%% fake a disabled plugin in config
|
||||
ok = ensure_state(Bar2, front, false),
|
||||
|
||||
assert_app_running(emqx_plugin_template, false),
|
||||
assert_app_running(?EMQX_PLUGIN_APP_NAME, false),
|
||||
ok = emqx_plugins:ensure_started(),
|
||||
assert_app_running(emqx_plugin_template, true),
|
||||
assert_app_running(?EMQX_PLUGIN_APP_NAME, true),
|
||||
|
||||
%% fake enable bar-2
|
||||
ok = ensure_state(Bar2, rear, true),
|
||||
|
@ -269,18 +270,18 @@ t_start_restart_and_stop(Config) ->
|
|||
emqx_plugins:ensure_started()
|
||||
),
|
||||
%% but demo plugin should still be running
|
||||
assert_app_running(emqx_plugin_template, true),
|
||||
assert_app_running(?EMQX_PLUGIN_APP_NAME, true),
|
||||
|
||||
%% stop all
|
||||
ok = emqx_plugins:ensure_stopped(),
|
||||
assert_app_running(emqx_plugin_template, false),
|
||||
assert_app_running(?EMQX_PLUGIN_APP_NAME, false),
|
||||
ok = ensure_state(Bar2, rear, false),
|
||||
|
||||
ok = emqx_plugins:restart(NameVsn),
|
||||
assert_app_running(emqx_plugin_template, true),
|
||||
assert_app_running(?EMQX_PLUGIN_APP_NAME, true),
|
||||
%% repeat
|
||||
ok = emqx_plugins:restart(NameVsn),
|
||||
assert_app_running(emqx_plugin_template, true),
|
||||
assert_app_running(?EMQX_PLUGIN_APP_NAME, true),
|
||||
|
||||
ok = emqx_plugins:ensure_stopped(),
|
||||
ok = emqx_plugins:ensure_disabled(NameVsn),
|
||||
|
|
Loading…
Reference in New Issue