diff --git a/apps/emqx_exhook/priv/protos/exhook.proto b/apps/emqx_exhook/priv/protos/exhook.proto index 6f6b860af..27ce25661 100644 --- a/apps/emqx_exhook/priv/protos/exhook.proto +++ b/apps/emqx_exhook/priv/protos/exhook.proto @@ -148,6 +148,9 @@ message ClientAuthorizeRequest { AuthorizeReqType type = 2; + // In ClientAuthorizeRequest. + // Only "real-topic" will be serialized in gRPC request when shared-sub. + // For example, when client subscribes to `$share/group/t/1`, the real topic is `t/1`. string topic = 3; bool result = 4; @@ -456,7 +459,14 @@ message TopicFilter { string name = 1; - uint32 qos = 2; + // Deprecated + // Since EMQX 5.4.0, we have deprecated the 'qos' field in the `TopicFilter` structure. + // A new field named 'subopts,' has been added to encompass all subscription options. + // Please see the `SubOpts` structure for details. + reserved 2; + reserved "qos"; + + SubOpts subopts = 3; } message SubOpts { @@ -464,11 +474,20 @@ message SubOpts { // The QoS level uint32 qos = 1; - // deprecated + // Deprecated reserved 2; reserved "share"; - // The group name for shared subscription - // string share = 2; + // Since EMQX 5.4.0, we have deprecated the 'share' field in the `SubOpts` structure. + // The group name of shared subscription will be serialized with topic. + // hooks: + // "client.subscribe": + // ClientSubscribeRequest.TopicFilter.name = "$share/group/topic/1" + // "client.unsubscribe": + // ClientUnsubscribeRequest.TopicFilter.name = "$share/group/topic/1" + // "session.subscribed": + // SessionSubscribedRequest.topic = "$share/group/topic/1" + // "session.unsubscribed": + // SessionUnsubscribedRequest.topic = "$share/group/topic/1" // The Retain Handling option (MQTT v5.0) // diff --git a/apps/emqx_exhook/src/emqx_exhook_handler.erl b/apps/emqx_exhook/src/emqx_exhook_handler.erl index f3dfa111c..fe6af653b 100644 --- a/apps/emqx_exhook/src/emqx_exhook_handler.erl +++ b/apps/emqx_exhook/src/emqx_exhook_handler.erl @@ -192,7 +192,7 @@ on_session_subscribed(ClientInfo, Topic, SubOpts) -> Req = #{ clientinfo => clientinfo(ClientInfo), topic => emqx_topic:maybe_format_share(Topic), - subopts => maps:with([qos, rh, rap, nl], SubOpts) + subopts => subopts(SubOpts) }, cast('session.subscribed', Req). @@ -200,6 +200,7 @@ on_session_unsubscribed(ClientInfo, Topic, _SubOpts) -> Req = #{ clientinfo => clientinfo(ClientInfo), topic => emqx_topic:maybe_format_share(Topic) + %% no subopts when unsub }, cast('session.unsubscribed', Req). @@ -416,14 +417,19 @@ enrich_header(Headers, Message) -> end. topicfilters(Tfs) when is_list(Tfs) -> - GetQos = fun(SubOpts) -> - maps:get(qos, SubOpts, 0) - end, [ - #{name => emqx_topic:maybe_format_share(Topic), qos => GetQos(SubOpts)} + #{name => emqx_topic:maybe_format_share(Topic), subopts => subopts(SubOpts)} || {Topic, SubOpts} <- Tfs ]. +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) + }. + 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) -> diff --git a/apps/emqx_exhook/test/props/prop_exhook_hooks.erl b/apps/emqx_exhook/test/props/prop_exhook_hooks.erl index 827205d1d..041091e27 100644 --- a/apps/emqx_exhook/test/props/prop_exhook_hooks.erl +++ b/apps/emqx_exhook/test/props/prop_exhook_hooks.erl @@ -530,7 +530,10 @@ properties(M) when is_map(M) -> ). topicfilters(Tfs) when is_list(Tfs) -> - [#{name => Topic, qos => Qos} || {Topic, #{qos := Qos}} <- Tfs]. + [ + #{name => emqx_topic:maybe_format_share(Topic), subopts => subopts(SubOpts)} + || {Topic, SubOpts} <- Tfs + ]. %% @private stringfy(Term) when is_binary(Term) ->