From 9c48b016a93fbb3c1604703a41f0490d21957920 Mon Sep 17 00:00:00 2001 From: Ilya Averyanov Date: Thu, 18 May 2023 19:33:44 +0300 Subject: [PATCH 01/18] fix(evacuation): handle expire interval correctly --- apps/emqx/include/emqx_channel.hrl | 2 ++ apps/emqx/src/emqx_channel.erl | 2 +- apps/emqx/src/emqx_cm.erl | 1 + .../emqx/src/persistent_session/emqx_persistent_session.erl | 6 ++---- apps/emqx_eviction_agent/src/emqx_eviction_agent.app.src | 2 +- .../emqx_eviction_agent/src/emqx_eviction_agent_channel.erl | 4 ++-- apps/emqx_eviction_agent/test/emqx_eviction_agent_SUITE.erl | 6 +++--- .../test/emqx_eviction_agent_channel_SUITE.erl | 3 ++- 8 files changed, 14 insertions(+), 12 deletions(-) diff --git a/apps/emqx/include/emqx_channel.hrl b/apps/emqx/include/emqx_channel.hrl index d4362633a..be2448a20 100644 --- a/apps/emqx/include/emqx_channel.hrl +++ b/apps/emqx/include/emqx_channel.hrl @@ -40,3 +40,5 @@ session, will_msg ]). + +-define(EXPIRE_INTERVAL_INFINITE, 4294967295000). diff --git a/apps/emqx/src/emqx_channel.erl b/apps/emqx/src/emqx_channel.erl index 69e0a55f7..d5a9051f2 100644 --- a/apps/emqx/src/emqx_channel.erl +++ b/apps/emqx/src/emqx_channel.erl @@ -2064,7 +2064,7 @@ maybe_resume_session(#channel{ maybe_shutdown(Reason, Channel = #channel{conninfo = ConnInfo}) -> case maps:get(expiry_interval, ConnInfo) of - ?UINT_MAX -> + ?EXPIRE_INTERVAL_INFINITE -> {ok, Channel}; I when I > 0 -> {ok, ensure_timer(expire_timer, I, Channel)}; diff --git a/apps/emqx/src/emqx_cm.erl b/apps/emqx/src/emqx_cm.erl index 66c1db36e..f01b29a8c 100644 --- a/apps/emqx/src/emqx_cm.erl +++ b/apps/emqx/src/emqx_cm.erl @@ -769,6 +769,7 @@ mark_channel_connected(ChanPid) -> mark_channel_disconnected(ChanPid) -> ?tp(emqx_cm_connected_client_count_dec, #{chan_pid => ChanPid}), ets:delete(?CHAN_LIVE_TAB, ChanPid), + ?tp(emqx_cm_connected_client_count_dec_done, #{chan_pid => ChanPid}), ok. get_connected_client_count() -> diff --git a/apps/emqx/src/persistent_session/emqx_persistent_session.erl b/apps/emqx/src/persistent_session/emqx_persistent_session.erl index 68f783283..3e9e00c81 100644 --- a/apps/emqx/src/persistent_session/emqx_persistent_session.erl +++ b/apps/emqx/src/persistent_session/emqx_persistent_session.erl @@ -60,14 +60,12 @@ -export_type([sess_msg_key/0]). -include("emqx.hrl"). +-include("emqx_channel.hrl"). -include("emqx_persistent_session.hrl"). -include_lib("snabbkaffe/include/snabbkaffe.hrl"). -compile({inline, [is_store_enabled/0]}). -%% 16#FFFFFFFF * 1000 --define(MAX_EXPIRY_INTERVAL, 4294967295000). - %% NOTE: Order is significant because of traversal order of the table. -define(MARKER, 3). -define(DELIVERED, 2). @@ -424,7 +422,7 @@ pending(SessionID, MarkerIds) -> %% @private [MQTT-3.1.2-23] persistent_session_status(#session_store{expiry_interval = 0}) -> not_persistent; -persistent_session_status(#session_store{expiry_interval = ?MAX_EXPIRY_INTERVAL}) -> +persistent_session_status(#session_store{expiry_interval = ?EXPIRE_INTERVAL_INFINITE}) -> persistent; persistent_session_status(#session_store{expiry_interval = E, ts = TS}) -> case E + TS > erlang:system_time(millisecond) of diff --git a/apps/emqx_eviction_agent/src/emqx_eviction_agent.app.src b/apps/emqx_eviction_agent/src/emqx_eviction_agent.app.src index 239d9052e..7e6cf5b95 100644 --- a/apps/emqx_eviction_agent/src/emqx_eviction_agent.app.src +++ b/apps/emqx_eviction_agent/src/emqx_eviction_agent.app.src @@ -1,6 +1,6 @@ {application, emqx_eviction_agent, [ {description, "EMQX Eviction Agent"}, - {vsn, "5.0.0"}, + {vsn, "5.0.1"}, {registered, [ emqx_eviction_agent_sup, emqx_eviction_agent, diff --git a/apps/emqx_eviction_agent/src/emqx_eviction_agent_channel.erl b/apps/emqx_eviction_agent/src/emqx_eviction_agent_channel.erl index a6097f03d..1369ee969 100644 --- a/apps/emqx_eviction_agent/src/emqx_eviction_agent_channel.erl +++ b/apps/emqx_eviction_agent/src/emqx_eviction_agent_channel.erl @@ -218,10 +218,10 @@ cancel_expiry_timer(_) -> set_expiry_timer(#{conninfo := ConnInfo} = Channel) -> case maps:get(expiry_interval, ConnInfo) of - ?UINT_MAX -> + ?EXPIRE_INTERVAL_INFINITE -> {ok, Channel}; I when I > 0 -> - Timer = erlang:send_after(timer:seconds(I), self(), expire_session), + Timer = erlang:send_after(I, self(), expire_session), {ok, Channel#{expiry_timer => Timer}}; _ -> {error, should_be_expired} diff --git a/apps/emqx_eviction_agent/test/emqx_eviction_agent_SUITE.erl b/apps/emqx_eviction_agent/test/emqx_eviction_agent_SUITE.erl index 22b694d77..0f1b948c7 100644 --- a/apps/emqx_eviction_agent/test/emqx_eviction_agent_SUITE.erl +++ b/apps/emqx_eviction_agent/test/emqx_eviction_agent_SUITE.erl @@ -177,7 +177,7 @@ t_explicit_session_takeover(Config) -> ?assert(false, "Connection not evicted") end end, - #{?snk_kind := emqx_cm_connected_client_count_dec, chan_pid := ChanPid}, + #{?snk_kind := emqx_cm_connected_client_count_dec_done, chan_pid := ChanPid}, 2000 ), @@ -383,7 +383,7 @@ t_ws_conn(_Config) -> ?assertWaitEvent( ok = emqx_eviction_agent:evict_connections(1), - #{?snk_kind := emqx_cm_connected_client_count_dec}, + #{?snk_kind := emqx_cm_connected_client_count_dec_done}, 1000 ), @@ -418,7 +418,7 @@ t_quic_conn(_Config) -> ?assertWaitEvent( ok = emqx_eviction_agent:evict_connections(1), - #{?snk_kind := emqx_cm_connected_client_count_dec}, + #{?snk_kind := emqx_cm_connected_client_count_dec_done}, 1000 ), diff --git a/apps/emqx_eviction_agent/test/emqx_eviction_agent_channel_SUITE.erl b/apps/emqx_eviction_agent/test/emqx_eviction_agent_channel_SUITE.erl index 3b7ef6672..4ace80893 100644 --- a/apps/emqx_eviction_agent/test/emqx_eviction_agent_channel_SUITE.erl +++ b/apps/emqx_eviction_agent/test/emqx_eviction_agent_channel_SUITE.erl @@ -10,6 +10,7 @@ -include_lib("eunit/include/eunit.hrl"). -include_lib("common_test/include/ct.hrl"). -include_lib("emqx/include/emqx_mqtt.hrl"). +-include_lib("emqx/include/emqx_channel.hrl"). -define(CLIENT_ID, <<"client_with_session">>). @@ -101,7 +102,7 @@ t_start_infinite_expire(_Config) -> conninfo => #{ clientid => ?CLIENT_ID, receive_maximum => 32, - expiry_interval => ?UINT_MAX + expiry_interval => ?EXPIRE_INTERVAL_INFINITE } }, ?assertMatch( From 65f973044f928dc78a929aaa847c2beecff0fd7a Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Fri, 19 May 2023 16:09:15 -0300 Subject: [PATCH 02/18] feat(pulsar): improve authn error check time and add connect timeout Fixes https://emqx.atlassian.net/browse/EMQX-9910 --- apps/emqx_bridge_pulsar/rebar.config | 2 +- .../src/emqx_bridge_pulsar.erl | 8 +++++ .../src/emqx_bridge_pulsar_impl_producer.erl | 29 +++++++++++++++++-- rel/i18n/emqx_bridge_pulsar.hocon | 5 ++++ rel/i18n/zh/emqx_bridge_pulsar.hocon | 5 ++++ 5 files changed, 46 insertions(+), 3 deletions(-) diff --git a/apps/emqx_bridge_pulsar/rebar.config b/apps/emqx_bridge_pulsar/rebar.config index d5a63f320..c77007b93 100644 --- a/apps/emqx_bridge_pulsar/rebar.config +++ b/apps/emqx_bridge_pulsar/rebar.config @@ -2,7 +2,7 @@ {erl_opts, [debug_info]}. {deps, [ - {pulsar, {git, "https://github.com/emqx/pulsar-client-erl.git", {tag, "0.8.1"}}}, + {pulsar, {git, "https://github.com/emqx/pulsar-client-erl.git", {tag, "0.8.2"}}}, {emqx_connector, {path, "../../apps/emqx_connector"}}, {emqx_resource, {path, "../../apps/emqx_resource"}}, {emqx_bridge, {path, "../../apps/emqx_bridge"}} diff --git a/apps/emqx_bridge_pulsar/src/emqx_bridge_pulsar.erl b/apps/emqx_bridge_pulsar/src/emqx_bridge_pulsar.erl index 7d1b20d24..721937cd2 100644 --- a/apps/emqx_bridge_pulsar/src/emqx_bridge_pulsar.erl +++ b/apps/emqx_bridge_pulsar/src/emqx_bridge_pulsar.erl @@ -57,6 +57,14 @@ fields(config) -> sensitive => true, desc => ?DESC("authentication") } + )}, + {connect_timeout, + mk( + emqx_schema:duration_ms(), + #{ + default => <<"5s">>, + desc => ?DESC("connect_timeout") + } )} ] ++ emqx_connector_schema_lib:ssl_fields(); fields(producer_opts) -> diff --git a/apps/emqx_bridge_pulsar/src/emqx_bridge_pulsar_impl_producer.erl b/apps/emqx_bridge_pulsar/src/emqx_bridge_pulsar_impl_producer.erl index 59956e1b6..5ed706511 100644 --- a/apps/emqx_bridge_pulsar/src/emqx_bridge_pulsar_impl_producer.erl +++ b/apps/emqx_bridge_pulsar/src/emqx_bridge_pulsar_impl_producer.erl @@ -48,6 +48,7 @@ memory_overload_protection := boolean() }, compression := compression_mode(), + connect_timeout := emqx_schema:duration_ms(), max_batch_bytes := emqx_schema:bytesize(), message := message_template_raw(), pulsar_topic := binary(), @@ -81,7 +82,9 @@ on_start(InstanceId, Config) -> Servers = format_servers(Servers0), ClientId = make_client_id(InstanceId, BridgeName), SSLOpts = emqx_tls_lib:to_client_opts(SSL), + ConnectTimeout = maps:get(connect_timeout, Config, timer:seconds(5)), ClientOpts = #{ + connect_timeout => ConnectTimeout, ssl_opts => SSLOpts, conn_opts => conn_opts(Config) }, @@ -96,13 +99,19 @@ on_start(InstanceId, Config) -> } ); {error, Reason} -> + RedactedReason = emqx_utils:redact(Reason, fun is_sensitive_key/1), ?SLOG(error, #{ msg => "failed_to_start_pulsar_client", instance_id => InstanceId, pulsar_hosts => Servers, - reason => emqx_utils:redact(Reason, fun is_sensitive_key/1) + reason => RedactedReason }), - throw(failed_to_start_pulsar_client) + Message = + case get_error_message(RedactedReason) of + {ok, Msg} -> Msg; + error -> failed_to_start_pulsar_client + end, + throw(Message) end, start_producer(Config, InstanceId, ClientId, ClientOpts). @@ -422,3 +431,19 @@ partition_strategy(Strategy) -> Strategy. is_sensitive_key(auth_data) -> true; is_sensitive_key(_) -> false. + +get_error_message({BrokerErrorMap, _}) when is_map(BrokerErrorMap) -> + Iter = maps:iterator(BrokerErrorMap), + do_get_error_message(Iter); +get_error_message(_Error) -> + error. + +do_get_error_message(Iter) -> + case maps:next(Iter) of + {{_Broker, _Port}, #{message := Message}, _NIter} -> + {ok, Message}; + {_K, _V, NIter} -> + do_get_error_message(NIter); + none -> + error + end. diff --git a/rel/i18n/emqx_bridge_pulsar.hocon b/rel/i18n/emqx_bridge_pulsar.hocon index 92294bb75..d1f5c8b13 100644 --- a/rel/i18n/emqx_bridge_pulsar.hocon +++ b/rel/i18n/emqx_bridge_pulsar.hocon @@ -67,6 +67,11 @@ emqx_bridge_pulsar { label = "Enable or Disable" } + connect_timeout { + desc = "Maximum wait time for TCP connection establishment (including authentication time if enabled)." + label = "Connect Timeout" + } + desc_name { desc = "Bridge name, used as a human-readable description of the bridge." label = "Bridge Name" diff --git a/rel/i18n/zh/emqx_bridge_pulsar.hocon b/rel/i18n/zh/emqx_bridge_pulsar.hocon index 23643060b..3e0ba95c5 100644 --- a/rel/i18n/zh/emqx_bridge_pulsar.hocon +++ b/rel/i18n/zh/emqx_bridge_pulsar.hocon @@ -20,6 +20,11 @@ emqx_bridge_pulsar { label = "启用或停用" } + connect_timeout { + desc = "建立 TCP 连接时的最大等待时长(若启用认证,这个等待时长也包含完成认证所需时间)。" + label = "连接超时" + } + servers { desc = "以逗号分隔的 scheme://host[:port] 格式的 Pulsar URL 列表," "支持的 scheme 有 pulsar:// (默认)" From 0877e4296aba320d4f0381f6986925916cebc63c Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Mon, 22 May 2023 11:35:04 -0300 Subject: [PATCH 03/18] fix(dashboard): add missing function clause Example error: https://github.com/emqx/emqx/actions/runs/5045715277/jobs/9052482682#step:8:294 The previous change was not equivalent to the previous `maps:with/2` behavior. --- apps/emqx_dashboard/src/emqx_dashboard.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/emqx_dashboard/src/emqx_dashboard.erl b/apps/emqx_dashboard/src/emqx_dashboard.erl index aec811e5d..364853eec 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard.erl @@ -192,7 +192,9 @@ ranch_opts(Options) -> RanchOpts#{socket_opts => InetOpts ++ SocketOpts}. proto_opts(#{proxy_header := ProxyHeader}) -> - #{proxy_header => ProxyHeader}. + #{proxy_header => ProxyHeader}; +proto_opts(_Opts) -> + #{}. filter_false(_K, false, S) -> S; filter_false(K, V, S) -> [{K, V} | S]. From 67a30ee9761e83e763b6bcf2e031a1e093c8077e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=90=E6=96=87?= Date: Tue, 23 May 2023 10:16:43 +0800 Subject: [PATCH 04/18] test: bridge test init dashboard listener failed --- apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl b/apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl index 27c1c779a..49ddd19bd 100644 --- a/apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl +++ b/apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl @@ -161,7 +161,7 @@ init_node(Type) -> primary -> ok = emqx_config:put( [dashboard, listeners], - #{http => #{enable => true, bind => 18083}} + #{http => #{enable => true, bind => 18083}, proxy_header => false} ), ok = emqx_dashboard:start_listeners(), ready = emqx_dashboard_listener:regenerate_minirest_dispatch(), From d19ddb1832fbc7c15e900e9f047ed6097e369518 Mon Sep 17 00:00:00 2001 From: Kjell Winblad Date: Mon, 22 May 2023 13:32:49 +0200 Subject: [PATCH 05/18] fix: IoTDB bridge incoming payload needs to be parsed as JSON There was an incorrect assumption that the data incoming to the IoTDB bridge has already been parsed. This is fixed by parsing the payload as JSON data if the payload is not already a map. Fixes: https://emqx.atlassian.net/browse/EMQX-9854 --- .../src/emqx_bridge_iotdb.app.src | 2 +- .../src/emqx_bridge_iotdb_impl.erl | 61 +++++++++++++------ 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb.app.src b/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb.app.src index 9c5108307..cebf60cb1 100644 --- a/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb.app.src +++ b/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb.app.src @@ -1,7 +1,7 @@ %% -*- mode: erlang -*- {application, emqx_bridge_iotdb, [ {description, "EMQX Enterprise Apache IoTDB Bridge"}, - {vsn, "0.1.0"}, + {vsn, "0.1.1"}, {modules, [ emqx_bridge_iotdb, emqx_bridge_iotdb_impl diff --git a/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl b/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl index 2f8794560..416e19a3a 100644 --- a/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl +++ b/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl @@ -143,24 +143,42 @@ on_query_async(InstanceId, {send_message, Message}, ReplyFunAndArgs0, State) -> %% Internal Functions %%-------------------------------------------------------------------- -preproc_data(DataList) -> +make_parsed_payload(PayloadUnparsed) when is_binary(PayloadUnparsed) -> + emqx_utils_json:decode(PayloadUnparsed, [return_maps]); +make_parsed_payload(PayloadUnparsed) when is_list(PayloadUnparsed) -> + lists:map(fun make_parsed_payload/1, PayloadUnparsed); +make_parsed_payload( + #{ + measurement := Measurement, + data_type := DataType, + value := Value + } = Data +) -> + Data#{ + <<"measurement">> => Measurement, + <<"data_type">> => DataType, + <<"value">> => Value + }. + +preproc_data( + #{ + <<"measurement">> := Measurement, + <<"data_type">> := DataType, + <<"value">> := Value + } = Data +) -> + #{ + timestamp => emqx_plugin_libs_rule:preproc_tmpl( + maps:get(<<"timestamp">>, Data, <<"now">>) + ), + measurement => emqx_plugin_libs_rule:preproc_tmpl(Measurement), + data_type => DataType, + value => emqx_plugin_libs_rule:preproc_tmpl(Value) + }. + +preproc_data_list(DataList) -> lists:map( - fun( - #{ - measurement := Measurement, - data_type := DataType, - value := Value - } = Data - ) -> - #{ - timestamp => emqx_plugin_libs_rule:preproc_tmpl( - maps:get(<<"timestamp">>, Data, <<"now">>) - ), - measurement => emqx_plugin_libs_rule:preproc_tmpl(Measurement), - data_type => DataType, - value => emqx_plugin_libs_rule:preproc_tmpl(Value) - } - end, + fun preproc_data/1, DataList ). @@ -258,12 +276,15 @@ convert_float(Str) when is_binary(Str) -> convert_float(undefined) -> null. -make_iotdb_insert_request(Message, State) -> +make_iotdb_insert_request(MessageUnparsedPayload, State) -> + PayloadUnparsed = maps:get(payload, MessageUnparsedPayload), + PayloadParsed = make_parsed_payload(PayloadUnparsed), + Message = MessageUnparsedPayload#{payload => PayloadParsed}, IsAligned = maps:get(is_aligned, State, false), DeviceId = device_id(Message, State), IotDBVsn = maps:get(iotdb_version, State, ?VSN_1_0_X), Payload = make_list(maps:get(payload, Message)), - PreProcessedData = preproc_data(Payload), + PreProcessedData = preproc_data_list(Payload), DataList = proc_data(PreProcessedData, Message), InitAcc = #{timestamps => [], measurements => [], dtypes => [], values => []}, Rows = replace_dtypes(aggregate_rows(DataList, InitAcc), IotDBVsn), @@ -350,6 +371,8 @@ device_id(Message, State) -> case maps:get(device_id, State, undefined) of undefined -> case maps:get(payload, Message) of + #{<<"device_id">> := DeviceId} -> + DeviceId; #{device_id := DeviceId} -> DeviceId; _NotFound -> From 89ea1646bedacd18e4fe5991874a5db435ab3d7d Mon Sep 17 00:00:00 2001 From: Kjell Winblad Date: Tue, 23 May 2023 10:57:05 +0200 Subject: [PATCH 06/18] fix: IoTDB name for version option The previous name for the version option was v1.0.x which is clearly wrong since this option was tested against IoTDB version v1.1.0. This commit fixes this by renaming the option to v1.x. Fixes: https://emqx.atlassian.net/browse/EMQX-9926 --- apps/emqx_bridge_iotdb/include/emqx_bridge_iotdb.hrl | 2 +- apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb.erl | 6 +++--- apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/emqx_bridge_iotdb/include/emqx_bridge_iotdb.hrl b/apps/emqx_bridge_iotdb/include/emqx_bridge_iotdb.hrl index 5e6bf9ac5..5d693547a 100644 --- a/apps/emqx_bridge_iotdb/include/emqx_bridge_iotdb.hrl +++ b/apps/emqx_bridge_iotdb/include/emqx_bridge_iotdb.hrl @@ -5,7 +5,7 @@ -ifndef(EMQX_BRIDGE_IOTDB_HRL). -define(EMQX_BRIDGE_IOTDB_HRL, true). --define(VSN_1_0_X, 'v1.0.x'). +-define(VSN_1_X, 'v1.x'). -define(VSN_0_13_X, 'v0.13.x'). -endif. diff --git a/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb.erl b/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb.erl index 90e8d18a4..aa2c32589 100644 --- a/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb.erl +++ b/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb.erl @@ -109,10 +109,10 @@ basic_config() -> )}, {iotdb_version, mk( - hoconsc:enum([?VSN_1_0_X, ?VSN_0_13_X]), + hoconsc:enum([?VSN_1_X, ?VSN_0_13_X]), #{ desc => ?DESC("config_iotdb_version"), - default => ?VSN_1_0_X + default => ?VSN_1_X } )} ] ++ resource_creation_opts() ++ @@ -217,7 +217,7 @@ conn_bridge_example(_Method, Type) -> is_aligned => false, device_id => <<"my_device">>, base_url => <<"http://iotdb.local:18080/">>, - iotdb_version => ?VSN_1_0_X, + iotdb_version => ?VSN_1_X, connect_timeout => <<"15s">>, pool_type => <<"random">>, pool_size => 8, diff --git a/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl b/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl index 416e19a3a..9014fde0b 100644 --- a/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl +++ b/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl @@ -282,7 +282,7 @@ make_iotdb_insert_request(MessageUnparsedPayload, State) -> Message = MessageUnparsedPayload#{payload => PayloadParsed}, IsAligned = maps:get(is_aligned, State, false), DeviceId = device_id(Message, State), - IotDBVsn = maps:get(iotdb_version, State, ?VSN_1_0_X), + IotDBVsn = maps:get(iotdb_version, State, ?VSN_1_X), Payload = make_list(maps:get(payload, Message)), PreProcessedData = preproc_data_list(Payload), DataList = proc_data(PreProcessedData, Message), @@ -351,15 +351,15 @@ insert_value(1, Data, [Value | Values]) -> insert_value(Index, Data, [Value | Values]) -> [[null | Value] | insert_value(Index - 1, Data, Values)]. -iotdb_field_key(is_aligned, ?VSN_1_0_X) -> +iotdb_field_key(is_aligned, ?VSN_1_X) -> <<"is_aligned">>; iotdb_field_key(is_aligned, ?VSN_0_13_X) -> <<"isAligned">>; -iotdb_field_key(device_id, ?VSN_1_0_X) -> +iotdb_field_key(device_id, ?VSN_1_X) -> <<"device">>; iotdb_field_key(device_id, ?VSN_0_13_X) -> <<"deviceId">>; -iotdb_field_key(data_types, ?VSN_1_0_X) -> +iotdb_field_key(data_types, ?VSN_1_X) -> <<"data_types">>; iotdb_field_key(data_types, ?VSN_0_13_X) -> <<"dataTypes">>. From 29e0e41ec7e862a876cdcc08b24565322aa64ba0 Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Tue, 23 May 2023 09:08:55 -0300 Subject: [PATCH 07/18] docs: improve descriptions --- rel/i18n/zh/emqx_bridge_pulsar.hocon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rel/i18n/zh/emqx_bridge_pulsar.hocon b/rel/i18n/zh/emqx_bridge_pulsar.hocon index 3e0ba95c5..4e2fd5c9f 100644 --- a/rel/i18n/zh/emqx_bridge_pulsar.hocon +++ b/rel/i18n/zh/emqx_bridge_pulsar.hocon @@ -22,7 +22,7 @@ emqx_bridge_pulsar { connect_timeout { desc = "建立 TCP 连接时的最大等待时长(若启用认证,这个等待时长也包含完成认证所需时间)。" - label = "连接超时" + label = "连接超时时间" } servers { From 91150e6d831d93ab508078769dc9c789fe7ba2c6 Mon Sep 17 00:00:00 2001 From: "Zaiming (Stone) Shi" Date: Mon, 22 May 2023 23:07:47 +0200 Subject: [PATCH 08/18] chore: allow special chars in log dir the current valdiator does not allow space and ':' in log file path which is an unresonable restriction --- apps/emqx_conf/src/emqx_conf.app.src | 2 +- apps/emqx_conf/src/emqx_conf_schema.erl | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/apps/emqx_conf/src/emqx_conf.app.src b/apps/emqx_conf/src/emqx_conf.app.src index e6c3d9cd9..c31a16b9b 100644 --- a/apps/emqx_conf/src/emqx_conf.app.src +++ b/apps/emqx_conf/src/emqx_conf.app.src @@ -1,6 +1,6 @@ {application, emqx_conf, [ {description, "EMQX configuration management"}, - {vsn, "0.1.19"}, + {vsn, "0.1.20"}, {registered, []}, {mod, {emqx_conf_app, []}}, {applications, [kernel, stdlib, emqx_ctl]}, diff --git a/apps/emqx_conf/src/emqx_conf_schema.erl b/apps/emqx_conf/src/emqx_conf_schema.erl index 94cbfb221..76213c690 100644 --- a/apps/emqx_conf/src/emqx_conf_schema.erl +++ b/apps/emqx_conf/src/emqx_conf_schema.erl @@ -893,8 +893,7 @@ fields("log_file_handler") -> #{ desc => ?DESC("log_file_handler_file"), default => <<"${EMQX_LOG_DIR}/emqx.log">>, - converter => fun emqx_schema:naive_env_interpolation/1, - validator => fun validate_file_location/1 + converter => fun emqx_schema:naive_env_interpolation/1 } )}, {"rotation", @@ -1333,11 +1332,6 @@ emqx_schema_high_prio_roots() -> )}, lists:keyreplace("authorization", 1, Roots, Authz). -validate_file_location(File) -> - ValidFile = "^[/\\_a-zA-Z0-9\\.\\-]*$", - Error = "Invalid file name: " ++ ValidFile, - validator_string_re(File, ValidFile, Error). - validate_time_offset(Offset) -> ValidTimeOffset = "^([\\-\\+][0-1][0-9]:[0-6][0-9]|system|utc)$", Error = From 2fa5b511bf90b62ed5cd1ca21c6fb3ac0a4f05fa Mon Sep 17 00:00:00 2001 From: "Zaiming (Stone) Shi" Date: Tue, 23 May 2023 10:36:41 +0200 Subject: [PATCH 09/18] chore: hide stale config --- apps/emqx/src/emqx_schema.erl | 3 ++- rel/i18n/emqx_schema.hocon | 3 --- rel/i18n/zh/emqx_schema.hocon | 3 --- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/apps/emqx/src/emqx_schema.erl b/apps/emqx/src/emqx_schema.erl index dfeae6d64..80894319b 100644 --- a/apps/emqx/src/emqx_schema.erl +++ b/apps/emqx/src/emqx_schema.erl @@ -1540,7 +1540,8 @@ fields("broker") -> boolean(), #{ default => true, - desc => ?DESC(broker_route_batch_clean) + desc => "This config is stale since 4.3", + importance => ?IMPORTANCE_HIDDEN } )}, {"perf", diff --git a/rel/i18n/emqx_schema.hocon b/rel/i18n/emqx_schema.hocon index af26d2fdb..5d6977b47 100644 --- a/rel/i18n/emqx_schema.hocon +++ b/rel/i18n/emqx_schema.hocon @@ -213,9 +213,6 @@ pending connections can grow to.""" fields_tcp_opts_backlog.label: """TCP backlog length""" -broker_route_batch_clean.desc: -"""Enable batch clean for deleted routes.""" - fields_mqtt_quic_listener_initial_window_packets.desc: """The size (in packets) of the initial congestion window for a connection. Default: 10""" diff --git a/rel/i18n/zh/emqx_schema.hocon b/rel/i18n/zh/emqx_schema.hocon index 835372868..0e329eac9 100644 --- a/rel/i18n/zh/emqx_schema.hocon +++ b/rel/i18n/zh/emqx_schema.hocon @@ -208,9 +208,6 @@ fields_tcp_opts_backlog.desc: fields_tcp_opts_backlog.label: """TCP 连接队列长度""" -broker_route_batch_clean.desc: -"""是否开启批量清除路由。""" - fields_mqtt_quic_listener_initial_window_packets.desc: """一个连接的初始拥堵窗口的大小(以包为单位)。默认值:10""" From 47a3096776cd301a6278c637a6733d1b08c36b28 Mon Sep 17 00:00:00 2001 From: "Zaiming (Stone) Shi" Date: Tue, 23 May 2023 12:55:13 +0200 Subject: [PATCH 10/18] test: add test case for file path validation --- apps/emqx_conf/src/emqx_conf_schema.erl | 27 +++++++++++- .../emqx_conf/test/emqx_conf_schema_tests.erl | 44 +++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/apps/emqx_conf/src/emqx_conf_schema.erl b/apps/emqx_conf/src/emqx_conf_schema.erl index 76213c690..b37c3f71e 100644 --- a/apps/emqx_conf/src/emqx_conf_schema.erl +++ b/apps/emqx_conf/src/emqx_conf_schema.erl @@ -508,6 +508,7 @@ fields("node") -> desc => ?DESC(node_crash_dump_file), default => crash_dump_file_default(), importance => ?IMPORTANCE_HIDDEN, + converter => fun ensure_unicode_path/2, 'readOnly' => true } )}, @@ -754,6 +755,7 @@ fields("rpc") -> file(), #{ mapping => "gen_rpc.certfile", + converter => fun ensure_unicode_path/2, desc => ?DESC(rpc_certfile) } )}, @@ -762,6 +764,7 @@ fields("rpc") -> file(), #{ mapping => "gen_rpc.keyfile", + converter => fun ensure_unicode_path/2, desc => ?DESC(rpc_keyfile) } )}, @@ -770,6 +773,7 @@ fields("rpc") -> file(), #{ mapping => "gen_rpc.cacertfile", + converter => fun ensure_unicode_path/2, desc => ?DESC(rpc_cacertfile) } )}, @@ -892,8 +896,10 @@ fields("log_file_handler") -> file(), #{ desc => ?DESC("log_file_handler_file"), - default => <<"${EMQX_LOG_DIR}/emqx.log">>, - converter => fun emqx_schema:naive_env_interpolation/1 + converter => fun(Path, Opts) -> + emqx_schema:naive_env_interpolation(ensure_unicode_path(Path, Opts)) + end, + default => <<"${EMQX_LOG_DIR}/emqx.log">> } )}, {"rotation", @@ -1349,3 +1355,20 @@ validator_string_re(Val, RE, Error) -> node_array() -> hoconsc:union([emqx_schema:comma_separated_atoms(), hoconsc:array(atom())]). + +ensure_unicode_path(undefined, _) -> + undefined; +ensure_unicode_path(Path, #{make_serializable := true}) -> + %% format back to serializable string + unicode:characters_to_binary(Path, utf8); +ensure_unicode_path(Path, Opts) when is_binary(Path) -> + case unicode:characters_to_list(Path, utf8) of + {R, _, _} when R =:= error orelse R =:= incomplete -> + throw({"bad_file_path_string", Path}); + PathStr -> + ensure_unicode_path(PathStr, Opts) + end; +ensure_unicode_path(Path, _) when is_list(Path) -> + Path; +ensure_unicode_path(Path, _) -> + throw({"not_string", Path}). diff --git a/apps/emqx_conf/test/emqx_conf_schema_tests.erl b/apps/emqx_conf/test/emqx_conf_schema_tests.erl index 06784e32d..4eaf3db6b 100644 --- a/apps/emqx_conf/test/emqx_conf_schema_tests.erl +++ b/apps/emqx_conf/test/emqx_conf_schema_tests.erl @@ -232,3 +232,47 @@ ensure_acl_conf() -> true -> ok; false -> file:write_file(File, <<"">>) end. + +log_path_test_() -> + Fh = fun(Path) -> + #{<<"log">> => #{<<"file_handlers">> => #{<<"name1">> => #{<<"file">> => Path}}}} + end, + Assert = fun(Name, Path, Conf) -> + ?assertMatch(#{log := #{file_handlers := #{Name := #{file := Path}}}}, Conf) + end, + + [ + {"default-values", fun() -> Assert(default, "log/emqx.log", check(#{})) end}, + {"file path with space", fun() -> Assert(name1, "a /b", check(Fh(<<"a /b">>))) end}, + {"windows", fun() -> Assert(name1, "c:\\a\\ b\\", check(Fh(<<"c:\\a\\ b\\">>))) end}, + {"unicoded", fun() -> Assert(name1, "路 径", check(Fh(<<"路 径"/utf8>>))) end}, + {"bad utf8", fun() -> + ?assertThrow( + {emqx_conf_schema, [ + #{ + kind := validation_error, + reason := {"bad_file_path_string", _} + } + ]}, + check(Fh(<<239, 32, 132, 47, 117, 116, 102, 56>>)) + ) + end}, + {"not string", fun() -> + ?assertThrow( + {emqx_conf_schema, [ + #{ + kind := validation_error, + reason := {"not_string", _} + } + ]}, + check(Fh(#{<<"foo">> => <<"bar">>})) + ) + end} + ]. + +check(Config) -> + Schema = emqx_conf_schema, + {_, Conf} = hocon_tconf:map(Schema, Config, [log], #{ + atom_key => false, required => false, format => map + }), + emqx_utils_maps:unsafe_atom_key_map(Conf). From ea2fea77d795a8ba0e24cfd37aa59f684b676a15 Mon Sep 17 00:00:00 2001 From: Kjell Winblad Date: Tue, 23 May 2023 15:47:09 +0200 Subject: [PATCH 11/18] fix: RabbitMQ field not marked as required This commit makes sure that the RabbitMQ password filed is marked as required. This ensures that the user provides a password and that the bridge does not throw a function clause exception if the password filed is not set. Fixes: https://emqx.atlassian.net/browse/EMQX-9974 --- .../src/emqx_bridge_rabbitmq.app.src | 2 +- .../src/emqx_bridge_rabbitmq_connector.erl | 2 +- apps/emqx_connector/src/emqx_connector_schema_lib.erl | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/emqx_bridge_rabbitmq/src/emqx_bridge_rabbitmq.app.src b/apps/emqx_bridge_rabbitmq/src/emqx_bridge_rabbitmq.app.src index 36f47aaf6..2b572a98c 100644 --- a/apps/emqx_bridge_rabbitmq/src/emqx_bridge_rabbitmq.app.src +++ b/apps/emqx_bridge_rabbitmq/src/emqx_bridge_rabbitmq.app.src @@ -1,6 +1,6 @@ {application, emqx_bridge_rabbitmq, [ {description, "EMQX Enterprise RabbitMQ Bridge"}, - {vsn, "0.1.0"}, + {vsn, "0.1.1"}, {registered, []}, {applications, [kernel, stdlib, ecql, rabbit_common, amqp_client]}, {env, []}, diff --git a/apps/emqx_bridge_rabbitmq/src/emqx_bridge_rabbitmq_connector.erl b/apps/emqx_bridge_rabbitmq/src/emqx_bridge_rabbitmq_connector.erl index 6f833d659..3e809d99c 100644 --- a/apps/emqx_bridge_rabbitmq/src/emqx_bridge_rabbitmq_connector.erl +++ b/apps/emqx_bridge_rabbitmq/src/emqx_bridge_rabbitmq_connector.erl @@ -72,7 +72,7 @@ fields(config) -> desc => ?DESC("username") } )}, - {password, fun emqx_connector_schema_lib:password/1}, + {password, fun emqx_connector_schema_lib:password_required/1}, {pool_size, hoconsc:mk( typerefl:pos_integer(), diff --git a/apps/emqx_connector/src/emqx_connector_schema_lib.erl b/apps/emqx_connector/src/emqx_connector_schema_lib.erl index f64208311..a277fe8c8 100644 --- a/apps/emqx_connector/src/emqx_connector_schema_lib.erl +++ b/apps/emqx_connector/src/emqx_connector_schema_lib.erl @@ -30,6 +30,7 @@ database/1, username/1, password/1, + password_required/1, auto_reconnect/1 ]). @@ -104,6 +105,14 @@ password(sensitive) -> true; password(converter) -> fun emqx_schema:password_converter/2; password(_) -> undefined. +password_required(type) -> binary(); +password_required(desc) -> ?DESC("password"); +password_required(required) -> true; +password_required(format) -> <<"password">>; +password_required(sensitive) -> true; +password_required(converter) -> fun emqx_schema:password_converter/2; +password_required(_) -> undefined. + auto_reconnect(type) -> boolean(); auto_reconnect(desc) -> ?DESC("auto_reconnect"); auto_reconnect(default) -> true; From 63180c87be875e651c3781b742715cbb4c2d9a16 Mon Sep 17 00:00:00 2001 From: Kjell Winblad Date: Tue, 23 May 2023 18:53:14 +0200 Subject: [PATCH 12/18] style: simplify code for better readability Co-authored-by: Thales Macedo Garitezi --- apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl b/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl index 9014fde0b..8331e715f 100644 --- a/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl +++ b/apps/emqx_bridge_iotdb/src/emqx_bridge_iotdb_impl.erl @@ -277,9 +277,7 @@ convert_float(undefined) -> null. make_iotdb_insert_request(MessageUnparsedPayload, State) -> - PayloadUnparsed = maps:get(payload, MessageUnparsedPayload), - PayloadParsed = make_parsed_payload(PayloadUnparsed), - Message = MessageUnparsedPayload#{payload => PayloadParsed}, + Message = maps:update_with(payload, fun make_parsed_payload/1, MessageUnparsedPayload), IsAligned = maps:get(is_aligned, State, false), DeviceId = device_id(Message, State), IotDBVsn = maps:get(iotdb_version, State, ?VSN_1_X), From 25437adf02d23eae9c09eb7fb3324d6289bcf598 Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Tue, 23 May 2023 14:06:40 -0300 Subject: [PATCH 13/18] fix(webhook): fix empty ehttpc worker list clause on health check (r5.0) Fixes https://emqx.atlassian.net/browse/EMQX-9970 --- apps/emqx_connector/src/emqx_connector_http.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/emqx_connector/src/emqx_connector_http.erl b/apps/emqx_connector/src/emqx_connector_http.erl index bb822a60a..4a9cb345f 100644 --- a/apps/emqx_connector/src/emqx_connector_http.erl +++ b/apps/emqx_connector/src/emqx_connector_http.erl @@ -416,7 +416,9 @@ resolve_pool_worker(#{pool_name := PoolName} = State, Key) -> on_get_status(_InstId, #{pool_name := PoolName, connect_timeout := Timeout} = State) -> case do_get_status(PoolName, Timeout) of ok -> - {connected, State}; + connected; + {error, still_connecting} -> + connecting; {error, Reason} -> {disconnected, State, Reason} end. @@ -438,7 +440,8 @@ do_get_status(PoolName, Timeout) -> end end, try emqx_utils:pmap(DoPerWorker, Workers, Timeout) of - % we crash in case of non-empty lists since we don't know what to do in that case + [] -> + {error, still_connecting}; [_ | _] = Results -> case [E || {error, _} = E <- Results] of [] -> From b5f63f4151d69817b942757e173bda56aba5999a Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Tue, 23 May 2023 14:50:19 -0300 Subject: [PATCH 14/18] test: fix wrong test listeners option --- apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl b/apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl index 49ddd19bd..86f088855 100644 --- a/apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl +++ b/apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl @@ -161,7 +161,7 @@ init_node(Type) -> primary -> ok = emqx_config:put( [dashboard, listeners], - #{http => #{enable => true, bind => 18083}, proxy_header => false} + #{http => #{enable => true, bind => 18083, proxy_header => false}} ), ok = emqx_dashboard:start_listeners(), ready = emqx_dashboard_listener:regenerate_minirest_dispatch(), From 44ed6a19ee1f41f23d130ec674bb13ca9a51daf6 Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Tue, 23 May 2023 11:22:48 -0300 Subject: [PATCH 15/18] fix(bridge): pass resource option `request_timeout = infinity` along to buffer workers (r5.0) --- apps/emqx_bridge/src/emqx_bridge.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/emqx_bridge/src/emqx_bridge.erl b/apps/emqx_bridge/src/emqx_bridge.erl index 3aade0369..1fbd6902e 100644 --- a/apps/emqx_bridge/src/emqx_bridge.erl +++ b/apps/emqx_bridge/src/emqx_bridge.erl @@ -211,7 +211,7 @@ send_message(BridgeId, Message) -> query_opts(Config) -> case emqx_utils_maps:deep_get([resource_opts, request_timeout], Config, false) of - Timeout when is_integer(Timeout) -> + Timeout when is_integer(Timeout) orelse Timeout =:= infinity -> %% request_timeout is configured #{timeout => Timeout}; _ -> From e38645aa282064073fe48bcc39e3bbd77d860d29 Mon Sep 17 00:00:00 2001 From: JimMoen Date: Wed, 24 May 2023 17:13:18 +0800 Subject: [PATCH 16/18] fix: lookup topic without force percent decode * the minirest handler would do it --- apps/emqx_management/src/emqx_mgmt_api_topics.erl | 4 ++-- changes/ce/fix-10801.en.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changes/ce/fix-10801.en.md diff --git a/apps/emqx_management/src/emqx_mgmt_api_topics.erl b/apps/emqx_management/src/emqx_mgmt_api_topics.erl index 6b0e1f622..d451261ff 100644 --- a/apps/emqx_management/src/emqx_mgmt_api_topics.erl +++ b/apps/emqx_management/src/emqx_mgmt_api_topics.erl @@ -139,9 +139,9 @@ lookup(#{topic := Topic}) -> %%%============================================================================================== %% internal generate_topic(Params = #{<<"topic">> := Topic}) -> - Params#{<<"topic">> => uri_string:percent_decode(Topic)}; + Params#{<<"topic">> => Topic}; generate_topic(Params = #{topic := Topic}) -> - Params#{topic => uri_string:percent_decode(Topic)}; + Params#{topic => Topic}; generate_topic(Params) -> Params. diff --git a/changes/ce/fix-10801.en.md b/changes/ce/fix-10801.en.md new file mode 100644 index 000000000..4c36bd528 --- /dev/null +++ b/changes/ce/fix-10801.en.md @@ -0,0 +1 @@ +Avoid duplicated percent decode the topic name in API `/topics/{topic}` and `/topics`. From 1ac140312d48a3b1248abcf595b339e26475075e Mon Sep 17 00:00:00 2001 From: JimMoen Date: Wed, 24 May 2023 17:39:25 +0800 Subject: [PATCH 17/18] test: lookup topic with percent encoded topic name --- .../test/emqx_mgmt_api_topics_SUITE.erl | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/apps/emqx_management/test/emqx_mgmt_api_topics_SUITE.erl b/apps/emqx_management/test/emqx_mgmt_api_topics_SUITE.erl index 659ae0d44..e617c6dcb 100644 --- a/apps/emqx_management/test/emqx_mgmt_api_topics_SUITE.erl +++ b/apps/emqx_management/test/emqx_mgmt_api_topics_SUITE.erl @@ -92,4 +92,35 @@ t_nodes_api(Config) -> #{<<"topic">> := Topic, <<"node">> := Node2} ] = emqx_utils_json:decode(RouteResponse, [return_maps]), - ?assertEqual(lists:usort([Node, atom_to_binary(Slave)]), lists:usort([Node1, Node2])). + ?assertEqual(lists:usort([Node, atom_to_binary(Slave)]), lists:usort([Node1, Node2])), + + ok = emqtt:stop(Client). + +t_percent_topics(_Config) -> + Node = atom_to_binary(node(), utf8), + Topic = <<"test_%%1">>, + {ok, Client} = emqtt:start_link(#{ + username => <<"routes_username">>, clientid => <<"routes_cid">> + }), + {ok, _} = emqtt:connect(Client), + {ok, _, _} = emqtt:subscribe(Client, Topic), + + %% exact match with percent encoded topic + Path = emqx_mgmt_api_test_util:api_path(["topics"]), + QS = uri_string:compose_query([ + {"topic", Topic}, + {"node", atom_to_list(node())} + ]), + Headers = emqx_mgmt_api_test_util:auth_header_(), + {ok, MatchResponse} = emqx_mgmt_api_test_util:request_api(get, Path, QS, Headers), + MatchData = emqx_utils_json:decode(MatchResponse, [return_maps]), + ?assertMatch( + #{<<"count">> := 1, <<"page">> := 1, <<"limit">> := 100}, + maps:get(<<"meta">>, MatchData) + ), + ?assertMatch( + [#{<<"topic">> := Topic, <<"node">> := Node}], + maps:get(<<"data">>, MatchData) + ), + + ok = emqtt:stop(Client). From 8816da41a6d51b7a46ee3057c05ced8d2353a1b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=90=E6=96=87?= Date: Wed, 24 May 2023 21:06:15 +0800 Subject: [PATCH 18/18] test: log test failed --- .../emqx_conf/test/emqx_conf_schema_tests.erl | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/apps/emqx_conf/test/emqx_conf_schema_tests.erl b/apps/emqx_conf/test/emqx_conf_schema_tests.erl index 0f0cb3de1..32c66fb90 100644 --- a/apps/emqx_conf/test/emqx_conf_schema_tests.erl +++ b/apps/emqx_conf/test/emqx_conf_schema_tests.erl @@ -444,7 +444,7 @@ log_path_test_() -> #{<<"log">> => #{<<"file_handlers">> => #{<<"name1">> => #{<<"file">> => Path}}}} end, Assert = fun(Name, Path, Conf) -> - ?assertMatch(#{log := #{file_handlers := #{Name := #{file := Path}}}}, Conf) + ?assertMatch(#{log := #{file := #{Name := #{to := Path}}}}, Conf) end, [ @@ -457,7 +457,15 @@ log_path_test_() -> {emqx_conf_schema, [ #{ kind := validation_error, - reason := {"bad_file_path_string", _} + mismatches := + #{ + "handler_name" := + #{ + kind := validation_error, + path := "log.file.name1.to", + reason := {"bad_file_path_string", _} + } + } } ]}, check(Fh(<<239, 32, 132, 47, 117, 116, 102, 56>>)) @@ -468,7 +476,15 @@ log_path_test_() -> {emqx_conf_schema, [ #{ kind := validation_error, - reason := {"not_string", _} + mismatches := + #{ + "handler_name" := + #{ + kind := validation_error, + path := "log.file.name1.to", + reason := {"not_string", _} + } + } } ]}, check(Fh(#{<<"foo">> => <<"bar">>}))