feature(emqx_mod_rewrite): separate rewrite rules for pub and sub (#3676)
This commit is contained in:
parent
dbeabf3de0
commit
f47e10e08a
|
@ -1966,8 +1966,8 @@ module.presence.qos = 1
|
||||||
## Rewrite Module
|
## Rewrite Module
|
||||||
|
|
||||||
## {rewrite, Topic, Re, Dest}
|
## {rewrite, Topic, Re, Dest}
|
||||||
## module.rewrite.rule.1 = x/# ^x/y/(.+)$ z/y/$1
|
## module.rewrite.pub.rule.1 = x/# ^x/y/(.+)$ z/y/$1
|
||||||
## module.rewrite.rule.2 = y/+/z/# ^y/(.+)/z/(.+)$ y/z/$2
|
## module.rewrite.sub.rule.1 = y/+/z/# ^y/(.+)/z/(.+)$ y/z/$2
|
||||||
|
|
||||||
##-------------------------------------------------------------------
|
##-------------------------------------------------------------------
|
||||||
## Plugins
|
## Plugins
|
||||||
|
|
|
@ -1945,6 +1945,14 @@ end}.
|
||||||
{datatype, string}
|
{datatype, string}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
|
{mapping, "module.rewrite.pub.rule.$id", "emqx.modules", [
|
||||||
|
{datatype, string}
|
||||||
|
]}.
|
||||||
|
|
||||||
|
{mapping, "module.rewrite.sub.rule.$id", "emqx.modules", [
|
||||||
|
{datatype, string}
|
||||||
|
]}.
|
||||||
|
|
||||||
{translation, "emqx.modules", fun(Conf) ->
|
{translation, "emqx.modules", fun(Conf) ->
|
||||||
Subscriptions = fun() ->
|
Subscriptions = fun() ->
|
||||||
List = cuttlefish_variable:filter_by_prefix("module.subscription", Conf),
|
List = cuttlefish_variable:filter_by_prefix("module.subscription", Conf),
|
||||||
|
@ -1957,10 +1965,16 @@ end}.
|
||||||
end,
|
end,
|
||||||
Rewrites = fun() ->
|
Rewrites = fun() ->
|
||||||
Rules = cuttlefish_variable:filter_by_prefix("module.rewrite.rule", Conf),
|
Rules = cuttlefish_variable:filter_by_prefix("module.rewrite.rule", Conf),
|
||||||
lists:map(fun({[_, "rewrite", "rule", I], Rule}) ->
|
PubRules = cuttlefish_variable:filter_by_prefix("module.rewrite.pub.rule", Conf),
|
||||||
|
SubRules = cuttlefish_variable:filter_by_prefix("module.rewrite.sub.rule", Conf),
|
||||||
|
TotalRules = lists:append(
|
||||||
|
[ {["module", "rewrite", "pub", "rule", I], Rule} || {["module", "rewrite", "rule", I], Rule} <- Rules] ++ PubRules,
|
||||||
|
[ {["module", "rewrite", "sub", "rule", I], Rule} || {["module", "rewrite", "rule", I], Rule} <- Rules] ++ SubRules
|
||||||
|
),
|
||||||
|
lists:map(fun({[_, "rewrite", PubOrSub, "rule", I], Rule}) ->
|
||||||
[Topic, Re, Dest] = string:tokens(Rule, " "),
|
[Topic, Re, Dest] = string:tokens(Rule, " "),
|
||||||
{rewrite, list_to_binary(Topic), list_to_binary(Re), list_to_binary(Dest)}
|
{rewrite, list_to_atom(PubOrSub), list_to_binary(Topic), list_to_binary(Re), list_to_binary(Dest)}
|
||||||
end, Rules)
|
end, TotalRules)
|
||||||
end,
|
end,
|
||||||
lists:append([
|
lists:append([
|
||||||
[{emqx_mod_presence, [{qos, cuttlefish:conf_get("module.presence.qos", Conf, 1)}]}],
|
[{emqx_mod_presence, [{qos, cuttlefish:conf_get("module.presence.qos", Conf, 1)}]}],
|
||||||
|
@ -2165,3 +2179,28 @@ end}.
|
||||||
{size_limit, cuttlefish:conf_get("alarm.size_limit", Conf)},
|
{size_limit, cuttlefish:conf_get("alarm.size_limit", Conf)},
|
||||||
{validity_period, cuttlefish:conf_get("alarm.validity_period", Conf)}]
|
{validity_period, cuttlefish:conf_get("alarm.validity_period", Conf)}]
|
||||||
end}.
|
end}.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Telemetry
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
{mapping, "telemetry.enabled", "emqx.telemetry", [
|
||||||
|
{default, false},
|
||||||
|
{datatype, {enum, [true, false]}}
|
||||||
|
]}.
|
||||||
|
|
||||||
|
{mapping, "telemetry.url", "emqx.telemetry", [
|
||||||
|
{default, "https://telemetry-emqx-io.bigpar.vercel.app/api/telemetry"},
|
||||||
|
{datatype, string}
|
||||||
|
]}.
|
||||||
|
|
||||||
|
{mapping, "telemetry.report_interval", "emqx.telemetry", [
|
||||||
|
{default, "7d"},
|
||||||
|
{datatype, {duration, s}}
|
||||||
|
]}.
|
||||||
|
|
||||||
|
{translation, "emqx.telemetry", fun(Conf) ->
|
||||||
|
[ {enabled, cuttlefish:conf_get("telemetry.enabled", Conf)}
|
||||||
|
, {url, cuttlefish:conf_get("telemetry.url", Conf)}
|
||||||
|
, {report_interval, cuttlefish:conf_get("telemetry.report_interval", Conf)}
|
||||||
|
]
|
||||||
|
end}.
|
||||||
|
|
|
@ -44,10 +44,10 @@
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
load(RawRules) ->
|
load(RawRules) ->
|
||||||
Rules = compile(RawRules),
|
{PubRules, SubRules} = compile(RawRules),
|
||||||
emqx_hooks:add('client.subscribe', {?MODULE, rewrite_subscribe, [Rules]}),
|
emqx_hooks:add('client.subscribe', {?MODULE, rewrite_subscribe, [SubRules]}),
|
||||||
emqx_hooks:add('client.unsubscribe', {?MODULE, rewrite_unsubscribe, [Rules]}),
|
emqx_hooks:add('client.unsubscribe', {?MODULE, rewrite_unsubscribe, [SubRules]}),
|
||||||
emqx_hooks:add('message.publish', {?MODULE, rewrite_publish, [Rules]}).
|
emqx_hooks:add('message.publish', {?MODULE, rewrite_publish, [PubRules]}).
|
||||||
|
|
||||||
rewrite_subscribe(_ClientInfo, _Properties, TopicFilters, Rules) ->
|
rewrite_subscribe(_ClientInfo, _Properties, TopicFilters, Rules) ->
|
||||||
{ok, [{match_and_rewrite(Topic, Rules), Opts} || {Topic, Opts} <- TopicFilters]}.
|
{ok, [{match_and_rewrite(Topic, Rules), Opts} || {Topic, Opts} <- TopicFilters]}.
|
||||||
|
@ -70,10 +70,15 @@ description() ->
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
compile(Rules) ->
|
compile(Rules) ->
|
||||||
lists:map(fun({rewrite, Topic, Re, Dest}) ->
|
PubRules = [ begin
|
||||||
{ok, MP} = re:compile(Re),
|
{ok, MP} = re:compile(Re),
|
||||||
{rewrite, Topic, MP, Dest}
|
{rewrite, Topic, MP, Dest}
|
||||||
end, Rules).
|
end || {rewrite, pub, Topic, Re, Dest}<- Rules ],
|
||||||
|
SubRules = [ begin
|
||||||
|
{ok, MP} = re:compile(Re),
|
||||||
|
{rewrite, Topic, MP, Dest}
|
||||||
|
end || {rewrite, sub, Topic, Re, Dest}<- Rules ],
|
||||||
|
{PubRules, SubRules}.
|
||||||
|
|
||||||
match_and_rewrite(Topic, []) ->
|
match_and_rewrite(Topic, []) ->
|
||||||
Topic;
|
Topic;
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
-include("emqx_mqtt.hrl").
|
-include("emqx_mqtt.hrl").
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
-define(RULES, [{rewrite,<<"x/#">>,<<"^x/y/(.+)$">>,<<"z/y/$1">>},
|
-define(RULES, [{rewrite, pub, <<"x/#">>,<<"^x/y/(.+)$">>,<<"z/y/$1">>},
|
||||||
{rewrite,<<"y/+/z/#">>,<<"^y/(.+)/z/(.+)$">>,<<"y/z/$2">>}
|
{rewrite, sub, <<"y/+/z/#">>,<<"^y/(.+)/z/(.+)$">>,<<"y/z/$2">>}
|
||||||
]).
|
]).
|
||||||
|
|
||||||
all() -> emqx_ct:all(?MODULE).
|
all() -> emqx_ct:all(?MODULE).
|
||||||
|
@ -43,33 +43,43 @@ t_mod_rewrite(_Config) ->
|
||||||
ok = emqx_mod_rewrite:load(?RULES),
|
ok = emqx_mod_rewrite:load(?RULES),
|
||||||
{ok, C} = emqtt:start_link([{clientid, <<"rewrite_client">>}]),
|
{ok, C} = emqtt:start_link([{clientid, <<"rewrite_client">>}]),
|
||||||
{ok, _} = emqtt:connect(C),
|
{ok, _} = emqtt:connect(C),
|
||||||
OrigTopics = [<<"x/y/2">>, <<"x/1/2">>, <<"y/a/z/b">>, <<"y/def">>],
|
PubOrigTopics = [<<"x/y/2">>, <<"x/1/2">>],
|
||||||
DestTopics = [<<"z/y/2">>, <<"x/1/2">>, <<"y/z/b">>, <<"y/def">>],
|
PubDestTopics = [<<"z/y/2">>, <<"x/1/2">>],
|
||||||
%% Subscribe
|
SubOrigTopics = [<<"y/a/z/b">>, <<"y/def">>],
|
||||||
{ok, _Props, _} = emqtt:subscribe(C, [{Topic, ?QOS_1} || Topic <- OrigTopics]),
|
SubDestTopics = [<<"y/z/b">>, <<"y/def">>],
|
||||||
|
%% Sub Rules
|
||||||
|
{ok, _Props, _} = emqtt:subscribe(C, [{Topic, ?QOS_1} || Topic <- SubOrigTopics]),
|
||||||
timer:sleep(100),
|
timer:sleep(100),
|
||||||
Subscriptions = emqx_broker:subscriptions(<<"rewrite_client">>),
|
Subscriptions = emqx_broker:subscriptions(<<"rewrite_client">>),
|
||||||
?assertEqual(DestTopics, [Topic || {Topic, _SubOpts} <- Subscriptions]),
|
?assertEqual(SubDestTopics, [Topic || {Topic, _SubOpts} <- Subscriptions]),
|
||||||
%% Publish
|
RecvTopics1 = [begin
|
||||||
RecvTopics = [begin
|
|
||||||
ok = emqtt:publish(C, Topic, <<"payload">>),
|
ok = emqtt:publish(C, Topic, <<"payload">>),
|
||||||
{ok, #{topic := RecvTopic}} = receive_publish(100),
|
{ok, #{topic := RecvTopic}} = receive_publish(100),
|
||||||
RecvTopic
|
RecvTopic
|
||||||
end || Topic <- OrigTopics],
|
end || Topic <- SubDestTopics],
|
||||||
?assertEqual(DestTopics, RecvTopics),
|
?assertEqual(SubDestTopics, RecvTopics1),
|
||||||
%% Unsubscribe
|
{ok, _, _} = emqtt:unsubscribe(C, SubOrigTopics),
|
||||||
{ok, _, _} = emqtt:unsubscribe(C, OrigTopics),
|
|
||||||
timer:sleep(100),
|
timer:sleep(100),
|
||||||
?assertEqual([], emqx_broker:subscriptions(<<"rewrite_client">>)),
|
?assertEqual([], emqx_broker:subscriptions(<<"rewrite_client">>)),
|
||||||
|
%% Pub Rules
|
||||||
|
{ok, _Props, _} = emqtt:subscribe(C, [{Topic, ?QOS_1} || Topic <- PubDestTopics]),
|
||||||
|
RecvTopics2 = [begin
|
||||||
|
ok = emqtt:publish(C, Topic, <<"payload">>),
|
||||||
|
{ok, #{topic := RecvTopic}} = receive_publish(100),
|
||||||
|
RecvTopic
|
||||||
|
end || Topic <- PubOrigTopics],
|
||||||
|
?assertEqual(PubDestTopics, RecvTopics2),
|
||||||
|
{ok, _, _} = emqtt:unsubscribe(C, PubDestTopics),
|
||||||
|
|
||||||
ok = emqtt:disconnect(C),
|
ok = emqtt:disconnect(C),
|
||||||
ok = emqx_mod_rewrite:unload(?RULES).
|
ok = emqx_mod_rewrite:unload(?RULES).
|
||||||
|
|
||||||
t_rewrite_rule(_Config) ->
|
t_rewrite_rule(_Config) ->
|
||||||
Rules = emqx_mod_rewrite:compile(?RULES),
|
{PubRules, SubRules} = emqx_mod_rewrite:compile(?RULES),
|
||||||
?assertEqual(<<"z/y/2">>, emqx_mod_rewrite:match_and_rewrite(<<"x/y/2">>, Rules)),
|
?assertEqual(<<"z/y/2">>, emqx_mod_rewrite:match_and_rewrite(<<"x/y/2">>, PubRules)),
|
||||||
?assertEqual(<<"x/1/2">>, emqx_mod_rewrite:match_and_rewrite(<<"x/1/2">>, Rules)),
|
?assertEqual(<<"x/1/2">>, emqx_mod_rewrite:match_and_rewrite(<<"x/1/2">>, PubRules)),
|
||||||
?assertEqual(<<"y/z/b">>, emqx_mod_rewrite:match_and_rewrite(<<"y/a/z/b">>, Rules)),
|
?assertEqual(<<"y/z/b">>, emqx_mod_rewrite:match_and_rewrite(<<"y/a/z/b">>, SubRules)),
|
||||||
?assertEqual(<<"y/def">>, emqx_mod_rewrite:match_and_rewrite(<<"y/def">>, Rules)).
|
?assertEqual(<<"y/def">>, emqx_mod_rewrite:match_and_rewrite(<<"y/def">>, SubRules)).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
|
|
Loading…
Reference in New Issue