From ae828e8cfbce3882b3c88a4335e826ea3cde3752 Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Thu, 18 Jul 2024 17:17:11 -0300 Subject: [PATCH] feat(connectors api): add dependent actions and sources to response Fixes https://emqx.atlassian.net/browse/EMQX-12654 --- .../test/emqx_bridge_v2_api_SUITE.erl | 153 +++++++++++++++++- .../emqx_connector/src/emqx_connector_api.erl | 23 ++- .../test/emqx_connector_api_SUITE.erl | 7 +- 3 files changed, 169 insertions(+), 14 deletions(-) diff --git a/apps/emqx_bridge/test/emqx_bridge_v2_api_SUITE.erl b/apps/emqx_bridge/test/emqx_bridge_v2_api_SUITE.erl index 039402738..bf19b364e 100644 --- a/apps/emqx_bridge/test/emqx_bridge_v2_api_SUITE.erl +++ b/apps/emqx_bridge/test/emqx_bridge_v2_api_SUITE.erl @@ -252,23 +252,26 @@ init_per_testcase(TestCase, Config) when BridgeConfig | Config ]; -init_per_testcase(_TestCase, Config) -> +init_per_testcase(TestCase, Config) -> case ?config(cluster_nodes, Config) of undefined -> init_mocks(); Nodes -> [erpc:call(Node, ?MODULE, init_mocks, []) || Node <- Nodes] end, + ShouldCreateConnector = not lists:member(TestCase, skip_connector_creation_test_cases()), case ?config(bridge_kind, Config) of - action -> + action when ShouldCreateConnector -> {ok, 201, _} = request(post, uri(["connectors"]), ?ACTIONS_CONNECTOR, Config); - source -> + source when ShouldCreateConnector -> {ok, 201, _} = request( post, uri(["connectors"]), source_connector_create_config(#{}), Config - ) + ); + _ -> + ok end, Config. @@ -284,6 +287,11 @@ end_per_testcase(_TestCase, Config) -> ok = emqx_common_test_helpers:call_janitor(), ok. +skip_connector_creation_test_cases() -> + [ + t_connector_dependencies + ]. + %%------------------------------------------------------------------------------ %% Helper fns %%------------------------------------------------------------------------------ @@ -500,6 +508,23 @@ source_config_base() -> } }. +mqtt_action_config_base() -> + source_config_base(). + +mqtt_action_create_config(Overrides0) -> + Overrides = emqx_utils_maps:binary_key_map(Overrides0), + Conf0 = maps:merge( + mqtt_action_config_base(), + #{ + <<"enable">> => true, + <<"type">> => ?SOURCE_TYPE + } + ), + emqx_utils_maps:deep_merge( + Conf0, + Overrides + ). + source_create_config(Overrides0) -> Overrides = emqx_utils_maps:binary_key_map(Overrides0), Conf0 = maps:merge( @@ -575,6 +600,32 @@ maybe_get_other_node(Config) -> OtherNode end. +list_connectors_api() -> + Res = emqx_bridge_v2_testlib:list_connectors_http_api(), + emqx_mgmt_api_test_util:simplify_result(Res). + +get_connector_api(Type, Name) -> + Res = emqx_bridge_v2_testlib:get_connector_api(Type, Name), + emqx_mgmt_api_test_util:simplify_result(Res). + +create_source_api(Name, Type, Params) -> + Res = emqx_bridge_v2_testlib:create_kind_api([ + {bridge_kind, source}, + {source_type, Type}, + {source_name, Name}, + {source_config, Params} + ]), + emqx_mgmt_api_test_util:simplify_result(Res). + +create_action_api(Name, Type, Params) -> + Res = emqx_bridge_v2_testlib:create_kind_api([ + {bridge_kind, action}, + {action_type, Type}, + {action_name, Name}, + {action_config, Params} + ]), + emqx_mgmt_api_test_util:simplify_result(Res). + %%------------------------------------------------------------------------------ %% Testcases %%------------------------------------------------------------------------------ @@ -1598,3 +1649,97 @@ t_start_action_or_source_with_disabled_connector(matrix) -> t_start_action_or_source_with_disabled_connector(Config) -> ok = emqx_bridge_v2_testlib:t_start_action_or_source_with_disabled_connector(Config), ok. + +%% Verifies that listing connectors return the actions and sources that depend on the +%% connector +t_connector_dependencies(matrix) -> + [ + [single, actions], + [single, sources] + ]; +t_connector_dependencies(Config) when is_list(Config) -> + ?check_trace( + begin + %% This particular source type happens to serve both actions and sources, a + %% nice edge case for this test. + ActionType = ?SOURCE_TYPE, + ConnectorType = ?SOURCE_CONNECTOR_TYPE, + ConnectorName = <<"c">>, + {ok, {{_, 201, _}, _, _}} = + emqx_bridge_v2_testlib:create_connector_api([ + {connector_config, source_connector_create_config(#{})}, + {connector_name, ConnectorName}, + {connector_type, ConnectorType} + ]), + ?assertMatch( + {200, [ + #{ + <<"actions">> := [], + <<"sources">> := [] + } + ]}, + list_connectors_api() + ), + ?assertMatch( + {200, #{ + <<"actions">> := [], + <<"sources">> := [] + }}, + get_connector_api(ConnectorType, ConnectorName) + ), + + SourceName1 = <<"s1">>, + {201, _} = create_source_api( + SourceName1, + ?SOURCE_TYPE, + source_create_config(#{ + <<"connector">> => ConnectorName + }) + ), + ?assertMatch( + {200, [ + #{ + <<"actions">> := [], + <<"sources">> := [SourceName1] + } + ]}, + list_connectors_api() + ), + ?assertMatch( + {200, #{ + <<"actions">> := [], + <<"sources">> := [SourceName1] + }}, + get_connector_api(ConnectorType, ConnectorName) + ), + + ActionName1 = <<"a1">>, + {201, _} = create_action_api( + ActionName1, + ActionType, + mqtt_action_create_config(#{ + <<"connector">> => ConnectorName + }) + ), + ?assertMatch( + {200, [ + #{ + <<"actions">> := [ActionName1], + <<"sources">> := [SourceName1] + } + ]}, + list_connectors_api() + ), + ?assertMatch( + {200, #{ + <<"actions">> := [ActionName1], + <<"sources">> := [SourceName1] + }}, + get_connector_api(ConnectorType, ConnectorName) + ), + + ok + end, + [] + ), + ok. diff --git a/apps/emqx_connector/src/emqx_connector_api.erl b/apps/emqx_connector/src/emqx_connector_api.erl index 4c7a0476e..c1e0b8ec2 100644 --- a/apps/emqx_connector/src/emqx_connector_api.erl +++ b/apps/emqx_connector/src/emqx_connector_api.erl @@ -655,7 +655,22 @@ format_resource_data(error, undefined, Result) -> format_resource_data(error, Error, Result) -> Result#{status_reason => emqx_utils:readable_error_msg(Error)}; format_resource_data(channels, Channels, Result) -> - Result#{actions => lists:map(fun format_action/1, maps:keys(Channels))}; + #{ + actions := Actions, + sources := Sources + } = lists:foldl( + fun(Id, Acc) -> + case emqx_bridge_v2:parse_id(Id) of + #{kind := source, name := Name} -> + maps:update_with(sources, fun(Ss) -> [Name | Ss] end, Acc); + #{name := Name} -> + maps:update_with(actions, fun(As) -> [Name | As] end, Acc) + end + end, + #{actions => [], sources => []}, + maps:keys(Channels) + ), + Result#{actions => lists:sort(Actions), sources => lists:sort(Sources)}; format_resource_data(K, V, Result) -> Result#{K => V}. @@ -673,12 +688,6 @@ unpack_connector_conf(Type, PackedConf) -> #{<<"foo">> := RawConf} = maps:get(TypeBin, Bridges), RawConf. -format_action(ActionId) -> - case emqx_bridge_v2:parse_id(ActionId) of - #{name := Name} -> - Name - end. - is_ok(ok) -> ok; is_ok(OkResult = {ok, _}) -> diff --git a/apps/emqx_connector/test/emqx_connector_api_SUITE.erl b/apps/emqx_connector/test/emqx_connector_api_SUITE.erl index f3e91ef12..31069b075 100644 --- a/apps/emqx_connector/test/emqx_connector_api_SUITE.erl +++ b/apps/emqx_connector/test/emqx_connector_api_SUITE.erl @@ -109,9 +109,10 @@ emqx_conf, emqx, emqx_auth, - emqx_management, - {emqx_connector, "connectors {}"}, - {emqx_bridge, "actions {}"} + emqx_connector, + emqx_bridge, + emqx_rule_engine, + emqx_management ]). -define(APPSPEC_DASHBOARD,