From f94b943ec26546104196b2559d501e480fa7399e Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Fri, 15 Dec 2023 09:38:34 -0300 Subject: [PATCH] fix(bridge_v1_api): fill defaults for v2 raw configs Fixes https://emqx.atlassian.net/browse/EMQX-11593 --- apps/emqx_bridge/src/emqx_bridge_api.erl | 14 ++-- .../src/schema/emqx_bridge_schema.erl | 4 +- ...qx_bridge_v1_compatibility_layer_SUITE.erl | 69 +++++++++++++++++-- 3 files changed, 71 insertions(+), 16 deletions(-) diff --git a/apps/emqx_bridge/src/emqx_bridge_api.erl b/apps/emqx_bridge/src/emqx_bridge_api.erl index 9ec0d440c..ceb8f7166 100644 --- a/apps/emqx_bridge/src/emqx_bridge_api.erl +++ b/apps/emqx_bridge/src/emqx_bridge_api.erl @@ -895,25 +895,19 @@ aggregate_metrics( format_resource( #{ - type := Type, + type := ActionType, name := BridgeName, raw_config := RawConf, resource_data := ResourceData }, Node ) -> - RawConfFull = - case emqx_bridge_v2:is_bridge_v2_type(Type) of - true -> - %% The defaults are already filled in - RawConf; - false -> - fill_defaults(Type, RawConf) - end, + BridgeV1Type = downgrade_type(ActionType, emqx_bridge_lib:get_conf(ActionType, BridgeName)), + RawConfFull = fill_defaults(BridgeV1Type, RawConf), redact( maps:merge( RawConfFull#{ - type => downgrade_type(Type, emqx_bridge_lib:get_conf(Type, BridgeName)), + type => BridgeV1Type, name => maps:get(<<"name">>, RawConf, BridgeName), node => Node }, diff --git a/apps/emqx_bridge/src/schema/emqx_bridge_schema.erl b/apps/emqx_bridge/src/schema/emqx_bridge_schema.erl index aa58d825e..699d4f30c 100644 --- a/apps/emqx_bridge/src/schema/emqx_bridge_schema.erl +++ b/apps/emqx_bridge/src/schema/emqx_bridge_schema.erl @@ -36,7 +36,7 @@ ]). %% for testing only --export([enterprise_api_schemas/1]). +-export([enterprise_api_schemas/1, enterprise_fields_bridges/0]). %%====================================================================================== %% Hocon Schema Definitions @@ -191,7 +191,7 @@ fields(bridges) -> end } )} - ] ++ enterprise_fields_bridges(); + ] ++ ?MODULE:enterprise_fields_bridges(); fields("metrics") -> [ {"dropped", mk(integer(), #{desc => ?DESC("metric_dropped")})}, diff --git a/apps/emqx_bridge/test/emqx_bridge_v1_compatibility_layer_SUITE.erl b/apps/emqx_bridge/test/emqx_bridge_v1_compatibility_layer_SUITE.erl index 215013a6d..b268c127d 100644 --- a/apps/emqx_bridge/test/emqx_bridge_v1_compatibility_layer_SUITE.erl +++ b/apps/emqx_bridge/test/emqx_bridge_v1_compatibility_layer_SUITE.erl @@ -106,7 +106,9 @@ setup_mocks() -> emqx_bridge_v2_schema, registered_api_schemas, 1, - fun(Method) -> [{bridge_type_bin(), hoconsc:ref(?MODULE, "api_" ++ Method)}] end + fun(Method) -> + [{bridge_type_bin(), hoconsc:ref(?MODULE, "api_v2_" ++ Method)}] + end ), catch meck:new(emqx_bridge_schema, MeckOpts), @@ -114,7 +116,24 @@ setup_mocks() -> emqx_bridge_schema, enterprise_api_schemas, 1, - fun(Method) -> [{bridge_type_bin(), hoconsc:ref(?MODULE, "api_" ++ Method)}] end + fun(Method) -> + [{bridge_type_bin(), hoconsc:ref(?MODULE, "api_v1_" ++ Method)}] + end + ), + meck:expect( + emqx_bridge_schema, + enterprise_fields_bridges, + 0, + fun() -> + [ + { + bridge_type_bin(), + hoconsc:mk( + hoconsc:map(name, hoconsc:ref(?MODULE, v1_bridge)), #{} + ) + } + ] + end ), ok. @@ -156,7 +175,7 @@ fields("connector") -> {on_start_fun, hoconsc:mk(binary(), #{})}, {ssl, hoconsc:ref(ssl)} ]; -fields("api_post") -> +fields("api_v2_post") -> [ {connector, hoconsc:mk(binary(), #{})}, {name, hoconsc:mk(binary(), #{})}, @@ -164,6 +183,20 @@ fields("api_post") -> {send_to, hoconsc:mk(atom(), #{})} | fields("connector") ]; +fields("api_v1_post") -> + ConnectorFields = proplists:delete(resource_opts, fields("connector")), + [ + {connector, hoconsc:mk(binary(), #{})}, + {name, hoconsc:mk(binary(), #{})}, + {type, hoconsc:mk(bridge_type(), #{})}, + {send_to, hoconsc:mk(atom(), #{})}, + {resource_opts, hoconsc:mk(hoconsc:ref(?MODULE, v1_resource_opts), #{})} + | ConnectorFields + ]; +fields(v1_bridge) -> + lists:foldl(fun proplists:delete/2, fields("api_v1_post"), [name, type]); +fields(v1_resource_opts) -> + emqx_resource_schema:create_opts(_Overrides = []); fields(ssl) -> emqx_schema:client_ssl_opts_schema(#{required => false}). @@ -333,9 +366,11 @@ get_connector_http(Name) -> create_bridge_http_api_v1(Opts) -> Name = maps:get(name, Opts), Overrides = maps:get(overrides, Opts, #{}), + OverrideFn = maps:get(override_fn, Opts, fun(X) -> X end), BridgeConfig0 = emqx_utils_maps:deep_merge(bridge_config(), Overrides), BridgeConfig = maps:without([<<"connector">>], BridgeConfig0), - Params = BridgeConfig#{<<"type">> => bridge_type_bin(), <<"name">> => Name}, + Params0 = BridgeConfig#{<<"type">> => bridge_type_bin(), <<"name">> => Name}, + Params = OverrideFn(Params0), Path = emqx_mgmt_api_test_util:api_path(["bridges"]), ct:pal("creating bridge (http v1): ~p", [Params]), Res = request(post, Path, Params), @@ -919,3 +954,29 @@ t_obfuscated_secrets_probe(_Config) -> ), ok. + +t_v1_api_fill_defaults(_Config) -> + %% Ensure only one sub-field is used, but we get back the defaults filled in. + BridgeName = ?FUNCTION_NAME, + OverrideFn = fun(Params) -> + ResourceOpts = #{<<"resume_interval">> => 100}, + maps:put(<<"resource_opts">>, ResourceOpts, Params) + end, + ?assertMatch( + {ok, + {{_, 201, _}, _, #{ + <<"resource_opts">> := + #{ + <<"resume_interval">> := _, + <<"query_mode">> := _, + <<"inflight_window">> := _, + <<"start_timeout">> := _, + <<"start_after_created">> := _, + <<"max_buffer_bytes">> := _, + <<"batch_size">> := _ + } + }}}, + create_bridge_http_api_v1(#{name => BridgeName, override_fn => OverrideFn}) + ), + + ok.