From 1a4afabe9fed06bb0038f95c898be69c72b94bf3 Mon Sep 17 00:00:00 2001 From: firest Date: Wed, 13 Apr 2022 17:30:37 +0800 Subject: [PATCH] chore(exhook): reformat exhook codes --- apps/emqx_exhook/include/emqx_exhook.hrl | 42 +- apps/emqx_exhook/rebar.config | 65 ++- apps/emqx_exhook/src/emqx_exhook.app.src | 24 +- apps/emqx_exhook/src/emqx_exhook.appup.src | 13 +- apps/emqx_exhook/src/emqx_exhook.erl | 71 ++- apps/emqx_exhook/src/emqx_exhook_api.erl | 484 ++++++++------- apps/emqx_exhook/src/emqx_exhook_app.erl | 9 +- apps/emqx_exhook/src/emqx_exhook_handler.erl | 402 ++++++++----- apps/emqx_exhook/src/emqx_exhook_metrics.erl | 323 ++++++---- apps/emqx_exhook/src/emqx_exhook_mgr.erl | 413 +++++++------ apps/emqx_exhook/src/emqx_exhook_schema.erl | 109 ++-- apps/emqx_exhook/src/emqx_exhook_server.erl | 401 ++++++++----- apps/emqx_exhook/src/emqx_exhook_sup.erl | 34 +- .../src/proto/emqx_exhook_proto_v1.erl | 17 +- apps/emqx_exhook/test/emqx_exhook_SUITE.erl | 192 +++--- .../test/emqx_exhook_api_SUITE.erl | 225 ++++--- .../emqx_exhook/test/emqx_exhook_demo_svr.erl | 349 ++++++----- .../test/emqx_exhook_metrics_SUITE.erl | 187 +++--- .../test/props/prop_exhook_hooks.erl | 552 ++++++++++-------- 19 files changed, 2299 insertions(+), 1613 deletions(-) diff --git a/apps/emqx_exhook/include/emqx_exhook.hrl b/apps/emqx_exhook/include/emqx_exhook.hrl index 4ed9e16cc..6c386e688 100644 --- a/apps/emqx_exhook/include/emqx_exhook.hrl +++ b/apps/emqx_exhook/include/emqx_exhook.hrl @@ -21,27 +21,27 @@ -define(HOOKS_REF_COUNTER, emqx_exhook_ref_counter). -define(HOOKS_METRICS, emqx_exhook_metrics). --define(ENABLED_HOOKS, - [ {'client.connect', {emqx_exhook_handler, on_client_connect, []}} - , {'client.connack', {emqx_exhook_handler, on_client_connack, []}} - , {'client.connected', {emqx_exhook_handler, on_client_connected, []}} - , {'client.disconnected', {emqx_exhook_handler, on_client_disconnected, []}} - , {'client.authenticate', {emqx_exhook_handler, on_client_authenticate, []}} - , {'client.authorize', {emqx_exhook_handler, on_client_authorize, []}} - , {'client.subscribe', {emqx_exhook_handler, on_client_subscribe, []}} - , {'client.unsubscribe', {emqx_exhook_handler, on_client_unsubscribe, []}} - , {'session.created', {emqx_exhook_handler, on_session_created, []}} - , {'session.subscribed', {emqx_exhook_handler, on_session_subscribed, []}} - , {'session.unsubscribed',{emqx_exhook_handler, on_session_unsubscribed, []}} - , {'session.resumed', {emqx_exhook_handler, on_session_resumed, []}} - , {'session.discarded', {emqx_exhook_handler, on_session_discarded, []}} - , {'session.takenover', {emqx_exhook_handler, on_session_takenover, []}} - , {'session.terminated', {emqx_exhook_handler, on_session_terminated, []}} - , {'message.publish', {emqx_exhook_handler, on_message_publish, []}} - , {'message.delivered', {emqx_exhook_handler, on_message_delivered, []}} - , {'message.acked', {emqx_exhook_handler, on_message_acked, []}} - , {'message.dropped', {emqx_exhook_handler, on_message_dropped, []}} - ]). +-define(ENABLED_HOOKS, [ + {'client.connect', {emqx_exhook_handler, on_client_connect, []}}, + {'client.connack', {emqx_exhook_handler, on_client_connack, []}}, + {'client.connected', {emqx_exhook_handler, on_client_connected, []}}, + {'client.disconnected', {emqx_exhook_handler, on_client_disconnected, []}}, + {'client.authenticate', {emqx_exhook_handler, on_client_authenticate, []}}, + {'client.authorize', {emqx_exhook_handler, on_client_authorize, []}}, + {'client.subscribe', {emqx_exhook_handler, on_client_subscribe, []}}, + {'client.unsubscribe', {emqx_exhook_handler, on_client_unsubscribe, []}}, + {'session.created', {emqx_exhook_handler, on_session_created, []}}, + {'session.subscribed', {emqx_exhook_handler, on_session_subscribed, []}}, + {'session.unsubscribed', {emqx_exhook_handler, on_session_unsubscribed, []}}, + {'session.resumed', {emqx_exhook_handler, on_session_resumed, []}}, + {'session.discarded', {emqx_exhook_handler, on_session_discarded, []}}, + {'session.takenover', {emqx_exhook_handler, on_session_takenover, []}}, + {'session.terminated', {emqx_exhook_handler, on_session_terminated, []}}, + {'message.publish', {emqx_exhook_handler, on_message_publish, []}}, + {'message.delivered', {emqx_exhook_handler, on_message_delivered, []}}, + {'message.acked', {emqx_exhook_handler, on_message_acked, []}}, + {'message.dropped', {emqx_exhook_handler, on_message_dropped, []}} +]). -endif. diff --git a/apps/emqx_exhook/rebar.config b/apps/emqx_exhook/rebar.config index 8229c3a55..cc9a680c3 100644 --- a/apps/emqx_exhook/rebar.config +++ b/apps/emqx_exhook/rebar.config @@ -1,42 +1,57 @@ %%-*- mode: erlang -*- -{plugins, - [rebar3_proper, - {grpc_plugin, {git, "https://github.com/HJianBo/grpc_plugin", {tag, "v0.10.2"}}} +{plugins, [ + rebar3_proper, + {grpc_plugin, {git, "https://github.com/HJianBo/grpc_plugin", {tag, "v0.10.2"}}} ]}. -{deps, - [ {emqx, {path, "../emqx"}} - , {grpc, {git, "https://github.com/emqx/grpc-erl", {tag, "0.6.4"}}} +{deps, [ + {emqx, {path, "../emqx"}}, + {grpc, {git, "https://github.com/emqx/grpc-erl", {tag, "0.6.4"}}} ]}. -{grpc, - [{protos, ["priv/protos"]}, - {gpb_opts, [{module_name_prefix, "emqx_"}, - {module_name_suffix, "_pb"}]} +{grpc, [ + {protos, ["priv/protos"]}, + {gpb_opts, [ + {module_name_prefix, "emqx_"}, + {module_name_suffix, "_pb"} + ]} ]}. -{provider_hooks, - [{pre, [{compile, {grpc, gen}}, - {clean, {grpc, clean}}]} +{provider_hooks, [ + {pre, [ + {compile, {grpc, gen}}, + {clean, {grpc, clean}} + ]} ]}. {edoc_opts, [{preprocess, true}]}. -{erl_opts, [warn_unused_vars, - warn_shadow_vars, - warn_unused_import, - warn_obsolete_guard, - debug_info, - {parse_transform}]}. +{erl_opts, [ + warn_unused_vars, + warn_shadow_vars, + warn_unused_import, + warn_obsolete_guard, + debug_info, + {parse_transform} +]}. -{xref_checks, [undefined_function_calls, undefined_functions, - locals_not_used, deprecated_function_calls, - warnings_as_errors, deprecated_functions]}. +{xref_checks, [ + undefined_function_calls, + undefined_functions, + locals_not_used, + deprecated_function_calls, + warnings_as_errors, + deprecated_functions +]}. {xref_ignores, [emqx_exhook_pb]}. {cover_enabled, true}. {cover_opts, [verbose]}. {cover_export_enabled, true}. -{cover_excl_mods, [emqx_exhook_pb, - emqx_exhook_v_1_hook_provider_bhvr, - emqx_exhook_v_1_hook_provider_client]}. +{cover_excl_mods, [ + emqx_exhook_pb, + emqx_exhook_v_1_hook_provider_bhvr, + emqx_exhook_v_1_hook_provider_client +]}. + +{project_plugins, [erlfmt]}. diff --git a/apps/emqx_exhook/src/emqx_exhook.app.src b/apps/emqx_exhook/src/emqx_exhook.app.src index 354a3c763..6437acd81 100644 --- a/apps/emqx_exhook/src/emqx_exhook.app.src +++ b/apps/emqx_exhook/src/emqx_exhook.app.src @@ -1,13 +1,13 @@ %% -*- mode: erlang -*- -{application, emqx_exhook, - [{description, "EMQX Extension for Hook"}, - {vsn, "5.0.0"}, - {modules, []}, - {registered, []}, - {mod, {emqx_exhook_app, []}}, - {applications, [kernel,stdlib,grpc,emqx]}, - {env,[]}, - {licenses, ["Apache-2.0"]}, - {maintainers, ["EMQX Team "]}, - {links, [{"Homepage", "https://emqx.io/"}]} - ]}. +{application, emqx_exhook, [ + {description, "EMQX Extension for Hook"}, + {vsn, "5.0.0"}, + {modules, []}, + {registered, []}, + {mod, {emqx_exhook_app, []}}, + {applications, [kernel, stdlib, grpc, emqx]}, + {env, []}, + {licenses, ["Apache-2.0"]}, + {maintainers, ["EMQX Team "]}, + {links, [{"Homepage", "https://emqx.io/"}]} +]}. diff --git a/apps/emqx_exhook/src/emqx_exhook.appup.src b/apps/emqx_exhook/src/emqx_exhook.appup.src index 5ca8ba8da..3eab44dca 100644 --- a/apps/emqx_exhook/src/emqx_exhook.appup.src +++ b/apps/emqx_exhook/src/emqx_exhook.appup.src @@ -1,9 +1,8 @@ %% -*- mode: erlang -*- {VSN, - [ - {<<".*">>, []} - ], - [ - {<<".*">>, []} - ] -}. + [ + {<<".*">>, []} + ], + [ + {<<".*">>, []} + ]}. diff --git a/apps/emqx_exhook/src/emqx_exhook.erl b/apps/emqx_exhook/src/emqx_exhook.erl index 288bad9f2..f01ff4e7a 100644 --- a/apps/emqx_exhook/src/emqx_exhook.erl +++ b/apps/emqx_exhook/src/emqx_exhook.erl @@ -19,9 +19,10 @@ -include("emqx_exhook.hrl"). -include_lib("emqx/include/logger.hrl"). --export([ cast/2 - , call_fold/3 - ]). +-export([ + cast/2, + call_fold/3 +]). %% exported for `emqx_telemetry' -export([get_basic_usage_info/0]). @@ -38,12 +39,16 @@ cast(_, _, []) -> ok; cast(Hookpoint, Req, [ServerName | More]) -> %% XXX: Need a real asynchronous running - _ = emqx_exhook_server:call(Hookpoint, Req, - emqx_exhook_mgr:server(ServerName)), + _ = emqx_exhook_server:call( + Hookpoint, + Req, + emqx_exhook_mgr:server(ServerName) + ), cast(Hookpoint, Req, More). --spec call_fold(atom(), term(), function()) -> {ok, term()} - | {stop, term()}. +-spec call_fold(atom(), term(), function()) -> + {ok, term()} + | {stop, term()}. call_fold(Hookpoint, Req, AccFun) -> case emqx_exhook_mgr:running() of [] -> @@ -90,33 +95,43 @@ deny_action_result('message.publish', Msg) -> %%-------------------------------------------------------------------- -spec get_basic_usage_info() -> - #{ num_servers => non_neg_integer() - , servers => - [#{ driver => Driver - , hooks => [emqx_exhook_server:hookpoint()] - }] - } when Driver :: grpc. + #{ + num_servers => non_neg_integer(), + servers => + [ + #{ + driver => Driver, + hooks => [emqx_exhook_server:hookpoint()] + } + ] + } +when + Driver :: grpc. get_basic_usage_info() -> try Servers = emqx_exhook_mgr:running(), NumServers = length(Servers), ServerInfo = lists:map( - fun(ServerName) -> - Hooks = emqx_exhook_mgr:hooks(ServerName), - HookNames = lists:map(fun(#{name := Name}) -> Name end, Hooks), - #{ hooks => HookNames - , %% currently, only grpc driver exists. - driver => grpc - } - end, - Servers), - #{ num_servers => NumServers - , servers => ServerInfo - } + fun(ServerName) -> + Hooks = emqx_exhook_mgr:hooks(ServerName), + HookNames = lists:map(fun(#{name := Name}) -> Name end, Hooks), + #{ + hooks => HookNames, + %% currently, only grpc driver exists. + driver => grpc + } + end, + Servers + ), + #{ + num_servers => NumServers, + servers => ServerInfo + } catch _:_ -> - #{ num_servers => 0 - , servers => [] - } + #{ + num_servers => 0, + servers => [] + } end. diff --git a/apps/emqx_exhook/src/emqx_exhook_api.erl b/apps/emqx_exhook/src/emqx_exhook_api.erl index 2a3ae8c84..27f8203ba 100644 --- a/apps/emqx_exhook/src/emqx_exhook_api.erl +++ b/apps/emqx_exhook/src/emqx_exhook_api.erl @@ -33,10 +33,13 @@ -define(BAD_REQUEST, 'BAD_REQUEST'). -define(BAD_RPC, 'BAD_RPC'). --dialyzer([{nowarn_function, [ fill_cluster_server_info/5 - , nodes_server_info/5 - , fill_server_hooks_info/4 - ]}]). +-dialyzer([ + {nowarn_function, [ + fill_cluster_server_info/5, + nodes_server_info/5, + fill_server_hooks_info/4 + ]} +]). %%-------------------------------------------------------------------- %% schema @@ -50,142 +53,174 @@ paths() -> ["/exhooks", "/exhooks/:name", "/exhooks/:name/move", "/exhooks/:name schema(("/exhooks")) -> #{ - 'operationId' => exhooks, - get => #{tags => ?TAGS, - desc => <<"List all servers">>, - responses => #{200 => mk(array(ref(detail_server_info)), #{})} - }, - post => #{tags => ?TAGS, - desc => <<"Add a servers">>, - 'requestBody' => server_conf_schema(), - responses => #{201 => mk(ref(detail_server_info), #{}), - 500 => error_codes([?BAD_RPC], <<"Bad RPC">>) - } - } - }; - + 'operationId' => exhooks, + get => #{ + tags => ?TAGS, + desc => <<"List all servers">>, + responses => #{200 => mk(array(ref(detail_server_info)), #{})} + }, + post => #{ + tags => ?TAGS, + desc => <<"Add a servers">>, + 'requestBody' => server_conf_schema(), + responses => #{ + 201 => mk(ref(detail_server_info), #{}), + 500 => error_codes([?BAD_RPC], <<"Bad RPC">>) + } + } + }; schema("/exhooks/:name") -> - #{'operationId' => action_with_name, - get => #{tags => ?TAGS, - desc => <<"Get the detail information of server">>, - parameters => params_server_name_in_path(), - responses => #{200 => mk(ref(detail_server_info), #{}), - 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) - } - }, - put => #{tags => ?TAGS, - desc => <<"Update the server">>, - parameters => params_server_name_in_path(), - 'requestBody' => server_conf_schema(), - responses => #{200 => <<>>, - 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), - 500 => error_codes([?BAD_RPC], <<"Bad RPC">>) - } - }, - delete => #{tags => ?TAGS, - desc => <<"Delete the server">>, - parameters => params_server_name_in_path(), - responses => #{204 => <<>>, - 500 => error_codes([?BAD_RPC], <<"Bad RPC">>) - } - } - }; - + #{ + 'operationId' => action_with_name, + get => #{ + tags => ?TAGS, + desc => <<"Get the detail information of server">>, + parameters => params_server_name_in_path(), + responses => #{ + 200 => mk(ref(detail_server_info), #{}), + 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + } + }, + put => #{ + tags => ?TAGS, + desc => <<"Update the server">>, + parameters => params_server_name_in_path(), + 'requestBody' => server_conf_schema(), + responses => #{ + 200 => <<>>, + 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), + 500 => error_codes([?BAD_RPC], <<"Bad RPC">>) + } + }, + delete => #{ + tags => ?TAGS, + desc => <<"Delete the server">>, + parameters => params_server_name_in_path(), + responses => #{ + 204 => <<>>, + 500 => error_codes([?BAD_RPC], <<"Bad RPC">>) + } + } + }; schema("/exhooks/:name/hooks") -> - #{'operationId' => server_hooks, - get => #{tags => ?TAGS, - desc => <<"Get the hooks information of server">>, - parameters => params_server_name_in_path(), - responses => #{200 => mk(array(ref(list_hook_info)), #{}), - 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) - } - } - }; - + #{ + 'operationId' => server_hooks, + get => #{ + tags => ?TAGS, + desc => <<"Get the hooks information of server">>, + parameters => params_server_name_in_path(), + responses => #{ + 200 => mk(array(ref(list_hook_info)), #{}), + 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) + } + } + }; schema("/exhooks/:name/move") -> - #{'operationId' => move, - post => #{tags => ?TAGS, - desc => - <<"Move the server.\n", - "NOTE: The position should be \"front|rear|before:{name}|after:{name}\"\n">>, - parameters => params_server_name_in_path(), - 'requestBody' => emqx_dashboard_swagger:schema_with_examples( - ref(move_req), - position_example()), - responses => #{204 => <<"No Content">>, - 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), - 500 => error_codes([?BAD_RPC], <<"Bad RPC">>) - } - } - }. + #{ + 'operationId' => move, + post => #{ + tags => ?TAGS, + desc => + <<"Move the server.\n", + "NOTE: The position should be \"front|rear|before:{name}|after:{name}\"\n">>, + parameters => params_server_name_in_path(), + 'requestBody' => emqx_dashboard_swagger:schema_with_examples( + ref(move_req), + position_example() + ), + responses => #{ + 204 => <<"No Content">>, + 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), + 500 => error_codes([?BAD_RPC], <<"Bad RPC">>) + } + } + }. fields(move_req) -> - [{position, mk(string(), #{ desc => <<"The target position to be moved.">> - , example => <<"front">>})}]; - + [ + {position, + mk(string(), #{ + desc => <<"The target position to be moved.">>, + example => <<"front">> + })} + ]; fields(detail_server_info) -> - [ {metrics, mk(ref(metrics), #{})} - , {node_metrics, mk(array(ref(node_metrics)), #{})} - , {node_status, mk(array(ref(node_status)), #{})} - , {hooks, mk(array(ref(hook_info)), #{})} + [ + {metrics, mk(ref(metrics), #{})}, + {node_metrics, mk(array(ref(node_metrics)), #{})}, + {node_status, mk(array(ref(node_status)), #{})}, + {hooks, mk(array(ref(hook_info)), #{})} ] ++ emqx_exhook_schema:server_config(); - fields(list_hook_info) -> - [ {name, mk(binary(), #{desc => <<"The hook's name">>})} - , {params, mk(map(name, binary()), - #{desc => <<"The parameters used when the hook is registered">>})} - , {metrics, mk(ref(metrics), #{})} - , {node_metrics, mk(array(ref(node_metrics)), #{})} + [ + {name, mk(binary(), #{desc => <<"The hook's name">>})}, + {params, + mk( + map(name, binary()), + #{desc => <<"The parameters used when the hook is registered">>} + )}, + {metrics, mk(ref(metrics), #{})}, + {node_metrics, mk(array(ref(node_metrics)), #{})} ]; - fields(node_metrics) -> - [ {node, mk(string(), #{})} - , {metrics, mk(ref(metrics), #{})} + [ + {node, mk(string(), #{})}, + {metrics, mk(ref(metrics), #{})} ]; - fields(node_status) -> - [ {node, mk(string(), #{})} - , {status, mk(enum([running, waiting, stopped, error]), #{})} + [ + {node, mk(string(), #{})}, + {status, mk(enum([running, waiting, stopped, error]), #{})} ]; - fields(hook_info) -> - [ {name, mk(binary(), #{desc => <<"The hook's name">>})} - , {params, mk(map(name, binary()), - #{desc => <<"The parameters used when the hook is registered">>})} + [ + {name, mk(binary(), #{desc => <<"The hook's name">>})}, + {params, + mk( + map(name, binary()), + #{desc => <<"The parameters used when the hook is registered">>} + )} ]; - fields(metrics) -> - [ {succeed, mk(integer(), #{})} - , {failed, mk(integer(), #{})} - , {rate, mk(integer(), #{})} - , {max_rate, mk(integer(), #{})} + [ + {succeed, mk(integer(), #{})}, + {failed, mk(integer(), #{})}, + {rate, mk(integer(), #{})}, + {max_rate, mk(integer(), #{})} ]; - fields(server_config) -> emqx_exhook_schema:server_config(). params_server_name_in_path() -> - [{name, mk(string(), #{in => path, - required => true, - example => <<"default">>})} + [ + {name, + mk(string(), #{ + in => path, + required => true, + example => <<"default">> + })} ]. server_conf_schema() -> - SSL = #{ enable => false - , cacertfile => emqx:cert_file(<<"cacert.pem">>) - , certfile => emqx:cert_file(<<"cert.pem">>) - , keyfile => emqx:cert_file(<<"key.pem">>) - }, - schema_with_example(ref(server_config), - #{ name => "default" - , enable => true - , url => <<"http://127.0.0.1:8081">> - , request_timeout => "5s" - , failed_action => deny - , auto_reconnect => "60s" - , pool_size => 8 - , ssl => SSL - }). + SSL = #{ + enable => false, + cacertfile => emqx:cert_file(<<"cacert.pem">>), + certfile => emqx:cert_file(<<"cert.pem">>), + keyfile => emqx:cert_file(<<"key.pem">>) + }, + schema_with_example( + ref(server_config), + #{ + name => "default", + enable => true, + url => <<"http://127.0.0.1:8081">>, + request_timeout => "5s", + failed_action => deny, + auto_reconnect => "60s", + pool_size => 8, + ssl => SSL + } + ). %%-------------------------------------------------------------------- %% API @@ -194,7 +229,6 @@ exhooks(get, _) -> Confs = emqx:get_config([exhook, servers]), Infos = nodes_all_server_info(Confs), {200, Infos}; - exhooks(post, #{body := Body}) -> {ok, _} = emqx_exhook_mgr:update_config([exhook, servers], {add, Body}), #{<<"name">> := Name} = Body, @@ -202,67 +236,86 @@ exhooks(post, #{body := Body}) -> action_with_name(get, #{bindings := #{name := Name}}) -> get_nodes_server_info(Name); - action_with_name(put, #{bindings := #{name := Name}, body := Body}) -> - case emqx_exhook_mgr:update_config([exhook, servers], - {update, Name, Body}) of + case + emqx_exhook_mgr:update_config( + [exhook, servers], + {update, Name, Body} + ) + of {ok, not_found} -> - {400, #{code => <<"BAD_REQUEST">>, - message => <<"Server not found">> - }}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => <<"Server not found">> + }}; {ok, {error, Reason}} -> - {400, #{code => <<"BAD_REQUEST">>, - message => unicode:characters_to_binary( - io_lib:format("Error Reason:~p~n", [Reason])) - }}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => unicode:characters_to_binary( + io_lib:format("Error Reason:~p~n", [Reason]) + ) + }}; {ok, _} -> {200}; {error, Error} -> - {500, #{code => <<"BAD_RPC">>, - message => Error - }} + {500, #{ + code => <<"BAD_RPC">>, + message => Error + }} end; - action_with_name(delete, #{bindings := #{name := Name}}) -> - case emqx_exhook_mgr:update_config([exhook, servers], - {delete, Name}) of + case + emqx_exhook_mgr:update_config( + [exhook, servers], + {delete, Name} + ) + of {ok, _} -> {200}; {error, Error} -> - {500, #{code => <<"BAD_RPC">>, - message => Error - }} + {500, #{ + code => <<"BAD_RPC">>, + message => Error + }} end. move(post, #{bindings := #{name := Name}, body := #{<<"position">> := RawPosition}}) -> case parse_position(RawPosition) of {ok, Position} -> - case emqx_exhook_mgr:update_config([exhook, servers], - {move, Name, Position}) of + case + emqx_exhook_mgr:update_config( + [exhook, servers], + {move, Name, Position} + ) + of {ok, ok} -> {204}; {ok, not_found} -> - {400, #{code => <<"BAD_REQUEST">>, - message => <<"Server not found">> - }}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => <<"Server not found">> + }}; {error, Error} -> - {500, #{code => <<"BAD_RPC">>, - message => Error - }} + {500, #{ + code => <<"BAD_RPC">>, + message => Error + }} end; {error, invalid_position} -> - {400, #{code => <<"BAD_REQUEST">>, - message => <<"Invalid Position">> - }} + {400, #{ + code => <<"BAD_REQUEST">>, + message => <<"Invalid Position">> + }} end. server_hooks(get, #{bindings := #{name := Name}}) -> Confs = emqx:get_config([exhook, servers]), case lists:search(fun(#{name := CfgName}) -> CfgName =:= Name end, Confs) of false -> - {400, #{code => <<"BAD_REQUEST">>, - message => <<"Server not found">> - }}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => <<"Server not found">> + }}; _ -> Info = get_nodes_server_hooks_info(Name), {200, Info} @@ -272,9 +325,10 @@ get_nodes_server_info(Name) -> Confs = emqx:get_config([exhook, servers]), case lists:search(fun(#{name := CfgName}) -> CfgName =:= Name end, Confs) of false -> - {400, #{code => <<"BAD_REQUEST">>, - message => <<"Server not found">> - }}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => <<"Server not found">> + }}; {value, Conf} -> NodeStatus = nodes_server_info(Name), {200, maps:merge(Conf, NodeStatus)} @@ -292,33 +346,34 @@ node_all_server_info([#{name := ServerName} = Conf | T], AllInfos, Default, Acc) Info = fill_cluster_server_info(AllInfos, [], [], ServerName, Default), AllInfo = maps:merge(Conf, Info), node_all_server_info(T, AllInfos, Default, [AllInfo | Acc]); - node_all_server_info([], _, _, Acc) -> lists:reverse(Acc). fill_cluster_server_info([{Node, {error, _}} | T], StatusL, MetricsL, ServerName, Default) -> - fill_cluster_server_info(T, - [#{node => Node, status => error} | StatusL], - [#{node => Node, metrics => Default} | MetricsL], - ServerName, - Default); - + fill_cluster_server_info( + T, + [#{node => Node, status => error} | StatusL], + [#{node => Node, metrics => Default} | MetricsL], + ServerName, + Default + ); fill_cluster_server_info([{Node, Result} | T], StatusL, MetricsL, ServerName, Default) -> #{status := Status, metrics := Metrics} = Result, fill_cluster_server_info( - T, - [#{node => Node, status => maps:get(ServerName, Status, error)} | StatusL], - [#{node => Node, metrics => maps:get(ServerName, Metrics, Default)} | MetricsL], - ServerName, - Default); - + T, + [#{node => Node, status => maps:get(ServerName, Status, error)} | StatusL], + [#{node => Node, metrics => maps:get(ServerName, Metrics, Default)} | MetricsL], + ServerName, + Default + ); fill_cluster_server_info([], StatusL, MetricsL, ServerName, _) -> Metrics = emqx_exhook_metrics:metrics_aggregate_by_key(metrics, MetricsL), - #{metrics => Metrics, - node_metrics => MetricsL, - node_status => StatusL, - hooks => emqx_exhook_mgr:hooks(ServerName) - }. + #{ + metrics => Metrics, + node_metrics => MetricsL, + node_status => StatusL, + hooks => emqx_exhook_mgr:hooks(ServerName) + }. %%-------------------------------------------------------------------- %% GET /exhooks/{name} @@ -329,39 +384,41 @@ nodes_server_info(Name) -> nodes_server_info(InfoL, Name, Default, [], []). nodes_server_info([{Node, {error, _}} | T], Name, Default, StatusL, MetricsL) -> - nodes_server_info(T, - Name, - Default, - [#{node => Node, status => error} | StatusL], - [#{node => Node, metrics => Default} | MetricsL] - ); - + nodes_server_info( + T, + Name, + Default, + [#{node => Node, status => error} | StatusL], + [#{node => Node, metrics => Default} | MetricsL] + ); nodes_server_info([{Node, Result} | T], Name, Default, StatusL, MetricsL) -> #{status := Status, metrics := Metrics} = Result, - nodes_server_info(T, - Name, - Default, - [#{node => Node, status => Status} | StatusL], - [#{node => Node, metrics => Metrics} | MetricsL] - ); - + nodes_server_info( + T, + Name, + Default, + [#{node => Node, status => Status} | StatusL], + [#{node => Node, metrics => Metrics} | MetricsL] + ); nodes_server_info([], Name, _, StatusL, MetricsL) -> - #{metrics => emqx_exhook_metrics:metrics_aggregate_by_key(metrics, MetricsL), - node_status => StatusL, - node_metrics => MetricsL, - hooks => emqx_exhook_mgr:hooks(Name) - }. + #{ + metrics => emqx_exhook_metrics:metrics_aggregate_by_key(metrics, MetricsL), + node_status => StatusL, + node_metrics => MetricsL, + hooks => emqx_exhook_mgr:hooks(Name) + }. %%-------------------------------------------------------------------- %% GET /exhooks/{name}/hooks %%-------------------------------------------------------------------- get_nodes_server_hooks_info(Name) -> case emqx_exhook_mgr:hooks(Name) of - [] -> []; + [] -> + []; Hooks -> AllInfos = call_cluster(fun(Nodes) -> - emqx_exhook_proto_v1:server_hooks_metrics(Nodes, Name) - end), + emqx_exhook_proto_v1:server_hooks_metrics(Nodes, Name) + end), Default = emqx_exhook_metrics:new_metrics_info(), get_nodes_server_hooks_info(Hooks, AllInfos, Default, []) end. @@ -370,18 +427,15 @@ get_nodes_server_hooks_info([#{name := Name} = Spec | T], AllInfos, Default, Acc Info = fill_server_hooks_info(AllInfos, Name, Default, []), AllInfo = maps:merge(Spec, Info), get_nodes_server_hooks_info(T, AllInfos, Default, [AllInfo | Acc]); - get_nodes_server_hooks_info([], _, _, Acc) -> Acc. fill_server_hooks_info([{_, {error, _}} | T], Name, Default, MetricsL) -> fill_server_hooks_info(T, Name, Default, MetricsL); - fill_server_hooks_info([{Node, MetricsMap} | T], Name, Default, MetricsL) -> Metrics = maps:get(Name, MetricsMap, Default), NodeMetrics = #{node => Node, metrics => Metrics}, fill_server_hooks_info(T, Name, Default, [NodeMetrics | MetricsL]); - fill_server_hooks_info([], _Name, _Default, MetricsL) -> Metrics = emqx_exhook_metrics:metrics_aggregate_by_key(metrics, MetricsL), #{metrics => Metrics, node_metrics => MetricsL}. @@ -391,31 +445,39 @@ fill_server_hooks_info([], _Name, _Default, MetricsL) -> %%-------------------------------------------------------------------- -spec call_cluster(fun(([node()]) -> emqx_rpc:erpc_multicall(A))) -> - [{node(), A | {error, _Err}}]. + [{node(), A | {error, _Err}}]. call_cluster(Fun) -> Nodes = mria_mnesia:running_nodes(), Ret = Fun(Nodes), lists:zip(Nodes, lists:map(fun emqx_rpc:unwrap_erpc/1, Ret)). - %%-------------------------------------------------------------------- %% Internal Funcs %%-------------------------------------------------------------------- position_example() -> - #{ front => - #{ summary => <<"absolute position 'front'">> - , value => #{<<"position">> => <<"front">>}} - , rear => - #{ summary => <<"absolute position 'rear'">> - , value => #{<<"position">> => <<"rear">>}} - , related_before => - #{ summary => <<"relative position 'before'">> - , value => #{<<"position">> => <<"before:default">>}} - , related_after => - #{ summary => <<"relative position 'after'">> - , value => #{<<"position">> => <<"after:default">>}} - }. + #{ + front => + #{ + summary => <<"absolute position 'front'">>, + value => #{<<"position">> => <<"front">>} + }, + rear => + #{ + summary => <<"absolute position 'rear'">>, + value => #{<<"position">> => <<"rear">>} + }, + related_before => + #{ + summary => <<"relative position 'before'">>, + value => #{<<"position">> => <<"before:default">>} + }, + related_after => + #{ + summary => <<"relative position 'after'">>, + value => #{<<"position">> => <<"after:default">>} + } + }. parse_position(<<"front">>) -> {ok, ?CMD_MOVE_FRONT}; diff --git a/apps/emqx_exhook/src/emqx_exhook_app.erl b/apps/emqx_exhook/src/emqx_exhook_app.erl index 5b723da33..48bfcd2e4 100644 --- a/apps/emqx_exhook/src/emqx_exhook_app.erl +++ b/apps/emqx_exhook/src/emqx_exhook_app.erl @@ -20,10 +20,11 @@ -include("emqx_exhook.hrl"). --export([ start/2 - , stop/1 - , prep_stop/1 - ]). +-export([ + start/2, + stop/1, + prep_stop/1 +]). %%-------------------------------------------------------------------- %% Application callbacks diff --git a/apps/emqx_exhook/src/emqx_exhook_handler.erl b/apps/emqx_exhook/src/emqx_exhook_handler.erl index e93c965f2..0d6f2897d 100644 --- a/apps/emqx_exhook/src/emqx_exhook_handler.erl +++ b/apps/emqx_exhook/src/emqx_exhook_handler.erl @@ -20,47 +20,53 @@ -include_lib("emqx/include/emqx.hrl"). -include_lib("emqx/include/logger.hrl"). - --export([ on_client_connect/2 - , on_client_connack/3 - , on_client_connected/2 - , on_client_disconnected/3 - , on_client_authenticate/2 - , on_client_authorize/4 - , on_client_subscribe/3 - , on_client_unsubscribe/3 - ]). +-export([ + on_client_connect/2, + on_client_connack/3, + on_client_connected/2, + on_client_disconnected/3, + on_client_authenticate/2, + on_client_authorize/4, + on_client_subscribe/3, + on_client_unsubscribe/3 +]). %% Session Lifecircle Hooks --export([ on_session_created/2 - , on_session_subscribed/3 - , on_session_unsubscribed/3 - , on_session_resumed/2 - , on_session_discarded/2 - , on_session_takenover/2 - , on_session_terminated/3 - ]). +-export([ + on_session_created/2, + on_session_subscribed/3, + on_session_unsubscribed/3, + on_session_resumed/2, + on_session_discarded/2, + on_session_takenover/2, + on_session_terminated/3 +]). --export([ on_message_publish/1 - , on_message_dropped/3 - , on_message_delivered/2 - , on_message_acked/2 - ]). +-export([ + on_message_publish/1, + on_message_dropped/3, + on_message_delivered/2, + on_message_acked/2 +]). %% Utils --export([ message/1 - , headers/1 - , stringfy/1 - , merge_responsed_bool/2 - , merge_responsed_message/2 - , assign_to_message/2 - , clientinfo/1 - ]). +-export([ + message/1, + headers/1, + stringfy/1, + merge_responsed_bool/2, + merge_responsed_message/2, + assign_to_message/2, + clientinfo/1 +]). --import(emqx_exhook, - [ cast/2 - , call_fold/3 - ]). +-import( + emqx_exhook, + [ + cast/2, + call_fold/3 + ] +). -elvis([{elvis_style, god_modules, disable}]). @@ -69,15 +75,18 @@ %%-------------------------------------------------------------------- on_client_connect(ConnInfo, Props) -> - Req = #{conninfo => conninfo(ConnInfo), - props => properties(Props) - }, + Req = #{ + conninfo => conninfo(ConnInfo), + props => properties(Props) + }, cast('client.connect', Req). on_client_connack(ConnInfo, Rc, Props) -> - Req = #{conninfo => conninfo(ConnInfo), - result_code => stringfy(Rc), - props => properties(Props)}, + Req = #{ + conninfo => conninfo(ConnInfo), + result_code => stringfy(Rc), + props => properties(Props) + }, cast('client.connack', Req). on_client_connected(ClientInfo, _ConnInfo) -> @@ -85,9 +94,10 @@ on_client_connected(ClientInfo, _ConnInfo) -> cast('client.connected', Req). on_client_disconnected(ClientInfo, Reason, _ConnInfo) -> - Req = #{clientinfo => clientinfo(ClientInfo), - reason => stringfy(Reason) - }, + Req = #{ + clientinfo => clientinfo(ClientInfo), + reason => stringfy(Reason) + }, cast('client.disconnected', Req). on_client_authenticate(ClientInfo, AuthResult) -> @@ -98,14 +108,24 @@ on_client_authenticate(ClientInfo, AuthResult) -> %% detailed info too. %% Bool = AuthResult == ok, - Req = #{clientinfo => clientinfo(ClientInfo), - result => Bool - }, + Req = #{ + clientinfo => clientinfo(ClientInfo), + result => Bool + }, - case call_fold('client.authenticate', Req, - fun merge_responsed_bool/2) of + case + call_fold( + 'client.authenticate', + Req, + fun merge_responsed_bool/2 + ) + of {StopOrOk, #{result := Result0}} when is_boolean(Result0) -> - Result = case Result0 of true -> ok; _ -> {error, not_authorized} end, + Result = + case Result0 of + true -> ok; + _ -> {error, not_authorized} + end, {StopOrOk, Result}; _ -> {ok, AuthResult} @@ -113,35 +133,49 @@ on_client_authenticate(ClientInfo, AuthResult) -> on_client_authorize(ClientInfo, PubSub, Topic, Result) -> Bool = Result == allow, - Type = case PubSub of - publish -> 'PUBLISH'; - subscribe -> 'SUBSCRIBE' - end, - Req = #{clientinfo => clientinfo(ClientInfo), - type => Type, - topic => Topic, - result => Bool - }, - case call_fold('client.authorize', Req, - fun merge_responsed_bool/2) of + Type = + case PubSub of + publish -> 'PUBLISH'; + subscribe -> 'SUBSCRIBE' + end, + Req = #{ + clientinfo => clientinfo(ClientInfo), + type => Type, + topic => Topic, + result => Bool + }, + case + call_fold( + 'client.authorize', + Req, + fun merge_responsed_bool/2 + ) + of {StopOrOk, #{result := Result0}} when is_boolean(Result0) -> - NResult = case Result0 of true -> allow; _ -> deny end, + NResult = + case Result0 of + true -> allow; + _ -> deny + end, {StopOrOk, NResult}; - _ -> {ok, Result} + _ -> + {ok, Result} end. on_client_subscribe(ClientInfo, Props, TopicFilters) -> - Req = #{clientinfo => clientinfo(ClientInfo), - props => properties(Props), - topic_filters => topicfilters(TopicFilters) - }, + Req = #{ + clientinfo => clientinfo(ClientInfo), + props => properties(Props), + topic_filters => topicfilters(TopicFilters) + }, cast('client.subscribe', Req). on_client_unsubscribe(ClientInfo, Props, TopicFilters) -> - Req = #{clientinfo => clientinfo(ClientInfo), - props => properties(Props), - topic_filters => topicfilters(TopicFilters) - }, + Req = #{ + clientinfo => clientinfo(ClientInfo), + props => properties(Props), + topic_filters => topicfilters(TopicFilters) + }, cast('client.unsubscribe', Req). %%-------------------------------------------------------------------- @@ -153,16 +187,18 @@ on_session_created(ClientInfo, _SessInfo) -> cast('session.created', Req). on_session_subscribed(ClientInfo, Topic, SubOpts) -> - Req = #{clientinfo => clientinfo(ClientInfo), - topic => Topic, - subopts => maps:with([qos, share, rh, rap, nl], SubOpts) - }, + Req = #{ + clientinfo => clientinfo(ClientInfo), + topic => Topic, + subopts => maps:with([qos, share, rh, rap, nl], SubOpts) + }, cast('session.subscribed', Req). on_session_unsubscribed(ClientInfo, Topic, _SubOpts) -> - Req = #{clientinfo => clientinfo(ClientInfo), - topic => Topic - }, + Req = #{ + clientinfo => clientinfo(ClientInfo), + topic => Topic + }, cast('session.unsubscribed', Req). on_session_resumed(ClientInfo, _SessInfo) -> @@ -178,8 +214,10 @@ on_session_takenover(ClientInfo, _SessInfo) -> cast('session.takenover', Req). on_session_terminated(ClientInfo, Reason, _SessInfo) -> - Req = #{clientinfo => clientinfo(ClientInfo), - reason => stringfy(Reason)}, + Req = #{ + clientinfo => clientinfo(ClientInfo), + reason => stringfy(Reason) + }, cast('session.terminated', Req). %%-------------------------------------------------------------------- @@ -190,110 +228,159 @@ on_message_publish(#message{topic = <<"$SYS/", _/binary>>}) -> ok; on_message_publish(Message) -> Req = #{message => message(Message)}, - case call_fold('message.publish', Req, - fun emqx_exhook_handler:merge_responsed_message/2) of + case + call_fold( + 'message.publish', + Req, + fun emqx_exhook_handler:merge_responsed_message/2 + ) + of {StopOrOk, #{message := NMessage}} -> {StopOrOk, assign_to_message(NMessage, Message)}; - _ -> {ok, Message} + _ -> + {ok, Message} end. on_message_dropped(#message{topic = <<"$SYS/", _/binary>>}, _By, _Reason) -> ok; on_message_dropped(Message, _By, Reason) -> - Req = #{message => message(Message), - reason => stringfy(Reason) - }, + Req = #{ + message => message(Message), + reason => stringfy(Reason) + }, cast('message.dropped', Req). on_message_delivered(_ClientInfo, #message{topic = <<"$SYS/", _/binary>>}) -> ok; on_message_delivered(ClientInfo, Message) -> - Req = #{clientinfo => clientinfo(ClientInfo), - message => message(Message) - }, + Req = #{ + clientinfo => clientinfo(ClientInfo), + message => message(Message) + }, cast('message.delivered', Req). on_message_acked(_ClientInfo, #message{topic = <<"$SYS/", _/binary>>}) -> ok; on_message_acked(ClientInfo, Message) -> - Req = #{clientinfo => clientinfo(ClientInfo), - message => message(Message) - }, + Req = #{ + clientinfo => clientinfo(ClientInfo), + message => message(Message) + }, cast('message.acked', Req). %%-------------------------------------------------------------------- %% Types -properties(undefined) -> []; +properties(undefined) -> + []; properties(M) when is_map(M) -> - maps:fold(fun(K, V, Acc) -> - [#{name => stringfy(K), - value => stringfy(V)} | Acc] - end, [], M). + maps:fold( + fun(K, V, Acc) -> + [ + #{ + name => stringfy(K), + value => stringfy(V) + } + | Acc + ] + end, + [], + M + ). -conninfo(ConnInfo = - #{clientid := ClientId, - peername := {Peerhost, _}, - sockname := {_, SockPort}}) -> +conninfo( + ConnInfo = + #{ + clientid := ClientId, + peername := {Peerhost, _}, + sockname := {_, SockPort} + } +) -> Username = maps:get(username, ConnInfo, undefined), ProtoName = maps:get(proto_name, ConnInfo, undefined), ProtoVer = maps:get(proto_ver, ConnInfo, undefined), Keepalive = maps:get(keepalive, ConnInfo, 0), - #{node => stringfy(node()), - clientid => ClientId, - username => maybe(Username), - peerhost => ntoa(Peerhost), - sockport => SockPort, - proto_name => ProtoName, - proto_ver => stringfy(ProtoVer), - keepalive => Keepalive}. + #{ + node => stringfy(node()), + clientid => ClientId, + username => maybe(Username), + peerhost => ntoa(Peerhost), + sockport => SockPort, + proto_name => ProtoName, + proto_ver => stringfy(ProtoVer), + keepalive => Keepalive + }. -clientinfo(ClientInfo = - #{clientid := ClientId, username := Username, peerhost := PeerHost, - sockport := SockPort, protocol := Protocol, mountpoint := Mountpoiont}) -> - #{node => stringfy(node()), - clientid => ClientId, - username => maybe(Username), - password => maybe(maps:get(password, ClientInfo, undefined)), - peerhost => ntoa(PeerHost), - sockport => SockPort, - protocol => stringfy(Protocol), - mountpoint => maybe(Mountpoiont), - is_superuser => maps:get(is_superuser, ClientInfo, false), - anonymous => maps:get(anonymous, ClientInfo, true), - cn => maybe(maps:get(cn, ClientInfo, undefined)), - dn => maybe(maps:get(dn, ClientInfo, undefined))}. +clientinfo( + ClientInfo = + #{ + clientid := ClientId, + username := Username, + peerhost := PeerHost, + sockport := SockPort, + protocol := Protocol, + mountpoint := Mountpoiont + } +) -> + #{ + node => stringfy(node()), + clientid => ClientId, + username => maybe(Username), + password => maybe(maps:get(password, ClientInfo, undefined)), + peerhost => ntoa(PeerHost), + sockport => SockPort, + protocol => stringfy(Protocol), + mountpoint => maybe(Mountpoiont), + is_superuser => maps:get(is_superuser, ClientInfo, false), + anonymous => maps:get(anonymous, ClientInfo, true), + cn => maybe(maps:get(cn, ClientInfo, undefined)), + dn => maybe(maps:get(dn, ClientInfo, undefined)) + }. -message(#message{id = Id, qos = Qos, from = From, topic = Topic, - payload = Payload, timestamp = Ts, headers = Headers}) -> - #{node => stringfy(node()), - id => emqx_guid:to_hexstr(Id), - qos => Qos, - from => stringfy(From), - topic => Topic, - payload => Payload, - timestamp => Ts, - headers => headers(Headers) - }. +message(#message{ + id = Id, + qos = Qos, + from = From, + topic = Topic, + payload = Payload, + timestamp = Ts, + headers = Headers +}) -> + #{ + node => stringfy(node()), + id => emqx_guid:to_hexstr(Id), + qos => Qos, + from => stringfy(From), + topic => Topic, + payload => Payload, + timestamp => Ts, + headers => headers(Headers) + }. headers(Headers) -> Ls = [username, protocol, peerhost, allow_publish], maps:fold( - fun - (_, undefined, Acc) -> - Acc; %% Ignore undefined value - (K, V, Acc) -> - case lists:member(K, Ls) of - true -> - Acc#{atom_to_binary(K) => bin(K, V)}; - _ -> - Acc - end - end, #{}, Headers). + fun + (_, undefined, Acc) -> + %% Ignore undefined value + Acc; + (K, V, Acc) -> + case lists:member(K, Ls) of + true -> + Acc#{atom_to_binary(K) => bin(K, V)}; + _ -> + Acc + end + end, + #{}, + Headers + ). -bin(K, V) when K == username; - K == protocol; - K == allow_publish -> +bin(K, V) when + K == username; + K == protocol; + K == allow_publish +-> bin(V); bin(peerhost, V) -> bin(inet:ntoa(V)). @@ -302,8 +389,14 @@ bin(V) when is_binary(V) -> V; bin(V) when is_atom(V) -> atom_to_binary(V); bin(V) when is_list(V) -> iolist_to_binary(V). -assign_to_message(InMessage = #{qos := Qos, topic := Topic, - payload := Payload}, Message) -> +assign_to_message( + InMessage = #{ + qos := Qos, + topic := Topic, + payload := Payload + }, + Message +) -> NMsg = Message#message{qos = Qos, topic = Topic, payload = Payload}, enrich_header(maps:get(headers, InMessage, #{}), NMsg). @@ -320,7 +413,7 @@ enrich_header(Headers, Message) -> topicfilters(Tfs) when is_list(Tfs) -> [#{name => Topic, qos => Qos} || {Topic, #{qos := Qos}} <- Tfs]. -ntoa({0,0,0,0,0,16#ffff,AB,CD}) -> +ntoa({0, 0, 0, 0, 0, 16#ffff, AB, CD}) -> list_to_binary(inet_parse:ntoa({AB bsr 8, AB rem 256, CD bsr 8, CD rem 256})); ntoa(IP) -> list_to_binary(inet_parse:ntoa(IP)). @@ -344,8 +437,9 @@ stringfy(Term) -> %% see exhook.proto merge_responsed_bool(_Req, #{type := 'IGNORE'}) -> ignore; -merge_responsed_bool(Req, #{type := Type, value := {bool_result, NewBool}}) - when is_boolean(NewBool) -> +merge_responsed_bool(Req, #{type := Type, value := {bool_result, NewBool}}) when + is_boolean(NewBool) +-> {ret(Type), Req#{result => NewBool}}; merge_responsed_bool(_Req, Resp) -> ?SLOG(warning, #{msg => "unknown_responsed_value", resp => Resp}), diff --git a/apps/emqx_exhook/src/emqx_exhook_metrics.erl b/apps/emqx_exhook/src/emqx_exhook_metrics.erl index 75dc04709..eccb35b84 100644 --- a/apps/emqx_exhook/src/emqx_exhook_metrics.erl +++ b/apps/emqx_exhook/src/emqx_exhook_metrics.erl @@ -19,33 +19,43 @@ -include("emqx_exhook.hrl"). %% API --export([ init/0, succeed/2, failed/2 - , update/1, new_metrics_info/0, servers_metrics/0 - , on_server_deleted/1, server_metrics/1, hooks_metrics/1 - , metrics_aggregate/1, metrics_aggregate_by_key/2 - , metrics_aggregate_by/2 - ]). +-export([ + init/0, + succeed/2, + failed/2, + update/1, + new_metrics_info/0, + servers_metrics/0, + on_server_deleted/1, + server_metrics/1, + hooks_metrics/1, + metrics_aggregate/1, + metrics_aggregate_by_key/2, + metrics_aggregate_by/2 +]). --record(metrics, { index :: index() - , succeed = 0 :: non_neg_integer() - , failed = 0 :: non_neg_integer() - , rate = 0 :: non_neg_integer() - , max_rate = 0 :: non_neg_integer() - , window_rate :: integer() - }). +-record(metrics, { + index :: index(), + succeed = 0 :: non_neg_integer(), + failed = 0 :: non_neg_integer(), + rate = 0 :: non_neg_integer(), + max_rate = 0 :: non_neg_integer(), + window_rate :: integer() +}). -type metrics() :: #metrics{}. -type server_name() :: emqx_exhook_mgr:server_name(). -type hookpoint() :: emqx_exhook_server:hookpoint(). -type index() :: {server_name(), hookpoint()}. --type hooks_metrics() :: #{hookpoint() => metrics_info()}. --type servers_metrics() :: #{server_name() => metrics_info()}. +-type hooks_metrics() :: #{hookpoint() => metrics_info()}. +-type servers_metrics() :: #{server_name() => metrics_info()}. --type metrics_info() :: #{ succeed := non_neg_integer() - , failed := non_neg_integer() - , rate := number() - , max_rate := number() - }. +-type metrics_info() :: #{ + succeed := non_neg_integer(), + failed := non_neg_integer(), + rate := number(), + max_rate := number() +}. -define(INDEX(ServerName, HookPoint), {ServerName, HookPoint}). -export_type([metrics_info/0, servers_metrics/0, hooks_metrics/0]). @@ -54,77 +64,113 @@ %%% API %%-------------------------------------------------------------------- init() -> - _ = ets:new(?HOOKS_METRICS, - [ set, named_table, public - , {keypos, #metrics.index}, {write_concurrency, true} - , {read_concurrency, true} - ]), + _ = ets:new( + ?HOOKS_METRICS, + [ + set, + named_table, + public, + {keypos, #metrics.index}, + {write_concurrency, true}, + {read_concurrency, true} + ] + ), ok. -spec new_metric_info() -> metrics_info(). new_metric_info() -> - #{succeed => 0, - failed => 0, - rate => 0, - max_rate => 0 - }. + #{ + succeed => 0, + failed => 0, + rate => 0, + max_rate => 0 + }. -spec succeed(server_name(), hookpoint()) -> ok. succeed(Server, Hook) -> - inc(Server, Hook, #metrics.succeed, - #metrics{ index = {Server, Hook} - , window_rate = 0 - , succeed = 0 - }). + inc( + Server, + Hook, + #metrics.succeed, + #metrics{ + index = {Server, Hook}, + window_rate = 0, + succeed = 0 + } + ). -spec failed(server_name(), hookpoint()) -> ok. failed(Server, Hook) -> - inc(Server, Hook, #metrics.failed, - #metrics{ index = {Server, Hook} - , window_rate = 0 - , failed = 0 - }). + inc( + Server, + Hook, + #metrics.failed, + #metrics{ + index = {Server, Hook}, + window_rate = 0, + failed = 0 + } + ). -spec update(pos_integer()) -> true. update(Interval) -> - Fun = fun(#metrics{rate = Rate, - window_rate = WindowRate, - max_rate = MaxRate} = Metrics, - _) -> - case calc_metric(WindowRate, Interval) of - Rate -> true; - NewRate -> - MaxRate2 = erlang:max(MaxRate, NewRate), - Metrics2 = Metrics#metrics{rate = NewRate, - window_rate = 0, - max_rate = MaxRate2}, - ets:insert(?HOOKS_METRICS, Metrics2) - end - end, + Fun = fun( + #metrics{ + rate = Rate, + window_rate = WindowRate, + max_rate = MaxRate + } = Metrics, + _ + ) -> + case calc_metric(WindowRate, Interval) of + Rate -> + true; + NewRate -> + MaxRate2 = erlang:max(MaxRate, NewRate), + Metrics2 = Metrics#metrics{ + rate = NewRate, + window_rate = 0, + max_rate = MaxRate2 + }, + ets:insert(?HOOKS_METRICS, Metrics2) + end + end, ets:foldl(Fun, true, ?HOOKS_METRICS). -spec on_server_deleted(server_name()) -> true. on_server_deleted(Name) -> - ets:match_delete(?HOOKS_METRICS, - {metrics, {Name, '_'}, '_', '_', '_', '_', '_'}). + ets:match_delete( + ?HOOKS_METRICS, + {metrics, {Name, '_'}, '_', '_', '_', '_', '_'} + ). -spec server_metrics(server_name()) -> metrics_info(). server_metrics(SvrName) -> - Hooks = ets:match_object(?HOOKS_METRICS, - {metrics, {SvrName, '_'}, '_', '_', '_', '_', '_'}), + Hooks = ets:match_object( + ?HOOKS_METRICS, + {metrics, {SvrName, '_'}, '_', '_', '_', '_', '_'} + ), - Fold = fun(#metrics{succeed = Succeed, - failed = Failed, - rate = Rate, - max_rate = MaxRate}, - Acc) -> - [#{ succeed => Succeed - , failed => Failed - , rate => Rate - , max_rate => MaxRate - } | Acc] - end, + Fold = fun( + #metrics{ + succeed = Succeed, + failed = Failed, + rate = Rate, + max_rate = MaxRate + }, + Acc + ) -> + [ + #{ + succeed => Succeed, + failed => Failed, + rate => Rate, + max_rate => MaxRate + } + | Acc + ] + end, AllMetrics = lists:foldl(Fold, [], Hooks), metrics_aggregate(AllMetrics). @@ -133,21 +179,25 @@ server_metrics(SvrName) -> servers_metrics() -> AllMetrics = ets:tab2list(?HOOKS_METRICS), - GroupFun = fun(#metrics{index = ?INDEX(ServerName, _), - succeed = Succeed, - failed = Failed, - rate = Rate, - max_rate = MaxRate - }, - Acc) -> - SvrGroup = maps:get(ServerName, Acc, []), - Metrics = #{ succeed => Succeed - , failed => Failed - , rate => Rate - , max_rate => MaxRate - }, - Acc#{ServerName => [Metrics | SvrGroup]} - end, + GroupFun = fun( + #metrics{ + index = ?INDEX(ServerName, _), + succeed = Succeed, + failed = Failed, + rate = Rate, + max_rate = MaxRate + }, + Acc + ) -> + SvrGroup = maps:get(ServerName, Acc, []), + Metrics = #{ + succeed => Succeed, + failed => Failed, + rate => Rate, + max_rate => MaxRate + }, + Acc#{ServerName => [Metrics | SvrGroup]} + end, GroupBySever = lists:foldl(GroupFun, #{}, AllMetrics), @@ -156,21 +206,30 @@ servers_metrics() -> -spec hooks_metrics(server_name()) -> hooks_metrics(). hooks_metrics(SvrName) -> - Hooks = ets:match_object(?HOOKS_METRICS, - {metrics, {SvrName, '_'}, '_', '_', '_', '_', '_'}), + Hooks = ets:match_object( + ?HOOKS_METRICS, + {metrics, {SvrName, '_'}, '_', '_', '_', '_', '_'} + ), - Fold = fun(#metrics{index = ?INDEX(_, HookPoint), - succeed = Succeed, - failed = Failed, - rate = Rate, - max_rate = MaxRate}, - Acc) -> - Acc#{HookPoint => #{ succeed => Succeed - , failed => Failed - , rate => Rate - , max_rate => MaxRate - }} - end, + Fold = fun( + #metrics{ + index = ?INDEX(_, HookPoint), + succeed = Succeed, + failed = Failed, + rate = Rate, + max_rate = MaxRate + }, + Acc + ) -> + Acc#{ + HookPoint => #{ + succeed => Succeed, + failed => Failed, + rate => Rate, + max_rate => MaxRate + } + } + end, lists:foldl(Fold, #{}, Hooks). @@ -178,12 +237,14 @@ hooks_metrics(SvrName) -> metrics_aggregate(MetricsL) -> metrics_aggregate_by(fun(X) -> X end, MetricsL). --spec metrics_aggregate_by_key(Key, list(HasMetrics)) -> metrics_info() - when Key :: any(), - HasMetrics :: #{Key => metrics_info()}. +-spec metrics_aggregate_by_key(Key, list(HasMetrics)) -> metrics_info() when + Key :: any(), + HasMetrics :: #{Key => metrics_info()}. metrics_aggregate_by_key(Key, MetricsL) -> - metrics_aggregate_by(fun(X) -> maps:get(Key, X, new_metrics_info()) end, - MetricsL). + metrics_aggregate_by( + fun(X) -> maps:get(Key, X, new_metrics_info()) end, + MetricsL + ). %%-------------------------------------------------------------------- %%% Internal functions @@ -191,19 +252,22 @@ metrics_aggregate_by_key(Key, MetricsL) -> -spec inc(server_name(), hookpoint(), pos_integer(), metrics()) -> ok. inc(Server, Hook, Pos, Default) -> Index = {Server, Hook}, - _ = ets:update_counter(?HOOKS_METRICS, - Index, - [{#metrics.window_rate, 1}, {Pos, 1}], - Default), + _ = ets:update_counter( + ?HOOKS_METRICS, + Index, + [{#metrics.window_rate, 1}, {Pos, 1}], + Default + ), ok. -spec new_metrics_info() -> metrics_info(). new_metrics_info() -> - #{ succeed => 0 - , failed => 0 - , rate => 0 - , max_rate => 0 - }. + #{ + succeed => 0, + failed => 0, + rate => 0, + max_rate => 0 + }. -spec calc_metric(non_neg_integer(), non_neg_integer()) -> non_neg_integer(). calc_metric(Val, Interval) -> @@ -211,26 +275,31 @@ calc_metric(Val, Interval) -> erlang:ceil(Val * 1000 / Interval). -spec metrics_add(metrics_info(), metrics_info()) -> metrics_info(). -metrics_add(#{succeed := S1, failed := F1, rate := R1, max_rate := M1} - , #{succeed := S2, failed := F2, rate := R2, max_rate := M2} = Acc) -> - Acc#{ succeed := S1 + S2 - , failed := F1 + F2 - , rate := R1 + R2 - , max_rate := M1 + M2 - }. +metrics_add( + #{succeed := S1, failed := F1, rate := R1, max_rate := M1}, + #{succeed := S2, failed := F2, rate := R2, max_rate := M2} = Acc +) -> + Acc#{ + succeed := S1 + S2, + failed := F1 + F2, + rate := R1 + R2, + max_rate := M1 + M2 + }. --spec metrics_aggregate_by(fun((X) -> metrics_info()), list(X)) -> metrics_info() - when X :: any(). +-spec metrics_aggregate_by(fun((X) -> metrics_info()), list(X)) -> metrics_info() when + X :: any(). metrics_aggregate_by(_, []) -> new_metric_info(); - metrics_aggregate_by(Fun, MetricsL) -> Fold = fun(E, Acc) -> metrics_add(Fun(E), Acc) end, - #{rate := Rate, - max_rate := MaxRate} = Result = lists:foldl(Fold, new_metric_info(), MetricsL), + #{ + rate := Rate, + max_rate := MaxRate + } = Result = lists:foldl(Fold, new_metric_info(), MetricsL), Len = erlang:length(MetricsL), - Result#{rate := Rate div Len, - max_rate := MaxRate div Len - }. + Result#{ + rate := Rate div Len, + max_rate := MaxRate div Len + }. diff --git a/apps/emqx_exhook/src/emqx_exhook_mgr.erl b/apps/emqx_exhook/src/emqx_exhook_mgr.erl index e34a8c046..f11b91ae4 100644 --- a/apps/emqx_exhook/src/emqx_exhook_mgr.erl +++ b/apps/emqx_exhook/src/emqx_exhook_mgr.erl @@ -26,66 +26,73 @@ -export([start_link/0]). %% Mgmt API --export([ list/0 - , lookup/1 - , enable/1 - , disable/1 - , server_info/1 - , all_servers_info/0 - , server_hooks_metrics/1 - ]). +-export([ + list/0, + lookup/1, + enable/1, + disable/1, + server_info/1, + all_servers_info/0, + server_hooks_metrics/1 +]). %% Helper funcs --export([ running/0 - , server/1 - , hooks/1 - , init_ref_counter_table/0 - ]). +-export([ + running/0, + server/1, + hooks/1, + init_ref_counter_table/0 +]). --export([ update_config/2 - , pre_config_update/3 - , post_config_update/5 - ]). +-export([ + update_config/2, + pre_config_update/3, + post_config_update/5 +]). %% gen_server callbacks --export([ init/1 - , handle_call/3 - , handle_cast/2 - , handle_info/2 - , terminate/2 - , code_change/3 - ]). +-export([ + init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3 +]). -export([roots/0]). --type state() :: #{%% Running servers - running := servers(), - %% Wait to reload servers - waiting := servers(), - %% Marked stopped servers - stopped := servers(), - %% Timer references - trefs := map(), - orders := orders() - }. +%% Running servers +-type state() :: #{ + running := servers(), + %% Wait to reload servers + waiting := servers(), + %% Marked stopped servers + stopped := servers(), + %% Timer references + trefs := map(), + orders := orders() +}. -type server_name() :: binary(). -type servers() :: #{server_name() => server()}. -type server() :: server_options(). -type server_options() :: map(). --type position() :: front - | rear - | {before, binary()} - | {'after', binary()}. +-type position() :: + front + | rear + | {before, binary()} + | {'after', binary()}. -type orders() :: #{server_name() => integer()}. --type server_info() :: #{name := server_name(), - status := running | waiting | stopped, +-type server_info() :: #{ + name := server_name(), + status := running | waiting | stopped, - atom() => term() - }. + atom() => term() +}. -define(DEFAULT_TIMEOUT, 60000). -define(REFRESH_INTERVAL, timer:seconds(5)). @@ -96,9 +103,10 @@ %% APIs %%-------------------------------------------------------------------- --spec start_link() -> ignore - | {ok, pid()} - | {error, any()}. +-spec start_link() -> + ignore + | {ok, pid()} + | {error, any()}. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). @@ -137,7 +145,7 @@ roots() -> update_config(KeyPath, UpdateReq) -> case emqx_conf:update(KeyPath, UpdateReq, #{override_to => cluster}) of - {ok, UpdateResult} -> + {ok, UpdateResult} -> #{post_config_update := #{?MODULE := Result}} = UpdateResult, {ok, Result}; Error -> @@ -146,26 +154,30 @@ update_config(KeyPath, UpdateReq) -> pre_config_update(_, {add, Conf}, OldConf) -> {ok, OldConf ++ [Conf]}; - pre_config_update(_, {update, Name, Conf}, OldConf) -> case replace_conf(Name, fun(_) -> Conf end, OldConf) of not_found -> {error, not_found}; NewConf -> {ok, NewConf} end; - pre_config_update(_, {delete, ToDelete}, OldConf) -> - {ok, lists:dropwhile(fun(#{<<"name">> := Name}) -> Name =:= ToDelete end, - OldConf)}; - + {ok, + lists:dropwhile( + fun(#{<<"name">> := Name}) -> Name =:= ToDelete end, + OldConf + )}; pre_config_update(_, {move, Name, Position}, OldConf) -> case do_move(Name, Position, OldConf) of not_found -> {error, not_found}; NewConf -> {ok, NewConf} end; - pre_config_update(_, {enable, Name, Enable}, OldConf) -> - case replace_conf(Name, - fun(Conf) -> Conf#{<<"enable">> => Enable} end, OldConf) of + case + replace_conf( + Name, + fun(Conf) -> Conf#{<<"enable">> => Enable} end, + OldConf + ) + of not_found -> {error, not_found}; NewConf -> {ok, NewConf} end. @@ -186,13 +198,16 @@ init([]) -> {Waiting, Running, Stopped} = load_all_servers(ServerL), Orders = reorder(ServerL), refresh_tick(), - {ok, ensure_reload_timer( - #{waiting => Waiting, - running => Running, - stopped => Stopped, - trefs => #{}, - orders => Orders - })}. + {ok, + ensure_reload_timer( + #{ + waiting => Waiting, + running => Running, + stopped => Stopped, + trefs => #{}, + orders => Orders + } + )}. -spec load_all_servers(list(server_options())) -> {servers(), servers(), servers()}. load_all_servers(ServerL) -> @@ -208,15 +223,19 @@ load_all_servers([#{name := Name} = Options | More], Waiting, Running, Stopped) disable -> load_all_servers(More, Waiting, Running, Stopped#{Name => Options}) end; - load_all_servers([], Waiting, Running, Stopped) -> {Waiting, Running, Stopped}. -handle_call(list, _From, State = #{running := Running, - waiting := Waiting, - stopped := Stopped, - orders := Orders}) -> - +handle_call( + list, + _From, + State = #{ + running := Running, + waiting := Waiting, + stopped := Stopped, + orders := Orders + } +) -> R = get_servers_info(running, Running), W = get_servers_info(waiting, Waiting), S = get_servers_info(stopped, Stopped), @@ -225,29 +244,33 @@ handle_call(list, _From, State = #{running := Running, OrderServers = sort_name_by_order(Servers, Orders), {reply, OrderServers, State}; - -handle_call({update_config, {move, _Name, _Position}, NewConfL}, - _From, - State) -> +handle_call( + {update_config, {move, _Name, _Position}, NewConfL}, + _From, + State +) -> Orders = reorder(NewConfL), {reply, ok, State#{orders := Orders}}; - handle_call({update_config, {delete, ToDelete}, _}, _From, State) -> - {ok, #{orders := Orders, - stopped := Stopped - } = State2} = do_unload_server(ToDelete, State), + {ok, + #{ + orders := Orders, + stopped := Stopped + } = State2} = do_unload_server(ToDelete, State), - State3 = State2#{stopped := maps:remove(ToDelete, Stopped), - orders := maps:remove(ToDelete, Orders) - }, + State3 = State2#{ + stopped := maps:remove(ToDelete, Stopped), + orders := maps:remove(ToDelete, Orders) + }, emqx_exhook_metrics:on_server_deleted(ToDelete), {reply, ok, State3}; - -handle_call({update_config, {add, RawConf}, NewConfL}, - _From, - #{running := Running, waiting := Waitting, stopped := Stopped} = State) -> +handle_call( + {update_config, {add, RawConf}, NewConfL}, + _From, + #{running := Running, waiting := Waitting, stopped := Stopped} = State +) -> {_, #{name := Name} = Conf} = emqx_config:check_config(?MODULE, RawConf), case emqx_exhook_server:load(Name, Conf) of @@ -262,7 +285,6 @@ handle_call({update_config, {add, RawConf}, NewConfL}, end, Orders = reorder(NewConfL), {reply, ok, State2#{orders := Orders}}; - handle_call({lookup, Name}, _From, State) -> case where_is_server(Name, State) of not_found -> @@ -271,51 +293,57 @@ handle_call({lookup, Name}, _From, State) -> Result = maps:merge(Conf, #{status => Where}) end, {reply, Result, State}; - handle_call({update_config, {update, Name, _Conf}, NewConfL}, _From, State) -> {Result, State2} = restart_server(Name, NewConfL, State), {reply, Result, State2}; - handle_call({update_config, {enable, Name, _Enable}, NewConfL}, _From, State) -> {Result, State2} = restart_server(Name, NewConfL, State), {reply, Result, State2}; - handle_call({server_info, Name}, _From, State) -> case where_is_server(Name, State) of not_found -> Result = not_found; {Status, _} -> HooksMetrics = emqx_exhook_metrics:server_metrics(Name), - Result = #{ status => Status - , metrics => HooksMetrics - } + Result = #{ + status => Status, + metrics => HooksMetrics + } end, {reply, Result, State}; - -handle_call(all_servers_info, _From, #{running := Running, - waiting := Waiting, - stopped := Stopped} = State) -> +handle_call( + all_servers_info, + _From, + #{ + running := Running, + waiting := Waiting, + stopped := Stopped + } = State +) -> MakeStatus = fun(Status, Servers, Acc) -> - lists:foldl(fun(Name, IAcc) -> IAcc#{Name => Status} end, - Acc, - maps:keys(Servers)) - end, - Status = lists:foldl(fun({Status, Servers}, Acc) -> MakeStatus(Status, Servers, Acc) end, - #{}, - [{running, Running}, {waiting, Waiting}, {stopped, Stopped}]), + lists:foldl( + fun(Name, IAcc) -> IAcc#{Name => Status} end, + Acc, + maps:keys(Servers) + ) + end, + Status = lists:foldl( + fun({Status, Servers}, Acc) -> MakeStatus(Status, Servers, Acc) end, + #{}, + [{running, Running}, {waiting, Waiting}, {stopped, Stopped}] + ), Metrics = emqx_exhook_metrics:servers_metrics(), - Result = #{ status => Status - , metrics => Metrics - }, + Result = #{ + status => Status, + metrics => Metrics + }, {reply, Result, State}; - handle_call({server_hooks_metrics, Name}, _From, State) -> Result = emqx_exhook_metrics:hooks_metrics(Name), {reply, Result, State}; - handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. @@ -331,26 +359,32 @@ handle_info({timeout, _Ref, {reload, Name}}, State) -> {error, not_found} -> {noreply, NState}; {error, Reason} -> - ?SLOG(warning, - #{msg => "failed_to_reload_exhook_callback_server", + ?SLOG( + warning, + #{ + msg => "failed_to_reload_exhook_callback_server", reason => Reason, - name => Name}), + name => Name + } + ), {noreply, ensure_reload_timer(NState)} end; - handle_info(refresh_tick, State) -> refresh_tick(), emqx_exhook_metrics:update(?REFRESH_INTERVAL), {noreply, State}; - handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, State = #{running := Running}) -> - _ = maps:fold(fun(Name, _, AccIn) -> - {ok, NAccIn} = do_unload_server(Name, AccIn), - NAccIn - end, State, Running), + _ = maps:fold( + fun(Name, _, AccIn) -> + {ok, NAccIn} = do_unload_server(Name, AccIn), + NAccIn + end, + State, + Running + ), _ = unload_exhooks(), ok. @@ -362,12 +396,15 @@ code_change(_OldVsn, State, _Extra) -> %%-------------------------------------------------------------------- unload_exhooks() -> - [emqx:unhook(Name, {M, F}) || - {Name, {M, F, _A}} <- ?ENABLED_HOOKS]. + [ + emqx:unhook(Name, {M, F}) + || {Name, {M, F, _A}} <- ?ENABLED_HOOKS + ]. --spec do_load_server(server_name(), state()) -> {{error, not_found}, state()} - | {{error, already_started}, state()} - | {ok, state()}. +-spec do_load_server(server_name(), state()) -> + {{error, not_found}, state()} + | {{error, already_started}, state()} + | {ok, state()}. do_load_server(Name, State = #{orders := Orders}) -> case where_is_server(Name, State) of not_found -> @@ -378,14 +415,18 @@ do_load_server(Name, State = #{orders := Orders}) -> State2 = clean_reload_timer(Name, State), {Options, Map2} = maps:take(Name, Map), State3 = State2#{Where := Map2}, - #{running := Running, - stopped := Stopped} = State3, + #{ + running := Running, + stopped := Stopped + } = State3, case emqx_exhook_server:load(Name, Options) of - {ok, ServerState} -> - save(Name, ServerState), - update_order(Orders), - ?SLOG(info, #{msg => "load_exhook_callback_server_ok", - name => Name}), + {ok, ServerState} -> + save(Name, ServerState), + update_order(Orders), + ?SLOG(info, #{ + msg => "load_exhook_callback_server_ok", + name => Name + }), {ok, State3#{running := maps:put(Name, Options, Running)}}; {error, Reason} -> {{error, Reason}, State}; @@ -397,45 +438,58 @@ do_load_server(Name, State = #{orders := Orders}) -> -spec do_unload_server(server_name(), state()) -> {ok, state()}. do_unload_server(Name, #{stopped := Stopped} = State) -> case where_is_server(Name, State) of - {stopped, _} -> {ok, State}; + {stopped, _} -> + {ok, State}; {waiting, Waiting} -> {Options, Waiting2} = maps:take(Name, Waiting), - {ok, clean_reload_timer(Name, - State#{waiting := Waiting2, - stopped := maps:put(Name, Options, Stopped) - } - )}; + {ok, + clean_reload_timer( + Name, + State#{ + waiting := Waiting2, + stopped := maps:put(Name, Options, Stopped) + } + )}; {running, Running} -> Service = server(Name), ok = unsave(Name), ok = emqx_exhook_server:unload(Service), {Options, Running2} = maps:take(Name, Running), - {ok, State#{running := Running2, - stopped := maps:put(Name, Options, Stopped) - }}; - not_found -> {ok, State} + {ok, State#{ + running := Running2, + stopped := maps:put(Name, Options, Stopped) + }}; + not_found -> + {ok, State} end. -spec ensure_reload_timer(state()) -> state(). -ensure_reload_timer(State = #{waiting := Waiting, - stopped := Stopped, - trefs := TRefs}) -> +ensure_reload_timer( + State = #{ + waiting := Waiting, + stopped := Stopped, + trefs := TRefs + } +) -> Iter = maps:iterator(Waiting), {Waitting2, Stopped2, TRefs2} = ensure_reload_timer(maps:next(Iter), Waiting, Stopped, TRefs), - State#{waiting := Waitting2, - stopped := Stopped2, - trefs := TRefs2}. + State#{ + waiting := Waitting2, + stopped := Stopped2, + trefs := TRefs2 + }. ensure_reload_timer(none, Waiting, Stopped, TimerRef) -> {Waiting, Stopped, TimerRef}; - -ensure_reload_timer({Name, #{auto_reconnect := Intv}, Iter}, - Waiting, - Stopped, - TimerRef) when is_integer(Intv) -> +ensure_reload_timer( + {Name, #{auto_reconnect := Intv}, Iter}, + Waiting, + Stopped, + TimerRef +) when is_integer(Intv) -> Next = maps:next(Iter), case maps:is_key(Name, TimerRef) of true -> @@ -445,54 +499,49 @@ ensure_reload_timer({Name, #{auto_reconnect := Intv}, Iter}, TimerRef2 = maps:put(Name, Ref, TimerRef), ensure_reload_timer(Next, Waiting, Stopped, TimerRef2) end; - ensure_reload_timer({Name, Opts, Iter}, Waiting, Stopped, TimerRef) -> - ensure_reload_timer(maps:next(Iter), - maps:remove(Name, Waiting), - maps:put(Name, Opts, Stopped), - TimerRef). + ensure_reload_timer( + maps:next(Iter), + maps:remove(Name, Waiting), + maps:put(Name, Opts, Stopped), + TimerRef + ). -spec clean_reload_timer(server_name(), state()) -> state(). clean_reload_timer(Name, State = #{trefs := TRefs}) -> case maps:take(Name, TRefs) of - error -> State; + error -> + State; {TRef, NTRefs} -> _ = erlang:cancel_timer(TRef), State#{trefs := NTRefs} end. -spec do_move(binary(), position(), list(server_options())) -> - not_found | list(server_options()). + not_found | list(server_options()). do_move(Name, Position, ConfL) -> move(ConfL, Name, Position, []). move([#{<<"name">> := Name} = Server | T], Name, Position, HeadL) -> move_to(Position, Server, lists:reverse(HeadL) ++ T); - move([Server | T], Name, Position, HeadL) -> move(T, Name, Position, [Server | HeadL]); - move([], _Name, _Position, _HeadL) -> not_found. move_to(?CMD_MOVE_FRONT, Server, ServerL) -> [Server | ServerL]; - move_to(?CMD_MOVE_REAR, Server, ServerL) -> ServerL ++ [Server]; - move_to(Position, Server, ServerL) -> move_to(ServerL, Position, Server, []). move_to([#{<<"name">> := Name} | _] = T, ?CMD_MOVE_BEFORE(Name), Server, HeadL) -> lists:reverse(HeadL) ++ [Server | T]; - move_to([#{<<"name">> := Name} = H | T], ?CMD_MOVE_AFTER(Name), Server, HeadL) -> lists:reverse(HeadL) ++ [H, Server | T]; - move_to([H | T], Position, Server, HeadL) -> move_to(T, Position, Server, [H | HeadL]); - move_to([], _Position, _Server, _HeadL) -> not_found. @@ -504,48 +553,49 @@ reorder(ServerL) -> reorder([#{name := Name} | T], Order, Orders) -> reorder(T, Order + 1, Orders#{Name => Order}); - reorder([], _Order, Orders) -> Orders. get_servers_info(Status, Map) -> Fold = fun(Name, Conf, Acc) -> - [maps:merge(Conf, #{status => Status, - hooks => hooks(Name)}) | Acc] - end, + [ + maps:merge(Conf, #{ + status => Status, + hooks => hooks(Name) + }) + | Acc + ] + end, maps:fold(Fold, [], Map). where_is_server(Name, #{running := Running}) when is_map_key(Name, Running) -> {running, Running}; - where_is_server(Name, #{waiting := Waiting}) when is_map_key(Name, Waiting) -> {waiting, Waiting}; - where_is_server(Name, #{stopped := Stopped}) when is_map_key(Name, Stopped) -> {stopped, Stopped}; - where_is_server(_, _) -> not_found. -type replace_fun() :: fun((server_options()) -> server_options()). --spec replace_conf(binary(), replace_fun(), list(server_options())) -> not_found - | list(server_options()). +-spec replace_conf(binary(), replace_fun(), list(server_options())) -> + not_found + | list(server_options()). replace_conf(Name, ReplaceFun, ConfL) -> replace_conf(ConfL, Name, ReplaceFun, []). replace_conf([#{<<"name">> := Name} = H | T], Name, ReplaceFun, HeadL) -> New = ReplaceFun(H), lists:reverse(HeadL) ++ [New | T]; - replace_conf([H | T], Name, ReplaceFun, HeadL) -> replace_conf(T, Name, ReplaceFun, [H | HeadL]); - replace_conf([], _, _, _) -> not_found. --spec restart_server(binary(), list(server_options()), state()) -> {ok, state()} - | {{error, term()}, state()}. +-spec restart_server(binary(), list(server_options()), state()) -> + {ok, state()} + | {{error, term()}, state()}. restart_server(Name, ConfL, State) -> case lists:search(fun(#{name := CName}) -> CName =:= Name end, ConfL) of false -> @@ -567,12 +617,15 @@ restart_server(Name, ConfL, State) -> end. sort_name_by_order(Names, Orders) -> - lists:sort(fun(A, B) when is_binary(A) -> - maps:get(A, Orders) < maps:get(B, Orders); - (#{name := A}, #{name := B}) -> - maps:get(A, Orders) < maps:get(B, Orders) - end, - Names). + lists:sort( + fun + (A, B) when is_binary(A) -> + maps:get(A, Orders) < maps:get(B, Orders); + (#{name := A}, #{name := B}) -> + maps:get(A, Orders) < maps:get(B, Orders) + end, + Names + ). refresh_tick() -> erlang:send_after(?REFRESH_INTERVAL, self(), ?FUNCTION_NAME). diff --git a/apps/emqx_exhook/src/emqx_exhook_schema.erl b/apps/emqx_exhook/src/emqx_exhook_schema.erl index 6cafaf98c..06a98e920 100644 --- a/apps/emqx_exhook/src/emqx_exhook_schema.erl +++ b/apps/emqx_exhook/src/emqx_exhook_schema.erl @@ -39,46 +39,67 @@ namespace() -> exhook. roots() -> [exhook]. fields(exhook) -> - [{servers, - sc(hoconsc:array(ref(server)), - #{ default => [] - , desc => "List of exhook servers." - })} + [ + {servers, + sc( + hoconsc:array(ref(server)), + #{ + default => [], + desc => "List of exhook servers." + } + )} ]; - fields(server) -> - [ {name, sc(binary(), - #{ desc => "Name of the exhook server." - })} - , {enable, sc(boolean(), - #{ default => true - , desc => "Enable the exhook server." - })} - , {url, sc(binary(), - #{ desc => "URL of the gRPC server." - })} - , {request_timeout, sc(duration(), - #{ default => "5s" - , desc => "The timeout to request gRPC server." - })} - , {failed_action, failed_action()} - , {ssl, - sc(ref(ssl_conf), #{})} - , {auto_reconnect, - sc(hoconsc:union([false, duration()]), - #{ default => "60s" - , desc => "Whether to automatically reconnect (initialize) the gRPC server.
" - "When gRPC is not available, exhook tries to request the gRPC service at " - "that interval and reinitialize the list of mounted hooks." - })} - , {pool_size, - sc(integer(), - #{ default => 8 - , example => 8 - , desc => "The process pool size for gRPC client." - })} + [ + {name, + sc( + binary(), + #{desc => "Name of the exhook server."} + )}, + {enable, + sc( + boolean(), + #{ + default => true, + desc => "Enable the exhook server." + } + )}, + {url, + sc( + binary(), + #{desc => "URL of the gRPC server."} + )}, + {request_timeout, + sc( + duration(), + #{ + default => "5s", + desc => "The timeout to request gRPC server." + } + )}, + {failed_action, failed_action()}, + {ssl, sc(ref(ssl_conf), #{})}, + {auto_reconnect, + sc( + hoconsc:union([false, duration()]), + #{ + default => "60s", + desc => + "Whether to automatically reconnect (initialize) the gRPC server.
" + "When gRPC is not available, exhook tries to request the gRPC service at " + "that interval and reinitialize the list of mounted hooks." + } + )}, + {pool_size, + sc( + integer(), + #{ + default => 8, + example => 8, + desc => "The process pool size for gRPC client." + } + )} ]; - fields(ssl_conf) -> Schema = emqx_schema:client_ssl_opts_schema(#{}), lists:keydelete(user_lookup_fun, 1, Schema). @@ -99,11 +120,15 @@ ref(Field) -> hoconsc:ref(?MODULE, Field). failed_action() -> - sc(hoconsc:enum([deny, ignore]), - #{ default => deny - , desc => "The value that is returned when the request " - "to the gRPC server fails for any reason." - }). + sc( + hoconsc:enum([deny, ignore]), + #{ + default => deny, + desc => + "The value that is returned when the request " + "to the gRPC server fails for any reason." + } + ). server_config() -> fields(server). diff --git a/apps/emqx_exhook/src/emqx_exhook_server.erl b/apps/emqx_exhook/src/emqx_exhook_server.erl index 60c6f7ca9..b804d1478 100644 --- a/apps/emqx_exhook/src/emqx_exhook_server.erl +++ b/apps/emqx_exhook/src/emqx_exhook_server.erl @@ -22,56 +22,59 @@ -define(PB_CLIENT_MOD, emqx_exhook_v_1_hook_provider_client). %% Load/Unload --export([ load/2 - , unload/1 - ]). +-export([ + load/2, + unload/1 +]). %% APIs -export([call/3]). %% Infos --export([ name/1 - , hooks/1 - , format/1 - , failed_action/1 - ]). +-export([ + name/1, + hooks/1, + format/1, + failed_action/1 +]). -ifdef(TEST). -export([hk2func/1]). -endif. --type server() :: #{%% Server name (equal to grpc client channel name) - name := binary(), - %% The function options - options := map(), - %% gRPC channel pid - channel := pid(), - %% Registered hook names and options - hookspec := #{hookpoint() => map()}, - %% Metrcis name prefix - prefix := list() - }. +%% Server name (equal to grpc client channel name) +-type server() :: #{ + name := binary(), + %% The function options + options := map(), + %% gRPC channel pid + channel := pid(), + %% Registered hook names and options + hookspec := #{hookpoint() => map()}, + %% Metrcis name prefix + prefix := list() +}. - --type hookpoint() :: 'client.connect' - | 'client.connack' - | 'client.connected' - | 'client.disconnected' - | 'client.authenticate' - | 'client.authorize' - | 'client.subscribe' - | 'client.unsubscribe' - | 'session.created' - | 'session.subscribed' - | 'session.unsubscribed' - | 'session.resumed' - | 'session.discarded' - | 'session.takenover' - | 'session.terminated' - | 'message.publish' - | 'message.delivered' - | 'message.acked' - | 'message.dropped'. +-type hookpoint() :: + 'client.connect' + | 'client.connack' + | 'client.connected' + | 'client.disconnected' + | 'client.authenticate' + | 'client.authorize' + | 'client.subscribe' + | 'client.unsubscribe' + | 'session.created' + | 'session.subscribed' + | 'session.unsubscribed' + | 'session.resumed' + | 'session.discarded' + | 'session.takenover' + | 'session.terminated' + | 'message.publish' + | 'message.delivered' + | 'message.acked' + | 'message.dropped'. -export_type([server/0, hookpoint/0]). @@ -86,14 +89,16 @@ -spec load(binary(), map()) -> {ok, server()} | {error, term()} | disable. load(_Name, #{enable := false}) -> disable; - load(Name, #{request_timeout := Timeout, failed_action := FailedAction} = Opts) -> ReqOpts = #{timeout => Timeout, failed_action => FailedAction}, {SvrAddr, ClientOpts} = channel_opts(Opts), - case emqx_exhook_sup:start_grpc_client_channel( - Name, - SvrAddr, - ClientOpts) of + case + emqx_exhook_sup:start_grpc_client_channel( + Name, + SvrAddr, + ClientOpts + ) + of {ok, _ChannPoolPid} -> case do_init(Name, ReqOpts) of {ok, HookSpecs} -> @@ -102,42 +107,53 @@ load(Name, #{request_timeout := Timeout, failed_action := FailedAction} = Opts) ensure_metrics(Prefix, HookSpecs), %% Ensure hooks ensure_hooks(HookSpecs), - {ok, #{name => Name, - options => ReqOpts, - channel => _ChannPoolPid, - hookspec => HookSpecs, - prefix => Prefix }}; + {ok, #{ + name => Name, + options => ReqOpts, + channel => _ChannPoolPid, + hookspec => HookSpecs, + prefix => Prefix + }}; {error, _} = E -> emqx_exhook_sup:stop_grpc_client_channel(Name), E end; - {error, _} = E -> E + {error, _} = E -> + E end. %% @private channel_opts(Opts = #{url := URL}) -> - ClientOpts = maps:merge(#{pool_size => erlang:system_info(schedulers)}, - Opts), + ClientOpts = maps:merge( + #{pool_size => erlang:system_info(schedulers)}, + Opts + ), case uri_string:parse(URL) of #{scheme := <<"http">>, host := Host, port := Port} -> {format_http_uri("http", Host, Port), ClientOpts}; #{scheme := <<"https">>, host := Host, port := Port} -> SslOpts = case maps:get(ssl, Opts, undefined) of - undefined -> []; - #{enable := false} -> []; + undefined -> + []; + #{enable := false} -> + []; MapOpts -> filter( - [{cacertfile, maps:get(cacertfile, MapOpts, undefined)}, - {certfile, maps:get(certfile, MapOpts, undefined)}, - {keyfile, maps:get(keyfile, MapOpts, undefined)} - ]) + [ + {cacertfile, maps:get(cacertfile, MapOpts, undefined)}, + {certfile, maps:get(certfile, MapOpts, undefined)}, + {keyfile, maps:get(keyfile, MapOpts, undefined)} + ] + ) end, NClientOpts = ClientOpts#{ - gun_opts => - #{transport => ssl, - transport_opts => SslOpts} - }, + gun_opts => + #{ + transport => ssl, + transport_opts => SslOpts + } + }, {format_http_uri("https", Host, Port), NClientOpts}; Error -> error({bad_server_url, URL, Error}) @@ -147,7 +163,7 @@ format_http_uri(Scheme, Host, Port) -> lists:flatten(io_lib:format("~ts://~ts:~w", [Scheme, Host, Port])). filter(Ls) -> - [ E || E <- Ls, E /= undefined]. + [E || E <- Ls, E /= undefined]. -spec unload(server()) -> ok. unload(#{name := Name, options := ReqOpts, hookspec := HookSpecs}) -> @@ -162,19 +178,24 @@ do_deinit(Name, ReqOpts) -> do_init(ChannName, ReqOpts) -> %% BrokerInfo defined at: exhook.protos - BrokerInfo = maps:with([version, sysdescr, uptime, datetime], - maps:from_list(emqx_sys:info())), + BrokerInfo = maps:with( + [version, sysdescr, uptime, datetime], + maps:from_list(emqx_sys:info()) + ), Req = #{broker => BrokerInfo}, case do_call(ChannName, undefined, 'on_provider_loaded', Req, ReqOpts) of {ok, InitialResp} -> try {ok, resolve_hookspec(maps:get(hooks, InitialResp, []))} - catch _:Reason:Stk -> - ?SLOG(error, #{msg => "failed_to_init_channel", - channel_name => ChannName, - reason => Reason, - stacktrace => Stk}), - {error, Reason} + catch + _:Reason:Stk -> + ?SLOG(error, #{ + msg => "failed_to_init_channel", + channel_name => ChannName, + reason => Reason, + stacktrace => Stk + }), + {error, Reason} end; {error, Reason} -> {error, Reason} @@ -184,60 +205,80 @@ do_init(ChannName, ReqOpts) -> resolve_hookspec(HookSpecs) when is_list(HookSpecs) -> MessageHooks = message_hooks(), AvailableHooks = available_hooks(), - lists:foldr(fun(HookSpec, Acc) -> - case maps:get(name, HookSpec, undefined) of - undefined -> Acc; - Name0 -> - Name = try - binary_to_existing_atom(Name0, utf8) - catch T:R:_ -> {T,R} - end, - case {lists:member(Name, AvailableHooks), - lists:member(Name, MessageHooks)} of - {false, _} -> - error({unknown_hookpoint, Name}); - {true, false} -> - Acc#{Name => #{}}; - {true, true} -> - Acc#{Name => #{ - topics => maps:get(topics, HookSpec, [])}} - end - end - end, #{}, HookSpecs). + lists:foldr( + fun(HookSpec, Acc) -> + case maps:get(name, HookSpec, undefined) of + undefined -> + Acc; + Name0 -> + Name = + try + binary_to_existing_atom(Name0, utf8) + catch + T:R:_ -> {T, R} + end, + case {lists:member(Name, AvailableHooks), lists:member(Name, MessageHooks)} of + {false, _} -> + error({unknown_hookpoint, Name}); + {true, false} -> + Acc#{Name => #{}}; + {true, true} -> + Acc#{ + Name => #{ + topics => maps:get(topics, HookSpec, []) + } + } + end + end + end, + #{}, + HookSpecs + ). ensure_metrics(Prefix, HookSpecs) -> - Keys = [list_to_atom(Prefix ++ atom_to_list(Hookpoint)) - || Hookpoint <- maps:keys(HookSpecs)], + Keys = [ + list_to_atom(Prefix ++ atom_to_list(Hookpoint)) + || Hookpoint <- maps:keys(HookSpecs) + ], lists:foreach(fun emqx_metrics:ensure/1, Keys). ensure_hooks(HookSpecs) -> - lists:foreach(fun(Hookpoint) -> - case lists:keyfind(Hookpoint, 1, ?ENABLED_HOOKS) of - false -> - ?SLOG(error, #{msg => "skipped_unknown_hookpoint", hookpoint => Hookpoint}); - {Hookpoint, {M, F, A}} -> - emqx_hooks:put(Hookpoint, {M, F, A}), - ets:update_counter(?HOOKS_REF_COUNTER, Hookpoint, {2, 1}, {Hookpoint, 0}) - end - end, maps:keys(HookSpecs)). + lists:foreach( + fun(Hookpoint) -> + case lists:keyfind(Hookpoint, 1, ?ENABLED_HOOKS) of + false -> + ?SLOG(error, #{msg => "skipped_unknown_hookpoint", hookpoint => Hookpoint}); + {Hookpoint, {M, F, A}} -> + emqx_hooks:put(Hookpoint, {M, F, A}), + ets:update_counter(?HOOKS_REF_COUNTER, Hookpoint, {2, 1}, {Hookpoint, 0}) + end + end, + maps:keys(HookSpecs) + ). may_unload_hooks(HookSpecs) -> - lists:foreach(fun(Hookpoint) -> - case ets:update_counter(?HOOKS_REF_COUNTER, Hookpoint, {2, -1}, {Hookpoint, 0}) of - Cnt when Cnt =< 0 -> - case lists:keyfind(Hookpoint, 1, ?ENABLED_HOOKS) of - {Hookpoint, {M, F, _A}} -> - emqx_hooks:del(Hookpoint, {M, F}); - _ -> ok - end, - ets:delete(?HOOKS_REF_COUNTER, Hookpoint); - _ -> ok - end - end, maps:keys(HookSpecs)). + lists:foreach( + fun(Hookpoint) -> + case ets:update_counter(?HOOKS_REF_COUNTER, Hookpoint, {2, -1}, {Hookpoint, 0}) of + Cnt when Cnt =< 0 -> + case lists:keyfind(Hookpoint, 1, ?ENABLED_HOOKS) of + {Hookpoint, {M, F, _A}} -> + emqx_hooks:del(Hookpoint, {M, F}); + _ -> + ok + end, + ets:delete(?HOOKS_REF_COUNTER, Hookpoint); + _ -> + ok + end + end, + maps:keys(HookSpecs) + ). format(#{name := Name, hookspec := Hooks}) -> lists:flatten( - io_lib:format("name=~ts, hooks=~0p, active=true", [Name, Hooks])). + io_lib:format("name=~ts, hooks=~0p, active=true", [Name, Hooks]) + ). %%-------------------------------------------------------------------- %% APIs @@ -248,28 +289,41 @@ name(#{name := Name}) -> hooks(#{hookspec := Hooks}) -> FoldFun = fun(Hook, Params, Acc) -> - [#{ name => Hook - , params => Params - } | Acc] - end, + [ + #{ + name => Hook, + params => Params + } + | Acc + ] + end, maps:fold(FoldFun, [], Hooks). --spec call(hookpoint(), map(), server()) -> ignore - | {ok, Resp :: term()} - | {error, term()}. -call(Hookpoint, Req, #{name := ChannName, options := ReqOpts, - hookspec := Hooks, prefix := Prefix}) -> +-spec call(hookpoint(), map(), server()) -> + ignore + | {ok, Resp :: term()} + | {error, term()}. +call(Hookpoint, Req, #{ + name := ChannName, + options := ReqOpts, + hookspec := Hooks, + prefix := Prefix +}) -> case maps:get(Hookpoint, Hooks, undefined) of - undefined -> ignore; + undefined -> + ignore; Opts -> - NeedCall = case lists:member(Hookpoint, message_hooks()) of - false -> true; - _ -> - #{message := #{topic := Topic}} = Req, - match_topic_filter(Topic, maps:get(topics, Opts, [])) - end, + NeedCall = + case lists:member(Hookpoint, message_hooks()) of + false -> + true; + _ -> + #{message := #{topic := Topic}} = Req, + match_topic_filter(Topic, maps:get(topics, Opts, [])) + end, case NeedCall of - false -> ignore; + false -> + ignore; _ -> inc_metrics(Prefix, Hookpoint), GrpcFun = hk2func(Hookpoint), @@ -293,42 +347,67 @@ match_topic_filter(TopicName, TopicFilter) -> -ifdef(TEST). -define(CALL_PB_CLIENT(ChanneName, Fun, Req, Options), - apply(?PB_CLIENT_MOD, Fun, [Req, #{<<"channel">> => ChannName}, Options])). + apply(?PB_CLIENT_MOD, Fun, [Req, #{<<"channel">> => ChannName}, Options]) +). -else. -define(CALL_PB_CLIENT(ChanneName, Fun, Req, Options), - apply(?PB_CLIENT_MOD, Fun, [Req, Options])). + apply(?PB_CLIENT_MOD, Fun, [Req, Options]) +). -endif. -spec do_call(binary(), atom(), atom(), map(), map()) -> {ok, map()} | {error, term()}. do_call(ChannName, Hookpoint, Fun, Req, ReqOpts) -> Options = ReqOpts#{channel => ChannName}, - ?SLOG(debug, #{msg => "do_call", module => ?PB_CLIENT_MOD, function => Fun, - req => Req, options => Options}), + ?SLOG(debug, #{ + msg => "do_call", + module => ?PB_CLIENT_MOD, + function => Fun, + req => Req, + options => Options + }), case catch ?CALL_PB_CLIENT(ChanneName, Fun, Req, Options) of {ok, Resp, Metadata} -> ?SLOG(debug, #{msg => "do_call_ok", resp => Resp, metadata => Metadata}), update_metrics(Hookpoint, ChannName, fun emqx_exhook_metrics:succeed/2), {ok, Resp}; {error, {Code, Msg}, _Metadata} -> - ?SLOG(error, #{msg => "exhook_call_error", module => ?PB_CLIENT_MOD, function => Fun, - req => Req, options => Options, code => Code, packet => Msg}), + ?SLOG(error, #{ + msg => "exhook_call_error", + module => ?PB_CLIENT_MOD, + function => Fun, + req => Req, + options => Options, + code => Code, + packet => Msg + }), update_metrics(Hookpoint, ChannName, fun emqx_exhook_metrics:failed/2), {error, {Code, Msg}}; {error, Reason} -> - ?SLOG(error, #{msg => "exhook_call_error", module => ?PB_CLIENT_MOD, function => Fun, - req => Req, options => Options, reason => Reason}), + ?SLOG(error, #{ + msg => "exhook_call_error", + module => ?PB_CLIENT_MOD, + function => Fun, + req => Req, + options => Options, + reason => Reason + }), update_metrics(Hookpoint, ChannName, fun emqx_exhook_metrics:failed/2), {error, Reason}; {'EXIT', {Reason, Stk}} -> - ?SLOG(error, #{msg => "exhook_call_exception", module => ?PB_CLIENT_MOD, function => Fun, - req => Req, options => Options, stacktrace => Stk}), + ?SLOG(error, #{ + msg => "exhook_call_exception", + module => ?PB_CLIENT_MOD, + function => Fun, + req => Req, + options => Options, + stacktrace => Stk + }), update_metrics(Hookpoint, ChannName, fun emqx_exhook_metrics:failed/2), {error, Reason} end. update_metrics(undefined, _ChannName, _Fun) -> ok; - update_metrics(Hookpoint, ChannName, Fun) -> Fun(ChannName, Hookpoint). @@ -356,20 +435,36 @@ hk2func('session.discarded') -> 'on_session_discarded'; hk2func('session.takenover') -> 'on_session_takenover'; hk2func('session.terminated') -> 'on_session_terminated'; hk2func('message.publish') -> 'on_message_publish'; -hk2func('message.delivered') ->'on_message_delivered'; +hk2func('message.delivered') -> 'on_message_delivered'; hk2func('message.acked') -> 'on_message_acked'; -hk2func('message.dropped') ->'on_message_dropped'. +hk2func('message.dropped') -> 'on_message_dropped'. -compile({inline, [message_hooks/0]}). message_hooks() -> - ['message.publish', 'message.delivered', - 'message.acked', 'message.dropped']. + [ + 'message.publish', + 'message.delivered', + 'message.acked', + 'message.dropped' + ]. -compile({inline, [available_hooks/0]}). available_hooks() -> - ['client.connect', 'client.connack', 'client.connected', - 'client.disconnected', 'client.authenticate', 'client.authorize', - 'client.subscribe', 'client.unsubscribe', - 'session.created', 'session.subscribed', 'session.unsubscribed', - 'session.resumed', 'session.discarded', 'session.takenover', - 'session.terminated' | message_hooks()]. + [ + 'client.connect', + 'client.connack', + 'client.connected', + 'client.disconnected', + 'client.authenticate', + 'client.authorize', + 'client.subscribe', + 'client.unsubscribe', + 'session.created', + 'session.subscribed', + 'session.unsubscribed', + 'session.resumed', + 'session.discarded', + 'session.takenover', + 'session.terminated' + | message_hooks() + ]. diff --git a/apps/emqx_exhook/src/emqx_exhook_sup.erl b/apps/emqx_exhook/src/emqx_exhook_sup.erl index 513ca783e..fb424ddff 100644 --- a/apps/emqx_exhook/src/emqx_exhook_sup.erl +++ b/apps/emqx_exhook/src/emqx_exhook_sup.erl @@ -18,21 +18,22 @@ -behaviour(supervisor). --export([ start_link/0 - , init/1 - ]). +-export([ + start_link/0, + init/1 +]). --export([ start_grpc_client_channel/3 - , stop_grpc_client_channel/1 - ]). +-export([ + start_grpc_client_channel/3, + stop_grpc_client_channel/1 +]). --define(CHILD(Mod, Type, Args), - #{ id => Mod - , start => {Mod, start_link, Args} - , type => Type - , shutdown => 15000 - } - ). +-define(CHILD(Mod, Type, Args), #{ + id => Mod, + start => {Mod, start_link, Args}, + type => Type, + shutdown => 15000 +}). %%-------------------------------------------------------------------- %% Supervisor APIs & Callbacks @@ -52,9 +53,10 @@ init([]) -> %%-------------------------------------------------------------------- -spec start_grpc_client_channel( - binary(), - uri_string:uri_string(), - grpc_client_sup:options()) -> {ok, pid()} | {error, term()}. + binary(), + uri_string:uri_string(), + grpc_client_sup:options() +) -> {ok, pid()} | {error, term()}. start_grpc_client_channel(Name, SvrAddr, Options) -> grpc_client_sup:create_channel_pool(Name, SvrAddr, Options). diff --git a/apps/emqx_exhook/src/proto/emqx_exhook_proto_v1.erl b/apps/emqx_exhook/src/proto/emqx_exhook_proto_v1.erl index a61acf1e7..06127c620 100644 --- a/apps/emqx_exhook/src/proto/emqx_exhook_proto_v1.erl +++ b/apps/emqx_exhook/src/proto/emqx_exhook_proto_v1.erl @@ -18,12 +18,13 @@ -behaviour(emqx_bpapi). --export([ introduced_in/0 +-export([ + introduced_in/0, - , all_servers_info/1 - , server_info/2 - , server_hooks_metrics/2 - ]). + all_servers_info/1, + server_info/2, + server_hooks_metrics/2 +]). -include_lib("emqx/include/bpapi.hrl"). @@ -31,16 +32,16 @@ introduced_in() -> "5.0.0". -spec all_servers_info([node()]) -> - emqx_rpc:erpc_multicall(map()). + emqx_rpc:erpc_multicall(map()). all_servers_info(Nodes) -> erpc:multicall(Nodes, emqx_exhook_mgr, all_servers_info, []). -spec server_info([node()], emqx_exhook_mgr:server_name()) -> - emqx_rpc:erpc_multicall(map()). + emqx_rpc:erpc_multicall(map()). server_info(Nodes, Name) -> erpc:multicall(Nodes, emqx_exhook_mgr, server_info, [Name]). -spec server_hooks_metrics([node()], emqx_exhook_mgr:server_name()) -> - emqx_rpc:erpc_multicall(emqx_exhook_metrics:hooks_metrics()). + emqx_rpc:erpc_multicall(emqx_exhook_metrics:hooks_metrics()). server_hooks_metrics(Nodes, Name) -> erpc:multicall(Nodes, emqx_exhook_mgr, server_hooks_metrics, [Name]). diff --git a/apps/emqx_exhook/test/emqx_exhook_SUITE.erl b/apps/emqx_exhook/test/emqx_exhook_SUITE.erl index 5af8606a7..add366f76 100644 --- a/apps/emqx_exhook/test/emqx_exhook_SUITE.erl +++ b/apps/emqx_exhook/test/emqx_exhook_SUITE.erl @@ -23,26 +23,27 @@ -include_lib("common_test/include/ct.hrl"). -define(CLUSTER_RPC_SHARD, emqx_cluster_rpc_shard). --define(CONF_DEFAULT, <<" -exhook { - servers = [ - { name = default, - url = \"http://127.0.0.1:9000\" - }, - { name = enable, - enable = false, - url = \"http://127.0.0.1:9000\" - }, - { name = error, - url = \"http://127.0.0.1:9001\" - }, - { name = not_reconnect, - auto_reconnect = false, - url = \"http://127.0.0.1:9001\" - } - ] -} -">>). +-define(CONF_DEFAULT, << + "\n" + "exhook {\n" + " servers = [\n" + " { name = default,\n" + " url = \"http://127.0.0.1:9000\"\n" + " },\n" + " { name = enable,\n" + " enable = false,\n" + " url = \"http://127.0.0.1:9000\"\n" + " },\n" + " { name = error,\n" + " url = \"http://127.0.0.1:9001\"\n" + " },\n" + " { name = not_reconnect,\n" + " auto_reconnect = false,\n" + " url = \"http://127.0.0.1:9001\"\n" + " }\n" + " ]\n" + "}\n" +>>). %%-------------------------------------------------------------------- %% Setups @@ -79,7 +80,8 @@ init_per_testcase(_, Config) -> end_per_testcase(_, Config) -> case erlang:whereis(node()) of - undefined -> ok; + undefined -> + ok; P -> erlang:unlink(P), erlang:exit(P, kill) @@ -95,22 +97,29 @@ load_cfg(Cfg) -> t_access_failed_if_no_server_running(_) -> emqx_exhook_mgr:disable(<<"default">>), - ClientInfo = #{clientid => <<"user-id-1">>, - username => <<"usera">>, - peerhost => {127,0,0,1}, - sockport => 1883, - protocol => mqtt, - mountpoint => undefined - }, - ?assertMatch({stop, {error, not_authorized}}, - emqx_exhook_handler:on_client_authenticate(ClientInfo, #{auth_result => success})), + ClientInfo = #{ + clientid => <<"user-id-1">>, + username => <<"usera">>, + peerhost => {127, 0, 0, 1}, + sockport => 1883, + protocol => mqtt, + mountpoint => undefined + }, + ?assertMatch( + {stop, {error, not_authorized}}, + emqx_exhook_handler:on_client_authenticate(ClientInfo, #{auth_result => success}) + ), - ?assertMatch({stop, deny}, - emqx_exhook_handler:on_client_authorize(ClientInfo, publish, <<"t/1">>, allow)), + ?assertMatch( + {stop, deny}, + emqx_exhook_handler:on_client_authorize(ClientInfo, publish, <<"t/1">>, allow) + ), Message = emqx_message:make(<<"t/1">>, <<"abc">>), - ?assertMatch({stop, Message}, - emqx_exhook_handler:on_message_publish(Message)), + ?assertMatch( + {stop, Message}, + emqx_exhook_handler:on_message_publish(Message) + ), emqx_exhook_mgr:enable(<<"default">>). t_lookup(_) -> @@ -120,9 +129,14 @@ t_lookup(_) -> t_list(_) -> [H | _] = emqx_exhook_mgr:list(), - ?assertMatch(#{name := _, - status := _, - hooks := _}, H). + ?assertMatch( + #{ + name := _, + status := _, + hooks := _ + }, + H + ). t_unexpected(_) -> ok = gen_server:cast(emqx_exhook_mgr, unexpected), @@ -149,9 +163,11 @@ t_error_update_conf(_) -> ErrorAnd = #{<<"name">> => Name, <<"url">> => <<"http://127.0.0.1:9001">>}, {ok, _} = emqx_exhook_mgr:update_config(Path, {add, ErrorAnd}), - DisableAnd = #{<<"name">> => Name, - <<"url">> => <<"http://127.0.0.1:9001">>, - <<"enable">> => false}, + DisableAnd = #{ + <<"name">> => Name, + <<"url">> => <<"http://127.0.0.1:9001">>, + <<"enable">> => false + }, {ok, _} = emqx_exhook_mgr:update_config(Path, {add, DisableAnd}), {ok, _} = emqx_exhook_mgr:update_config(Path, {delete, <<"error">>}), @@ -179,10 +195,12 @@ t_metrics(_) -> t_handler(_) -> %% connect - {ok, C} = emqtt:start_link([{host, "localhost"}, - {port, 1883}, - {username, <<"gooduser">>}, - {clientid, <<"exhook_gooduser">>}]), + {ok, C} = emqtt:start_link([ + {host, "localhost"}, + {port, 1883}, + {username, <<"gooduser">>}, + {clientid, <<"exhook_gooduser">>} + ]), {ok, _} = emqtt:connect(C), %% pub/sub @@ -212,13 +230,14 @@ t_handler(_) -> ok. t_simulated_handler(_) -> - ClientInfo = #{clientid => <<"user-id-1">>, - username => <<"usera">>, - peerhost => {127,0,0,1}, - sockport => 1883, - protocol => mqtt, - mountpoint => undefined - }, + ClientInfo = #{ + clientid => <<"user-id-1">>, + username => <<"usera">>, + peerhost => {127, 0, 0, 1}, + sockport => 1883, + protocol => mqtt, + mountpoint => undefined + }, %% resume/takeover ok = emqx_exhook_handler:on_session_resumed(ClientInfo, undefined), ok = emqx_exhook_handler:on_session_discarded(ClientInfo, undefined), @@ -232,36 +251,38 @@ t_misc_test(_) -> ok. t_get_basic_usage_info(_Config) -> - #{ num_servers := NumServers - , servers := Servers - } = emqx_exhook:get_basic_usage_info(), + #{ + num_servers := NumServers, + servers := Servers + } = emqx_exhook:get_basic_usage_info(), ?assertEqual(1, NumServers), ?assertMatch([_], Servers), [#{driver := Driver, hooks := Hooks}] = Servers, ?assertEqual(grpc, Driver), ?assertEqual( - [ - 'client.authenticate', - 'client.authorize', - 'client.connack', - 'client.connect', - 'client.connected', - 'client.disconnected', - 'client.subscribe', - 'client.unsubscribe', - 'message.acked', - 'message.delivered', - 'message.dropped', - 'message.publish', - 'session.created', - 'session.discarded', - 'session.resumed', - 'session.subscribed', - 'session.takenover', - 'session.terminated', - 'session.unsubscribed' - ], - lists:sort(Hooks)). + [ + 'client.authenticate', + 'client.authorize', + 'client.connack', + 'client.connect', + 'client.connected', + 'client.disconnected', + 'client.subscribe', + 'client.unsubscribe', + 'message.acked', + 'message.delivered', + 'message.dropped', + 'message.publish', + 'session.created', + 'session.discarded', + 'session.resumed', + 'session.subscribed', + 'session.takenover', + 'session.terminated', + 'session.unsubscribed' + ], + lists:sort(Hooks) + ). %%-------------------------------------------------------------------- %% Utils @@ -276,14 +297,17 @@ unmeck_print() -> meck:unload(emqx_ctl). loaded_exhook_hookpoints() -> - lists:filtermap(fun(E) -> - Name = element(2, E), - Callbacks = element(3, E), - case lists:any(fun is_exhook_callback/1, Callbacks) of - true -> {true, Name}; - _ -> false - end - end, ets:tab2list(emqx_hooks)). + lists:filtermap( + fun(E) -> + Name = element(2, E), + Callbacks = element(3, E), + case lists:any(fun is_exhook_callback/1, Callbacks) of + true -> {true, Name}; + _ -> false + end + end, + ets:tab2list(emqx_hooks) + ). is_exhook_callback(Cb) -> Action = element(2, Cb), diff --git a/apps/emqx_exhook/test/emqx_exhook_api_SUITE.erl b/apps/emqx_exhook/test/emqx_exhook_api_SUITE.erl index 47a453f1e..26ad6f8e3 100644 --- a/apps/emqx_exhook/test/emqx_exhook_api_SUITE.erl +++ b/apps/emqx_exhook/test/emqx_exhook_api_SUITE.erl @@ -26,19 +26,29 @@ -define(BASE_PATH, "api"). -define(CLUSTER_RPC_SHARD, emqx_cluster_rpc_shard). --define(CONF_DEFAULT, <<" -exhook { - servers = - [ { name = default, - url = \"http://127.0.0.1:9000\" - } - ] -} -">>). +-define(CONF_DEFAULT, << + "\n" + "exhook {\n" + " servers =\n" + " [ { name = default,\n" + " url = \"http://127.0.0.1:9000\"\n" + " }\n" + " ]\n" + "}\n" +>>). all() -> - [ t_list, t_get, t_add, t_move_front, t_move_rear - , t_move_before, t_move_after, t_delete, t_hooks, t_update + [ + t_list, + t_get, + t_add, + t_move_front, + t_move_rear, + t_move_before, + t_move_after, + t_delete, + t_hooks, + t_update ]. init_per_suite(Config) -> @@ -71,7 +81,6 @@ init_per_testcase(t_add, Config) -> _ = emqx_exhook_demo_svr:start(<<"test1">>, 9001), timer:sleep(200), Config; - init_per_testcase(_, Config) -> {ok, _} = emqx_cluster_rpc:start_link(), timer:sleep(200), @@ -79,7 +88,8 @@ init_per_testcase(_, Config) -> end_per_testcase(_, Config) -> case erlang:whereis(node()) of - undefined -> ok; + undefined -> + ok; P -> erlang:unlink(P), erlang:exit(P, kill) @@ -87,108 +97,168 @@ end_per_testcase(_, Config) -> Config. t_list(_) -> - {ok, Data} = request_api(get, api_path(["exhooks"]), "", - auth_header_()), + {ok, Data} = request_api( + get, + api_path(["exhooks"]), + "", + auth_header_() + ), List = decode_json(Data), ?assertEqual(1, length(List)), [Svr] = List, - ?assertMatch(#{name := <<"default">>, - metrics := _, - node_metrics := _, - node_status := _, - hooks := _ - }, Svr). + ?assertMatch( + #{ + name := <<"default">>, + metrics := _, + node_metrics := _, + node_status := _, + hooks := _ + }, + Svr + ). t_get(_) -> - {ok, Data} = request_api(get, api_path(["exhooks", "default"]), "", - auth_header_()), + {ok, Data} = request_api( + get, + api_path(["exhooks", "default"]), + "", + auth_header_() + ), Svr = decode_json(Data), - ?assertMatch(#{name := <<"default">>, - metrics := _, - node_metrics := _, - node_status := _, - hooks := _ - }, Svr). + ?assertMatch( + #{ + name := <<"default">>, + metrics := _, + node_metrics := _, + node_status := _, + hooks := _ + }, + Svr + ). t_add(Cfg) -> Template = proplists:get_value(template, Cfg), - Instance = Template#{name => <<"test1">>, - url => "http://127.0.0.1:9001" - }, - {ok, Data} = request_api(post, api_path(["exhooks"]), "", - auth_header_(), Instance), + Instance = Template#{ + name => <<"test1">>, + url => "http://127.0.0.1:9001" + }, + {ok, Data} = request_api( + post, + api_path(["exhooks"]), + "", + auth_header_(), + Instance + ), Svr = decode_json(Data), - ?assertMatch(#{name := <<"test1">>, - metrics := _, - node_metrics := _, - node_status := _, - hooks := _}, Svr), + ?assertMatch( + #{ + name := <<"test1">>, + metrics := _, + node_metrics := _, + node_status := _, + hooks := _ + }, + Svr + ), ?assertMatch([<<"default">>, <<"test1">>], emqx_exhook_mgr:running()). t_move_front(_) -> - Result = request_api(post, api_path(["exhooks", "default", "move"]), "", - auth_header_(), - #{position => <<"front">>}), + Result = request_api( + post, + api_path(["exhooks", "default", "move"]), + "", + auth_header_(), + #{position => <<"front">>} + ), ?assertMatch({ok, <<>>}, Result), ?assertMatch([<<"default">>, <<"test1">>], emqx_exhook_mgr:running()). t_move_rear(_) -> - Result = request_api(post, api_path(["exhooks", "default", "move"]), "", - auth_header_(), - #{position => <<"rear">>}), + Result = request_api( + post, + api_path(["exhooks", "default", "move"]), + "", + auth_header_(), + #{position => <<"rear">>} + ), ?assertMatch({ok, <<>>}, Result), ?assertMatch([<<"test1">>, <<"default">>], emqx_exhook_mgr:running()). t_move_before(_) -> - Result = request_api(post, api_path(["exhooks", "default", "move"]), "", - auth_header_(), - #{position => <<"before:test1">>}), + Result = request_api( + post, + api_path(["exhooks", "default", "move"]), + "", + auth_header_(), + #{position => <<"before:test1">>} + ), ?assertMatch({ok, <<>>}, Result), ?assertMatch([<<"default">>, <<"test1">>], emqx_exhook_mgr:running()). t_move_after(_) -> - Result = request_api(post, api_path(["exhooks", "default", "move"]), "", - auth_header_(), - #{position => <<"after:test1">>}), + Result = request_api( + post, + api_path(["exhooks", "default", "move"]), + "", + auth_header_(), + #{position => <<"after:test1">>} + ), ?assertMatch({ok, <<>>}, Result), ?assertMatch([<<"test1">>, <<"default">>], emqx_exhook_mgr:running()). t_delete(_) -> - Result = request_api(delete, api_path(["exhooks", "test1"]), "", - auth_header_()), + Result = request_api( + delete, + api_path(["exhooks", "test1"]), + "", + auth_header_() + ), ?assertMatch({ok, <<>>}, Result), ?assertMatch([<<"default">>], emqx_exhook_mgr:running()). t_hooks(_Cfg) -> - {ok, Data} = request_api(get, api_path(["exhooks", "default", "hooks"]), "", - auth_header_()), + {ok, Data} = request_api( + get, + api_path(["exhooks", "default", "hooks"]), + "", + auth_header_() + ), [Hook1 | _] = decode_json(Data), - ?assertMatch(#{name := _, - params := _, - metrics := _, - node_metrics := _ - }, Hook1). + ?assertMatch( + #{ + name := _, + params := _, + metrics := _, + node_metrics := _ + }, + Hook1 + ). t_update(Cfg) -> Template = proplists:get_value(template, Cfg), Instance = Template#{enable => false}, - {ok, <<>>} = request_api(put, api_path(["exhooks", "default"]), "", - auth_header_(), Instance), + {ok, <<>>} = request_api( + put, + api_path(["exhooks", "default"]), + "", + auth_header_(), + Instance + ), ?assertMatch([], emqx_exhook_mgr:running()). @@ -203,24 +273,27 @@ request_api(Method, Url, QueryParams, Auth) -> request_api(Method, Url, QueryParams, Auth, []). request_api(Method, Url, QueryParams, Auth, []) -> - NewUrl = case QueryParams of - "" -> Url; - _ -> Url ++ "?" ++ QueryParams - end, + NewUrl = + case QueryParams of + "" -> Url; + _ -> Url ++ "?" ++ QueryParams + end, do_request_api(Method, {NewUrl, [Auth]}); request_api(Method, Url, QueryParams, Auth, Body) -> - NewUrl = case QueryParams of - "" -> Url; - _ -> Url ++ "?" ++ QueryParams - end, + NewUrl = + case QueryParams of + "" -> Url; + _ -> Url ++ "?" ++ QueryParams + end, do_request_api(Method, {NewUrl, [Auth], "application/json", emqx_json:encode(Body)}). -do_request_api(Method, Request)-> +do_request_api(Method, Request) -> case httpc:request(Method, Request, [], [{body_format, binary}]) of {error, socket_closed_remotely} -> {error, socket_closed_remotely}; - {ok, {{"HTTP/1.1", Code, _}, _, Return} } - when Code =:= 200 orelse Code =:= 204 orelse Code =:= 201 -> + {ok, {{"HTTP/1.1", Code, _}, _, Return}} when + Code =:= 200 orelse Code =:= 204 orelse Code =:= 201 + -> {ok, Return}; {ok, {Reason, _, _}} -> {error, Reason} @@ -232,8 +305,8 @@ auth_header_() -> auth_header_(binary_to_list(AppId), binary_to_list(AppSecret)). auth_header_(User, Pass) -> - Encoded = base64:encode_to_string(lists:append([User,":",Pass])), - {"Authorization","Basic " ++ Encoded}. + Encoded = base64:encode_to_string(lists:append([User, ":", Pass])), + {"Authorization", "Basic " ++ Encoded}. -api_path(Parts)-> +api_path(Parts) -> ?HOST ++ filename:join([?BASE_PATH, ?API_VERSION] ++ Parts). diff --git a/apps/emqx_exhook/test/emqx_exhook_demo_svr.erl b/apps/emqx_exhook/test/emqx_exhook_demo_svr.erl index 528ecb2a2..75d75aaa1 100644 --- a/apps/emqx_exhook/test/emqx_exhook_demo_svr.erl +++ b/apps/emqx_exhook/test/emqx_exhook_demo_svr.erl @@ -19,37 +19,39 @@ -behaviour(emqx_exhook_v_1_hook_provider_bhvr). %% --export([ start/0 - , start/2 - , stop/0 - , stop/1 - , take/0 - , in/1 - ]). +-export([ + start/0, + start/2, + stop/0, + stop/1, + take/0, + in/1 +]). %% gRPC server HookProvider callbacks --export([ on_provider_loaded/2 - , on_provider_unloaded/2 - , on_client_connect/2 - , on_client_connack/2 - , on_client_connected/2 - , on_client_disconnected/2 - , on_client_authenticate/2 - , on_client_authorize/2 - , on_client_subscribe/2 - , on_client_unsubscribe/2 - , on_session_created/2 - , on_session_subscribed/2 - , on_session_unsubscribed/2 - , on_session_resumed/2 - , on_session_discarded/2 - , on_session_takenover/2 - , on_session_terminated/2 - , on_message_publish/2 - , on_message_delivered/2 - , on_message_dropped/2 - , on_message_acked/2 - ]). +-export([ + on_provider_loaded/2, + on_provider_unloaded/2, + on_client_connect/2, + on_client_connack/2, + on_client_connected/2, + on_client_disconnected/2, + on_client_authenticate/2, + on_client_authorize/2, + on_client_subscribe/2, + on_client_unsubscribe/2, + on_session_created/2, + on_session_subscribed/2, + on_session_unsubscribed/2, + on_session_resumed/2, + on_session_discarded/2, + on_session_takenover/2, + on_session_terminated/2, + on_message_publish/2, + on_message_delivered/2, + on_message_dropped/2, + on_message_acked/2 +]). -define(PORT, 9000). -define(NAME, ?MODULE). @@ -62,7 +64,7 @@ start() -> start(?NAME, ?PORT). start(Name, Port) -> - Pid = spawn(fun() -> mgr_main(Name, Port) end), + Pid = spawn(fun() -> mgr_main(Name, Port) end), register(to_atom_name(Name), Pid), {ok, Pid}. @@ -75,17 +77,20 @@ stop(Name) -> take() -> to_atom_name(?NAME) ! {take, self()}, - receive {value, V} -> V - after 5000 -> error(timeout) end. + receive + {value, V} -> V + after 5000 -> error(timeout) + end. in({FunName, Req}) -> to_atom_name(?NAME) ! {in, FunName, Req}. mgr_main(Name, Port) -> application:ensure_all_started(grpc), - Services = #{protos => [emqx_exhook_pb], - services => #{'emqx.exhook.v1.HookProvider' => emqx_exhook_demo_svr} - }, + Services = #{ + protos => [emqx_exhook_pb], + services => #{'emqx.exhook.v1.HookProvider' => emqx_exhook_demo_svr} + }, Options = [], Svr = grpc:start_server(Name, Port, Services, Options), mgr_loop([Svr, queue:new(), queue:new()]). @@ -103,9 +108,12 @@ mgr_loop([Svr, Q, Takes]) -> end. reply(Q1, Q2) -> - case queue:len(Q1) =:= 0 orelse - queue:len(Q2) =:= 0 of - true -> {Q1, Q2}; + case + queue:len(Q1) =:= 0 orelse + queue:len(Q2) =:= 0 + of + true -> + {Q1, Q2}; _ -> {{value, {Name, V}}, NQ1} = queue:out(Q1), {{value, From}, NQ2} = queue:out(Q2), @@ -115,7 +123,6 @@ reply(Q1, Q2) -> to_atom_name(Name) when is_atom(Name) -> Name; - to_atom_name(Name) -> erlang:binary_to_atom(Name). @@ -123,240 +130,286 @@ to_atom_name(Name) -> %% callbacks %%-------------------------------------------------------------------- --spec on_provider_loaded(emqx_exhook_pb:provider_loaded_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:loaded_response(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_provider_loaded(emqx_exhook_pb:provider_loaded_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:loaded_response(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_provider_loaded(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), - {ok, #{hooks => [ - #{name => <<"client.connect">>}, - #{name => <<"client.connack">>}, - #{name => <<"client.connected">>}, - #{name => <<"client.disconnected">>}, - #{name => <<"client.authenticate">>}, - #{name => <<"client.authorize">>}, - #{name => <<"client.subscribe">>}, - #{name => <<"client.unsubscribe">>}, - #{name => <<"session.created">>}, - #{name => <<"session.subscribed">>}, - #{name => <<"session.unsubscribed">>}, - #{name => <<"session.resumed">>}, - #{name => <<"session.discarded">>}, - #{name => <<"session.takenover">>}, - #{name => <<"session.terminated">>}, - #{name => <<"message.publish">>}, - #{name => <<"message.delivered">>}, - #{name => <<"message.acked">>}, - #{name => <<"message.dropped">>}]}, Md}. --spec on_provider_unloaded(emqx_exhook_pb:provider_unloaded_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. + {ok, + #{ + hooks => [ + #{name => <<"client.connect">>}, + #{name => <<"client.connack">>}, + #{name => <<"client.connected">>}, + #{name => <<"client.disconnected">>}, + #{name => <<"client.authenticate">>}, + #{name => <<"client.authorize">>}, + #{name => <<"client.subscribe">>}, + #{name => <<"client.unsubscribe">>}, + #{name => <<"session.created">>}, + #{name => <<"session.subscribed">>}, + #{name => <<"session.unsubscribed">>}, + #{name => <<"session.resumed">>}, + #{name => <<"session.discarded">>}, + #{name => <<"session.takenover">>}, + #{name => <<"session.terminated">>}, + #{name => <<"message.publish">>}, + #{name => <<"message.delivered">>}, + #{name => <<"message.acked">>}, + #{name => <<"message.dropped">>} + ] + }, + Md}. +-spec on_provider_unloaded(emqx_exhook_pb:provider_unloaded_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_provider_unloaded(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_client_connect(emqx_exhook_pb:client_connect_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_client_connect(emqx_exhook_pb:client_connect_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_client_connect(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_client_connack(emqx_exhook_pb:client_connack_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_client_connack(emqx_exhook_pb:client_connack_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_client_connack(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_client_connected(emqx_exhook_pb:client_connected_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_client_connected(emqx_exhook_pb:client_connected_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_client_connected(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_client_disconnected(emqx_exhook_pb:client_disconnected_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_client_disconnected(emqx_exhook_pb:client_disconnected_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_client_disconnected(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_client_authenticate(emqx_exhook_pb:client_authenticate_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:valued_response(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_client_authenticate(emqx_exhook_pb:client_authenticate_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:valued_response(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_client_authenticate(#{clientinfo := #{username := Username}} = Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), %% some cases for testing case Username of <<"baduser">> -> - {ok, #{type => 'STOP_AND_RETURN', - value => {bool_result, false}}, Md}; + {ok, + #{ + type => 'STOP_AND_RETURN', + value => {bool_result, false} + }, + Md}; <<"gooduser">> -> - {ok, #{type => 'STOP_AND_RETURN', - value => {bool_result, true}}, Md}; + {ok, + #{ + type => 'STOP_AND_RETURN', + value => {bool_result, true} + }, + Md}; <<"normaluser">> -> - {ok, #{type => 'CONTINUE', - value => {bool_result, true}}, Md}; + {ok, + #{ + type => 'CONTINUE', + value => {bool_result, true} + }, + Md}; _ -> {ok, #{type => 'IGNORE'}, Md} end. --spec on_client_authorize(emqx_exhook_pb:client_authorize_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:valued_response(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_client_authorize(emqx_exhook_pb:client_authorize_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:valued_response(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_client_authorize(#{clientinfo := #{username := Username}} = Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), %% some cases for testing case Username of <<"baduser">> -> - {ok, #{type => 'STOP_AND_RETURN', - value => {bool_result, false}}, Md}; + {ok, + #{ + type => 'STOP_AND_RETURN', + value => {bool_result, false} + }, + Md}; <<"gooduser">> -> - {ok, #{type => 'STOP_AND_RETURN', - value => {bool_result, true}}, Md}; + {ok, + #{ + type => 'STOP_AND_RETURN', + value => {bool_result, true} + }, + Md}; <<"normaluser">> -> - {ok, #{type => 'CONTINUE', - value => {bool_result, true}}, Md}; + {ok, + #{ + type => 'CONTINUE', + value => {bool_result, true} + }, + Md}; _ -> {ok, #{type => 'IGNORE'}, Md} end. --spec on_client_subscribe(emqx_exhook_pb:client_subscribe_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_client_subscribe(emqx_exhook_pb:client_subscribe_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_client_subscribe(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_client_unsubscribe(emqx_exhook_pb:client_unsubscribe_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_client_unsubscribe(emqx_exhook_pb:client_unsubscribe_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_client_unsubscribe(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_session_created(emqx_exhook_pb:session_created_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_session_created(emqx_exhook_pb:session_created_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_session_created(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_session_subscribed(emqx_exhook_pb:session_subscribed_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_session_subscribed(emqx_exhook_pb:session_subscribed_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_session_subscribed(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_session_unsubscribed(emqx_exhook_pb:session_unsubscribed_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_session_unsubscribed(emqx_exhook_pb:session_unsubscribed_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_session_unsubscribed(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_session_resumed(emqx_exhook_pb:session_resumed_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_session_resumed(emqx_exhook_pb:session_resumed_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_session_resumed(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_session_discarded(emqx_exhook_pb:session_discarded_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_session_discarded(emqx_exhook_pb:session_discarded_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_session_discarded(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_session_takenover(emqx_exhook_pb:session_takenover_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_session_takenover(emqx_exhook_pb:session_takenover_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_session_takenover(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_session_terminated(emqx_exhook_pb:session_terminated_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_session_terminated(emqx_exhook_pb:session_terminated_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_session_terminated(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_message_publish(emqx_exhook_pb:message_publish_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:valued_response(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_message_publish(emqx_exhook_pb:message_publish_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:valued_response(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_message_publish(#{message := #{from := From} = Msg} = Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), %% some cases for testing case From of <<"baduser">> -> - NMsg = deny(Msg#{qos => 0, - topic => <<"">>, - payload => <<"">> - }), - {ok, #{type => 'STOP_AND_RETURN', - value => {message, NMsg}}, Md}; + NMsg = deny(Msg#{ + qos => 0, + topic => <<"">>, + payload => <<"">> + }), + {ok, + #{ + type => 'STOP_AND_RETURN', + value => {message, NMsg} + }, + Md}; <<"gooduser">> -> - NMsg = allow(Msg#{topic => From, - payload => From}), - {ok, #{type => 'STOP_AND_RETURN', - value => {message, NMsg}}, Md}; + NMsg = allow(Msg#{ + topic => From, + payload => From + }), + {ok, + #{ + type => 'STOP_AND_RETURN', + value => {message, NMsg} + }, + Md}; _ -> {ok, #{type => 'IGNORE'}, Md} end. deny(Msg) -> - NHeader = maps:put(<<"allow_publish">>, <<"false">>, - maps:get(headers, Msg, #{})), + NHeader = maps:put( + <<"allow_publish">>, + <<"false">>, + maps:get(headers, Msg, #{}) + ), maps:put(headers, NHeader, Msg). allow(Msg) -> - NHeader = maps:put(<<"allow_publish">>, <<"true">>, - maps:get(headers, Msg, #{})), + NHeader = maps:put( + <<"allow_publish">>, + <<"true">>, + maps:get(headers, Msg, #{}) + ), maps:put(headers, NHeader, Msg). --spec on_message_delivered(emqx_exhook_pb:message_delivered_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_message_delivered(emqx_exhook_pb:message_delivered_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_message_delivered(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_message_dropped(emqx_exhook_pb:message_dropped_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_message_dropped(emqx_exhook_pb:message_dropped_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_message_dropped(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), {ok, #{}, Md}. --spec on_message_acked(emqx_exhook_pb:message_acked_request(), grpc:metadata()) - -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} - | {error, grpc_cowboy_h:error_response()}. +-spec on_message_acked(emqx_exhook_pb:message_acked_request(), grpc:metadata()) -> + {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} + | {error, grpc_cowboy_h:error_response()}. on_message_acked(Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), diff --git a/apps/emqx_exhook/test/emqx_exhook_metrics_SUITE.erl b/apps/emqx_exhook/test/emqx_exhook_metrics_SUITE.erl index 079a292ae..674814eab 100644 --- a/apps/emqx_exhook/test/emqx_exhook_metrics_SUITE.erl +++ b/apps/emqx_exhook/test/emqx_exhook_metrics_SUITE.erl @@ -26,19 +26,20 @@ -define(TARGET_HOOK, 'message.publish'). --define(CONF, <<" -exhook { - servers = [ - { name = succed, - url = \"http://127.0.0.1:9000\" - }, - { name = failed, - failed_action = ignore, - url = \"http://127.0.0.1:9001\" - }, - ] -} -">>). +-define(CONF, << + "\n" + "exhook {\n" + " servers = [\n" + " { name = succed,\n" + " url = \"http://127.0.0.1:9000\"\n" + " },\n" + " { name = failed,\n" + " failed_action = ignore,\n" + " url = \"http://127.0.0.1:9001\"\n" + " },\n" + " ]\n" + "}\n" +>>). %%-------------------------------------------------------------------- %% Setups @@ -78,11 +79,11 @@ end_per_testcase(_, Config) -> %%-------------------------------------------------------------------- t_servers_metrics(_Cfg) -> Test = fun(C) -> - Repeat = fun() -> - emqtt:publish(C, <<"/exhook/metrics">>, <<>>, qos0) - end, - repeat(Repeat, 10) - end, + Repeat = fun() -> + emqtt:publish(C, <<"/exhook/metrics">>, <<>>, qos0) + end, + repeat(Repeat, 10) + end, with_connection(Test), timer:sleep(200), @@ -99,54 +100,58 @@ t_servers_metrics(_Cfg) -> t_rate(_) -> Test = fun(C) -> - Repeat = fun() -> - emqtt:publish(C, <<"/exhook/metrics">>, <<>>, qos0) - end, + Repeat = fun() -> + emqtt:publish(C, <<"/exhook/metrics">>, <<>>, qos0) + end, - repeat(Repeat, 5), - timer:sleep(200), - emqx_exhook_metrics:update(timer:seconds(1)), - SM = emqx_exhook_metrics:server_metrics(<<"succed">>), - ?assertMatch(#{rate := 5, max_rate := 5}, SM), + repeat(Repeat, 5), + timer:sleep(200), + emqx_exhook_metrics:update(timer:seconds(1)), + SM = emqx_exhook_metrics:server_metrics(<<"succed">>), + ?assertMatch(#{rate := 5, max_rate := 5}, SM), - repeat(Repeat, 6), - timer:sleep(200), - emqx_exhook_metrics:update(timer:seconds(1)), - SM2 = emqx_exhook_metrics:server_metrics(<<"succed">>), - ?assertMatch(#{rate := 6, max_rate := 6}, SM2), + repeat(Repeat, 6), + timer:sleep(200), + emqx_exhook_metrics:update(timer:seconds(1)), + SM2 = emqx_exhook_metrics:server_metrics(<<"succed">>), + ?assertMatch(#{rate := 6, max_rate := 6}, SM2), - repeat(Repeat, 3), - timer:sleep(200), - emqx_exhook_metrics:update(timer:seconds(1)), - SM3 = emqx_exhook_metrics:server_metrics(<<"succed">>), - ?assertMatch(#{rate := 3, max_rate := 6}, SM3) - end, + repeat(Repeat, 3), + timer:sleep(200), + emqx_exhook_metrics:update(timer:seconds(1)), + SM3 = emqx_exhook_metrics:server_metrics(<<"succed">>), + ?assertMatch(#{rate := 3, max_rate := 6}, SM3) + end, with_connection(Test), ok. t_hooks_metrics(_) -> Test = fun(C) -> - Repeat = fun() -> - emqtt:publish(C, <<"/exhook/metrics">>, <<>>, qos0) - end, + Repeat = fun() -> + emqtt:publish(C, <<"/exhook/metrics">>, <<>>, qos0) + end, - repeat(Repeat, 5), - timer:sleep(200), - HM = emqx_exhook_metrics:hooks_metrics(<<"succed">>), - ?assertMatch(#{'message.publish' := - #{failed := 0, succeed := 5}}, HM) - end, + repeat(Repeat, 5), + timer:sleep(200), + HM = emqx_exhook_metrics:hooks_metrics(<<"succed">>), + ?assertMatch( + #{ + 'message.publish' := + #{failed := 0, succeed := 5} + }, + HM + ) + end, with_connection(Test), ok. t_on_server_deleted(_) -> - Test = fun(C) -> - Repeat = fun() -> - emqtt:publish(C, <<"/exhook/metrics">>, <<>>, qos0) - end, - repeat(Repeat, 10) - end, + Repeat = fun() -> + emqtt:publish(C, <<"/exhook/metrics">>, <<>>, qos0) + end, + repeat(Repeat, 10) + end, with_connection(Test), timer:sleep(200), @@ -165,50 +170,56 @@ clear_metrics() -> ets:delete_all_objects(?HOOKS_METRICS). init_injections(Injects) -> - lists:map(fun({Name, _}) -> - Str = erlang:atom_to_list(Name), - case lists:prefix("on_", Str) of - true -> - Action = fun(Req, #{<<"channel">> := SvrName} = Md) -> - case maps:get(?SvrFun(SvrName, Name), Injects, undefined) of - undefined -> - meck:passthrough([Req, Md]); - Injection -> - Injection(Req, Md) - end - end, + lists:map( + fun({Name, _}) -> + Str = erlang:atom_to_list(Name), + case lists:prefix("on_", Str) of + true -> + Action = fun(Req, #{<<"channel">> := SvrName} = Md) -> + case maps:get(?SvrFun(SvrName, Name), Injects, undefined) of + undefined -> + meck:passthrough([Req, Md]); + Injection -> + Injection(Req, Md) + end + end, - meck:expect(emqx_exhook_demo_svr, Name, Action); - _ -> - false - end - end, - emqx_exhook_demo_svr:module_info(exports)). + meck:expect(emqx_exhook_demo_svr, Name, Action); + _ -> + false + end + end, + emqx_exhook_demo_svr:module_info(exports) + ). hook_injects() -> - #{?SvrFun(<<"failed">>, emqx_exhook_server:hk2func(?TARGET_HOOK)) => - fun(_Req, _Md) -> - {error, "Error due to test"} - end, - ?SvrFun(<<"failed">>, on_provider_loaded) => - fun(_Req, Md) -> - {ok, #{hooks => [#{name => <<"message.publish">>}]}, Md} - end, - ?SvrFun(<<"succed">>, on_provider_loaded) => - fun(_Req, Md) -> - {ok, #{hooks => [#{name => <<"message.publish">>}]}, Md} - end - }. + #{ + ?SvrFun(<<"failed">>, emqx_exhook_server:hk2func(?TARGET_HOOK)) => + fun(_Req, _Md) -> + {error, "Error due to test"} + end, + ?SvrFun(<<"failed">>, on_provider_loaded) => + fun(_Req, Md) -> + {ok, #{hooks => [#{name => <<"message.publish">>}]}, Md} + end, + ?SvrFun(<<"succed">>, on_provider_loaded) => + fun(_Req, Md) -> + {ok, #{hooks => [#{name => <<"message.publish">>}]}, Md} + end + }. with_connection(Fun) -> - {ok, C} = emqtt:start_link([{host, "localhost"}, - {port, 1883}, - {username, <<"admin">>}, - {clientid, <<"exhook_tester">>}]), + {ok, C} = emqtt:start_link([ + {host, "localhost"}, + {port, 1883}, + {username, <<"admin">>}, + {clientid, <<"exhook_tester">>} + ]), {ok, _} = emqtt:connect(C), try Fun(C) - catch Type:Error:Trace -> + catch + Type:Error:Trace -> emqtt:stop(C), erlang:raise(Type, Error, Trace) end. diff --git a/apps/emqx_exhook/test/props/prop_exhook_hooks.erl b/apps/emqx_exhook/test/props/prop_exhook_hooks.erl index 84999e4a8..9e0660681 100644 --- a/apps/emqx_exhook/test/props/prop_exhook_hooks.erl +++ b/apps/emqx_exhook/test/props/prop_exhook_hooks.erl @@ -19,363 +19,440 @@ -include_lib("proper/include/proper.hrl"). -include_lib("eunit/include/eunit.hrl"). --import(emqx_proper_types, - [ conninfo/0 - , clientinfo/0 - , sessioninfo/0 - , message/0 - , connack_return_code/0 - , topictab/0 - , topic/0 - , subopts/0 - ]). - --define(CONF_DEFAULT, <<" -exhook { - servers = - [ { name = default, - url = \"http://127.0.0.1:9000\" - } +-import( + emqx_proper_types, + [ + conninfo/0, + clientinfo/0, + sessioninfo/0, + message/0, + connack_return_code/0, + topictab/0, + topic/0, + subopts/0 ] -} -">>). +). + +-define(CONF_DEFAULT, << + "\n" + "exhook {\n" + " servers =\n" + " [ { name = default,\n" + " url = \"http://127.0.0.1:9000\"\n" + " }\n" + " ]\n" + "}\n" +>>). -define(ALL(Vars, Types, Exprs), - ?SETUP(fun() -> + ?SETUP( + fun() -> State = do_setup(), fun() -> do_teardown(State) end - end, ?FORALL(Vars, Types, Exprs))). - + end, + ?FORALL(Vars, Types, Exprs) + ) +). %%-------------------------------------------------------------------- %% Properties %%-------------------------------------------------------------------- prop_client_connect() -> - ?ALL({ConnInfo, ConnProps}, - {conninfo(), conn_properties()}, - begin - ok = emqx_hooks:run('client.connect', [ConnInfo, ConnProps]), - {'on_client_connect', Resp} = emqx_exhook_demo_svr:take(), - Expected = - #{props => properties(ConnProps), - conninfo => from_conninfo(ConnInfo) + ?ALL( + {ConnInfo, ConnProps}, + {conninfo(), conn_properties()}, + begin + ok = emqx_hooks:run('client.connect', [ConnInfo, ConnProps]), + {'on_client_connect', Resp} = emqx_exhook_demo_svr:take(), + Expected = + #{ + props => properties(ConnProps), + conninfo => from_conninfo(ConnInfo) }, - ?assertEqual(Expected, Resp), - true - end). + ?assertEqual(Expected, Resp), + true + end + ). prop_client_connack() -> - ?ALL({ConnInfo, Rc, AckProps}, - {conninfo(), connack_return_code(), ack_properties()}, + ?ALL( + {ConnInfo, Rc, AckProps}, + {conninfo(), connack_return_code(), ack_properties()}, begin ok = emqx_hooks:run('client.connack', [ConnInfo, Rc, AckProps]), {'on_client_connack', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{props => properties(AckProps), - result_code => atom_to_binary(Rc, utf8), - conninfo => from_conninfo(ConnInfo) - }, + #{ + props => properties(AckProps), + result_code => atom_to_binary(Rc, utf8), + conninfo => from_conninfo(ConnInfo) + }, ?assertEqual(Expected, Resp), true - end). + end + ). prop_client_authenticate() -> - ?ALL({ClientInfo0, AuthResult}, - {clientinfo(), authresult()}, + ?ALL( + {ClientInfo0, AuthResult}, + {clientinfo(), authresult()}, begin ClientInfo = inject_magic_into(username, ClientInfo0), OutAuthResult = emqx_hooks:run_fold('client.authenticate', [ClientInfo], AuthResult), - ExpectedAuthResult = case maps:get(username, ClientInfo) of - <<"baduser">> -> {error, not_authorized}; - <<"gooduser">> -> ok; - <<"normaluser">> -> ok; - _ -> case AuthResult of - ok -> ok; - _ -> {error, not_authorized} - end - end, + ExpectedAuthResult = + case maps:get(username, ClientInfo) of + <<"baduser">> -> + {error, not_authorized}; + <<"gooduser">> -> + ok; + <<"normaluser">> -> + ok; + _ -> + case AuthResult of + ok -> ok; + _ -> {error, not_authorized} + end + end, ?assertEqual(ExpectedAuthResult, OutAuthResult), {'on_client_authenticate', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{result => authresult_to_bool(AuthResult), - clientinfo => from_clientinfo(ClientInfo) - }, + #{ + result => authresult_to_bool(AuthResult), + clientinfo => from_clientinfo(ClientInfo) + }, ?assertEqual(Expected, Resp), true - end). + end + ). prop_client_authorize() -> - ?ALL({ClientInfo0, PubSub, Topic, Result}, - {clientinfo(), oneof([publish, subscribe]), - topic(), oneof([allow, deny])}, + ?ALL( + {ClientInfo0, PubSub, Topic, Result}, + {clientinfo(), oneof([publish, subscribe]), topic(), oneof([allow, deny])}, begin ClientInfo = inject_magic_into(username, ClientInfo0), OutResult = emqx_hooks:run_fold( - 'client.authorize', - [ClientInfo, PubSub, Topic], - Result), - ExpectedOutResult = case maps:get(username, ClientInfo) of - <<"baduser">> -> deny; - <<"gooduser">> -> allow; - <<"normaluser">> -> allow; - _ -> Result - end, + 'client.authorize', + [ClientInfo, PubSub, Topic], + Result + ), + ExpectedOutResult = + case maps:get(username, ClientInfo) of + <<"baduser">> -> deny; + <<"gooduser">> -> allow; + <<"normaluser">> -> allow; + _ -> Result + end, ?assertEqual(ExpectedOutResult, OutResult), {'on_client_authorize', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{result => aclresult_to_bool(Result), - type => pubsub_to_enum(PubSub), - topic => Topic, - clientinfo => from_clientinfo(ClientInfo) - }, + #{ + result => aclresult_to_bool(Result), + type => pubsub_to_enum(PubSub), + topic => Topic, + clientinfo => from_clientinfo(ClientInfo) + }, ?assertEqual(Expected, Resp), true - end). + end + ). prop_client_connected() -> - ?ALL({ClientInfo, ConnInfo}, - {clientinfo(), conninfo()}, + ?ALL( + {ClientInfo, ConnInfo}, + {clientinfo(), conninfo()}, begin ok = emqx_hooks:run('client.connected', [ClientInfo, ConnInfo]), {'on_client_connected', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{clientinfo => from_clientinfo(ClientInfo) - }, + #{clientinfo => from_clientinfo(ClientInfo)}, ?assertEqual(Expected, Resp), true - end). + end + ). prop_client_disconnected() -> - ?ALL({ClientInfo, Reason, ConnInfo}, - {clientinfo(), shutdown_reason(), conninfo()}, + ?ALL( + {ClientInfo, Reason, ConnInfo}, + {clientinfo(), shutdown_reason(), conninfo()}, begin ok = emqx_hooks:run('client.disconnected', [ClientInfo, Reason, ConnInfo]), {'on_client_disconnected', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{reason => stringfy(Reason), - clientinfo => from_clientinfo(ClientInfo) - }, + #{ + reason => stringfy(Reason), + clientinfo => from_clientinfo(ClientInfo) + }, ?assertEqual(Expected, Resp), true - end). + end + ). prop_client_subscribe() -> - ?ALL({ClientInfo, SubProps, TopicTab}, - {clientinfo(), sub_properties(), topictab()}, + ?ALL( + {ClientInfo, SubProps, TopicTab}, + {clientinfo(), sub_properties(), topictab()}, begin ok = emqx_hooks:run('client.subscribe', [ClientInfo, SubProps, TopicTab]), {'on_client_subscribe', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{props => properties(SubProps), - topic_filters => topicfilters(TopicTab), - clientinfo => from_clientinfo(ClientInfo) - }, + #{ + props => properties(SubProps), + topic_filters => topicfilters(TopicTab), + clientinfo => from_clientinfo(ClientInfo) + }, ?assertEqual(Expected, Resp), true - end). + end + ). prop_client_unsubscribe() -> - ?ALL({ClientInfo, UnSubProps, TopicTab}, - {clientinfo(), unsub_properties(), topictab()}, + ?ALL( + {ClientInfo, UnSubProps, TopicTab}, + {clientinfo(), unsub_properties(), topictab()}, begin ok = emqx_hooks:run('client.unsubscribe', [ClientInfo, UnSubProps, TopicTab]), {'on_client_unsubscribe', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{props => properties(UnSubProps), - topic_filters => topicfilters(TopicTab), - clientinfo => from_clientinfo(ClientInfo) - }, + #{ + props => properties(UnSubProps), + topic_filters => topicfilters(TopicTab), + clientinfo => from_clientinfo(ClientInfo) + }, ?assertEqual(Expected, Resp), true - end). + end + ). prop_session_created() -> - ?ALL({ClientInfo, SessInfo}, {clientinfo(), sessioninfo()}, + ?ALL( + {ClientInfo, SessInfo}, + {clientinfo(), sessioninfo()}, begin ok = emqx_hooks:run('session.created', [ClientInfo, SessInfo]), {'on_session_created', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{clientinfo => from_clientinfo(ClientInfo) - }, - ?assertEqual(Expected, Resp), + #{clientinfo => from_clientinfo(ClientInfo)}, + ?assertEqual(Expected, Resp), true - end). + end + ). prop_session_subscribed() -> - ?ALL({ClientInfo, Topic, SubOpts}, - {clientinfo(), topic(), subopts()}, + ?ALL( + {ClientInfo, Topic, SubOpts}, + {clientinfo(), topic(), subopts()}, begin ok = emqx_hooks:run('session.subscribed', [ClientInfo, Topic, SubOpts]), {'on_session_subscribed', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{topic => Topic, - subopts => subopts(SubOpts), - clientinfo => from_clientinfo(ClientInfo) - }, + #{ + topic => Topic, + subopts => subopts(SubOpts), + clientinfo => from_clientinfo(ClientInfo) + }, ?assertEqual(Expected, Resp), true - end). + end + ). prop_session_unsubscribed() -> - ?ALL({ClientInfo, Topic, SubOpts}, - {clientinfo(), topic(), subopts()}, + ?ALL( + {ClientInfo, Topic, SubOpts}, + {clientinfo(), topic(), subopts()}, begin ok = emqx_hooks:run('session.unsubscribed', [ClientInfo, Topic, SubOpts]), {'on_session_unsubscribed', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{topic => Topic, - clientinfo => from_clientinfo(ClientInfo) - }, + #{ + topic => Topic, + clientinfo => from_clientinfo(ClientInfo) + }, ?assertEqual(Expected, Resp), true - end). + end + ). prop_session_resumed() -> - ?ALL({ClientInfo, SessInfo}, {clientinfo(), sessioninfo()}, + ?ALL( + {ClientInfo, SessInfo}, + {clientinfo(), sessioninfo()}, begin ok = emqx_hooks:run('session.resumed', [ClientInfo, SessInfo]), {'on_session_resumed', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{clientinfo => from_clientinfo(ClientInfo) - }, + #{clientinfo => from_clientinfo(ClientInfo)}, ?assertEqual(Expected, Resp), true - end). + end + ). prop_session_discared() -> - ?ALL({ClientInfo, SessInfo}, {clientinfo(), sessioninfo()}, + ?ALL( + {ClientInfo, SessInfo}, + {clientinfo(), sessioninfo()}, begin ok = emqx_hooks:run('session.discarded', [ClientInfo, SessInfo]), {'on_session_discarded', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{clientinfo => from_clientinfo(ClientInfo) - }, + #{clientinfo => from_clientinfo(ClientInfo)}, ?assertEqual(Expected, Resp), true - end). + end + ). prop_session_takenover() -> - ?ALL({ClientInfo, SessInfo}, {clientinfo(), sessioninfo()}, + ?ALL( + {ClientInfo, SessInfo}, + {clientinfo(), sessioninfo()}, begin ok = emqx_hooks:run('session.takenover', [ClientInfo, SessInfo]), {'on_session_takenover', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{clientinfo => from_clientinfo(ClientInfo) - }, + #{clientinfo => from_clientinfo(ClientInfo)}, ?assertEqual(Expected, Resp), true - end). + end + ). prop_session_terminated() -> - ?ALL({ClientInfo, Reason, SessInfo}, - {clientinfo(), shutdown_reason(), sessioninfo()}, + ?ALL( + {ClientInfo, Reason, SessInfo}, + {clientinfo(), shutdown_reason(), sessioninfo()}, begin ok = emqx_hooks:run('session.terminated', [ClientInfo, Reason, SessInfo]), {'on_session_terminated', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{reason => stringfy(Reason), - clientinfo => from_clientinfo(ClientInfo) - }, + #{ + reason => stringfy(Reason), + clientinfo => from_clientinfo(ClientInfo) + }, ?assertEqual(Expected, Resp), true - end). + end + ). prop_message_publish() -> - ?ALL(Msg0, message(), + ?ALL( + Msg0, + message(), begin Msg = emqx_message:from_map( - inject_magic_into(from, emqx_message:to_map(Msg0))), - OutMsg= emqx_hooks:run_fold('message.publish', [], Msg), + inject_magic_into(from, emqx_message:to_map(Msg0)) + ), + OutMsg = emqx_hooks:run_fold('message.publish', [], Msg), case emqx_topic:match(emqx_message:topic(Msg), <<"$SYS/#">>) of true -> ?assertEqual(Msg, OutMsg), skip; _ -> - ExpectedOutMsg = case emqx_message:from(Msg) of - <<"baduser">> -> - MsgMap = #{headers := Headers} - = emqx_message:to_map(Msg), - emqx_message:from_map( - MsgMap#{qos => 0, - topic => <<"">>, - payload => <<"">>, - headers => maps:put(allow_publish, false, Headers) - }); - <<"gooduser">> = From -> - MsgMap = #{headers := Headers} - = emqx_message:to_map(Msg), - emqx_message:from_map( - MsgMap#{topic => From, - payload => From, - headers => maps:put(allow_publish, true, Headers) - }); - _ -> - Msg - end, + ExpectedOutMsg = + case emqx_message:from(Msg) of + <<"baduser">> -> + MsgMap = + #{headers := Headers} = + emqx_message:to_map(Msg), + emqx_message:from_map( + MsgMap#{ + qos => 0, + topic => <<"">>, + payload => <<"">>, + headers => maps:put(allow_publish, false, Headers) + } + ); + <<"gooduser">> = From -> + MsgMap = + #{headers := Headers} = + emqx_message:to_map(Msg), + emqx_message:from_map( + MsgMap#{ + topic => From, + payload => From, + headers => maps:put(allow_publish, true, Headers) + } + ); + _ -> + Msg + end, ?assertEqual(ExpectedOutMsg, OutMsg), {'on_message_publish', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{message => from_message(Msg) - }, + #{message => from_message(Msg)}, ?assertEqual(Expected, Resp) end, true - end). + end + ). prop_message_dropped() -> - ?ALL({Msg, By, Reason}, {message(), hardcoded, shutdown_reason()}, + ?ALL( + {Msg, By, Reason}, + {message(), hardcoded, shutdown_reason()}, begin ok = emqx_hooks:run('message.dropped', [Msg, By, Reason]), case emqx_topic:match(emqx_message:topic(Msg), <<"$SYS/#">>) of - true -> skip; + true -> + skip; _ -> {'on_message_dropped', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{reason => stringfy(Reason), - message => from_message(Msg) - }, + #{ + reason => stringfy(Reason), + message => from_message(Msg) + }, ?assertEqual(Expected, Resp) end, true - end). + end + ). prop_message_delivered() -> - ?ALL({ClientInfo, Msg}, {clientinfo(), message()}, + ?ALL( + {ClientInfo, Msg}, + {clientinfo(), message()}, begin ok = emqx_hooks:run('message.delivered', [ClientInfo, Msg]), case emqx_topic:match(emqx_message:topic(Msg), <<"$SYS/#">>) of - true -> skip; + true -> + skip; _ -> {'on_message_delivered', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{clientinfo => from_clientinfo(ClientInfo), - message => from_message(Msg) - }, + #{ + clientinfo => from_clientinfo(ClientInfo), + message => from_message(Msg) + }, ?assertEqual(Expected, Resp) end, true - end). + end + ). prop_message_acked() -> - ?ALL({ClientInfo, Msg}, {clientinfo(), message()}, + ?ALL( + {ClientInfo, Msg}, + {clientinfo(), message()}, begin ok = emqx_hooks:run('message.acked', [ClientInfo, Msg]), case emqx_topic:match(emqx_message:topic(Msg), <<"$SYS/#">>) of - true -> skip; + true -> + skip; _ -> {'on_message_acked', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{clientinfo => from_clientinfo(ClientInfo), - message => from_message(Msg) - }, + #{ + clientinfo => from_clientinfo(ClientInfo), + message => from_message(Msg) + }, ?assertEqual(Expected, Resp) end, true - end). + end + ). nodestr() -> stringfy(node()). @@ -388,7 +465,7 @@ sockport(#{sockname := {_, Port}}) -> %% copied from emqx_exhook -ntoa({0,0,0,0,0,16#ffff,AB,CD}) -> +ntoa({0, 0, 0, 0, 0, 16#ffff, AB, CD}) -> list_to_binary(inet_parse:ntoa({AB bsr 8, AB rem 256, CD bsr 8, CD rem 256})); ntoa(IP) -> list_to_binary(inet_parse:ntoa(IP)). @@ -396,12 +473,22 @@ ntoa(IP) -> maybe(undefined) -> <<>>; maybe(B) -> B. -properties(undefined) -> []; +properties(undefined) -> + []; properties(M) when is_map(M) -> - maps:fold(fun(K, V, Acc) -> - [#{name => stringfy(K), - value => stringfy(V)} | Acc] - end, [], M). + maps:fold( + fun(K, V, Acc) -> + [ + #{ + name => stringfy(K), + value => stringfy(V) + } + | Acc + ] + end, + [], + M + ). topicfilters(Tfs) when is_list(Tfs) -> [#{name => Topic, qos => Qos} || {Topic, #{qos := Qos}} <- Tfs]. @@ -417,12 +504,13 @@ stringfy(Term) -> unicode:characters_to_binary((io_lib:format("~0p", [Term]))). subopts(SubOpts) -> - #{qos => maps:get(qos, SubOpts, 0), - rh => maps:get(rh, SubOpts, 0), - rap => maps:get(rap, SubOpts, 0), - nl => maps:get(nl, SubOpts, 0), - share => maps:get(share, SubOpts, <<>>) - }. + #{ + qos => maps:get(qos, SubOpts, 0), + rh => maps:get(rh, SubOpts, 0), + rap => maps:get(rap, SubOpts, 0), + nl => maps:get(nl, SubOpts, 0), + share => maps:get(share, SubOpts, <<>>) + }. authresult_to_bool(AuthResult) -> AuthResult == ok. @@ -434,42 +522,46 @@ pubsub_to_enum(publish) -> 'PUBLISH'; pubsub_to_enum(subscribe) -> 'SUBSCRIBE'. from_conninfo(ConnInfo) -> - #{node => nodestr(), - clientid => maps:get(clientid, ConnInfo), - username => maybe(maps:get(username, ConnInfo, <<>>)), - peerhost => peerhost(ConnInfo), - sockport => sockport(ConnInfo), - proto_name => maps:get(proto_name, ConnInfo), - proto_ver => stringfy(maps:get(proto_ver, ConnInfo)), - keepalive => maps:get(keepalive, ConnInfo) - }. + #{ + node => nodestr(), + clientid => maps:get(clientid, ConnInfo), + username => maybe(maps:get(username, ConnInfo, <<>>)), + peerhost => peerhost(ConnInfo), + sockport => sockport(ConnInfo), + proto_name => maps:get(proto_name, ConnInfo), + proto_ver => stringfy(maps:get(proto_ver, ConnInfo)), + keepalive => maps:get(keepalive, ConnInfo) + }. from_clientinfo(ClientInfo) -> - #{node => nodestr(), - clientid => maps:get(clientid, ClientInfo), - username => maybe(maps:get(username, ClientInfo, <<>>)), - password => maybe(maps:get(password, ClientInfo, <<>>)), - peerhost => ntoa(maps:get(peerhost, ClientInfo)), - sockport => maps:get(sockport, ClientInfo), - protocol => stringfy(maps:get(protocol, ClientInfo)), - mountpoint => maybe(maps:get(mountpoint, ClientInfo, <<>>)), - is_superuser => maps:get(is_superuser, ClientInfo, false), - anonymous => maps:get(anonymous, ClientInfo, true), - cn => maybe(maps:get(cn, ClientInfo, <<>>)), - dn => maybe(maps:get(dn, ClientInfo, <<>>)) + #{ + node => nodestr(), + clientid => maps:get(clientid, ClientInfo), + username => maybe(maps:get(username, ClientInfo, <<>>)), + password => maybe(maps:get(password, ClientInfo, <<>>)), + peerhost => ntoa(maps:get(peerhost, ClientInfo)), + sockport => maps:get(sockport, ClientInfo), + protocol => stringfy(maps:get(protocol, ClientInfo)), + mountpoint => maybe(maps:get(mountpoint, ClientInfo, <<>>)), + is_superuser => maps:get(is_superuser, ClientInfo, false), + anonymous => maps:get(anonymous, ClientInfo, true), + cn => maybe(maps:get(cn, ClientInfo, <<>>)), + dn => maybe(maps:get(dn, ClientInfo, <<>>)) }. from_message(Msg) -> - #{node => nodestr(), - id => emqx_guid:to_hexstr(emqx_message:id(Msg)), - qos => emqx_message:qos(Msg), - from => stringfy(emqx_message:from(Msg)), - topic => emqx_message:topic(Msg), - payload => emqx_message:payload(Msg), - timestamp => emqx_message:timestamp(Msg), - headers => emqx_exhook_handler:headers( - emqx_message:get_headers(Msg)) - }. + #{ + node => nodestr(), + id => emqx_guid:to_hexstr(emqx_message:id(Msg)), + qos => emqx_message:qos(Msg), + from => stringfy(emqx_message:from(Msg)), + topic => emqx_message:topic(Msg), + payload => emqx_message:payload(Msg), + timestamp => emqx_message:timestamp(Msg), + headers => emqx_exhook_handler:headers( + emqx_message:get_headers(Msg) + ) + }. %%-------------------------------------------------------------------- %% Helper @@ -513,17 +605,19 @@ shutdown_reason() -> oneof([utf8(), {shutdown, emqx_proper_types:limited_atom()}]). authresult() -> - ?LET(RC, connack_return_code(), - case RC of - success -> ok; - _ -> {error, RC} - end). + ?LET( + RC, + connack_return_code(), + case RC of + success -> ok; + _ -> {error, RC} + end + ). inject_magic_into(Key, Object) -> case castspell() of muggles -> Object; - Spell -> - Object#{Key => Spell} + Spell -> Object#{Key => Spell} end. castspell() ->