Merge pull request #11862 from kjellwinblad/kjell/shared_con/del_rules/EMQX-11293
fix(bridge_v2 API): optional cascading delete operation
This commit is contained in:
commit
34ec7375ba
|
@ -38,7 +38,11 @@
|
||||||
list/0,
|
list/0,
|
||||||
lookup/2,
|
lookup/2,
|
||||||
create/3,
|
create/3,
|
||||||
remove/2
|
remove/2,
|
||||||
|
%% The following is the remove function that is called by the HTTP API
|
||||||
|
%% It also checks for rule action dependencies and optionally removes
|
||||||
|
%% them
|
||||||
|
check_deps_and_remove/3
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% Operations
|
%% Operations
|
||||||
|
@ -231,6 +235,25 @@ remove(BridgeType, BridgeName) ->
|
||||||
{error, Reason} -> {error, Reason}
|
{error, Reason} -> {error, Reason}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
check_deps_and_remove(BridgeType, BridgeName, AlsoDeleteActions) ->
|
||||||
|
AlsoDelete =
|
||||||
|
case AlsoDeleteActions of
|
||||||
|
true -> [rule_actions];
|
||||||
|
false -> []
|
||||||
|
end,
|
||||||
|
case
|
||||||
|
emqx_bridge_lib:maybe_withdraw_rule_action(
|
||||||
|
BridgeType,
|
||||||
|
BridgeName,
|
||||||
|
AlsoDelete
|
||||||
|
)
|
||||||
|
of
|
||||||
|
ok ->
|
||||||
|
remove(BridgeType, BridgeName);
|
||||||
|
{error, Reason} ->
|
||||||
|
{error, Reason}
|
||||||
|
end.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Helpers for CRUD API
|
%% Helpers for CRUD API
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
|
@ -123,6 +123,18 @@ param_path_id() ->
|
||||||
}
|
}
|
||||||
)}.
|
)}.
|
||||||
|
|
||||||
|
param_qs_delete_cascade() ->
|
||||||
|
{also_delete_dep_actions,
|
||||||
|
mk(
|
||||||
|
boolean(),
|
||||||
|
#{
|
||||||
|
in => query,
|
||||||
|
required => false,
|
||||||
|
default => false,
|
||||||
|
desc => ?DESC("desc_qs_also_delete_dep_actions")
|
||||||
|
}
|
||||||
|
)}.
|
||||||
|
|
||||||
param_path_operation_cluster() ->
|
param_path_operation_cluster() ->
|
||||||
{operation,
|
{operation,
|
||||||
mk(
|
mk(
|
||||||
|
@ -231,7 +243,7 @@ schema("/bridges_v2/:id") ->
|
||||||
tags => [<<"bridges_v2">>],
|
tags => [<<"bridges_v2">>],
|
||||||
summary => <<"Delete bridge">>,
|
summary => <<"Delete bridge">>,
|
||||||
description => ?DESC("desc_api5"),
|
description => ?DESC("desc_api5"),
|
||||||
parameters => [param_path_id()],
|
parameters => [param_path_id(), param_qs_delete_cascade()],
|
||||||
responses => #{
|
responses => #{
|
||||||
204 => <<"Bridge deleted">>,
|
204 => <<"Bridge deleted">>,
|
||||||
400 => error_schema(
|
400 => error_schema(
|
||||||
|
@ -365,19 +377,35 @@ schema("/bridges_v2_probe") ->
|
||||||
?BRIDGE_NOT_FOUND(BridgeType, BridgeName)
|
?BRIDGE_NOT_FOUND(BridgeType, BridgeName)
|
||||||
end
|
end
|
||||||
);
|
);
|
||||||
'/bridges_v2/:id'(delete, #{bindings := #{id := Id}}) ->
|
'/bridges_v2/:id'(delete, #{bindings := #{id := Id}, query_string := Qs}) ->
|
||||||
?TRY_PARSE_ID(
|
?TRY_PARSE_ID(
|
||||||
Id,
|
Id,
|
||||||
case emqx_bridge_v2:lookup(BridgeType, BridgeName) of
|
case emqx_bridge_v2:lookup(BridgeType, BridgeName) of
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
case emqx_bridge_v2:remove(BridgeType, BridgeName) of
|
AlsoDeleteActions =
|
||||||
|
case maps:get(<<"also_delete_dep_actions">>, Qs, <<"false">>) of
|
||||||
|
<<"true">> -> true;
|
||||||
|
true -> true;
|
||||||
|
_ -> false
|
||||||
|
end,
|
||||||
|
case
|
||||||
|
emqx_bridge_v2:check_deps_and_remove(BridgeType, BridgeName, AlsoDeleteActions)
|
||||||
|
of
|
||||||
ok ->
|
ok ->
|
||||||
?NO_CONTENT;
|
?NO_CONTENT;
|
||||||
{error, {active_channels, Channels}} ->
|
{error, #{
|
||||||
?BAD_REQUEST(
|
reason := rules_depending_on_this_bridge,
|
||||||
{<<"Cannot delete bridge while there are active channels defined for this bridge">>,
|
rule_ids := RuleIds
|
||||||
Channels}
|
}} ->
|
||||||
);
|
RuleIdLists = [binary_to_list(iolist_to_binary(X)) || X <- RuleIds],
|
||||||
|
RulesStr = string:join(RuleIdLists, ", "),
|
||||||
|
Msg = io_lib:format(
|
||||||
|
"Cannot delete bridge while active rules are depending on it: ~s\n"
|
||||||
|
"Append ?also_delete_dep_actions=true to the request URL to delete "
|
||||||
|
"rule actions that depend on this bridge as well.",
|
||||||
|
[RulesStr]
|
||||||
|
),
|
||||||
|
?BAD_REQUEST(iolist_to_binary(Msg));
|
||||||
{error, timeout} ->
|
{error, timeout} ->
|
||||||
?SERVICE_UNAVAILABLE(<<"request timeout">>);
|
?SERVICE_UNAVAILABLE(<<"request timeout">>);
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
|
|
|
@ -147,7 +147,8 @@
|
||||||
emqx,
|
emqx,
|
||||||
emqx_auth,
|
emqx_auth,
|
||||||
emqx_management,
|
emqx_management,
|
||||||
{emqx_bridge, "bridges_v2 {}"}
|
{emqx_bridge, "bridges_v2 {}"},
|
||||||
|
{emqx_rule_engine, "rule_engine { rules {} }"}
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-define(APPSPEC_DASHBOARD,
|
-define(APPSPEC_DASHBOARD,
|
||||||
|
@ -667,6 +668,73 @@ t_bridges_probe(Config) ->
|
||||||
),
|
),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
t_cascade_delete_actions(Config) ->
|
||||||
|
%% assert we there's no bridges at first
|
||||||
|
{ok, 200, []} = request_json(get, uri([?ROOT]), Config),
|
||||||
|
%% then we add a a bridge, using POST
|
||||||
|
%% POST /bridges_v2/ will create a bridge
|
||||||
|
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, ?BRIDGE_NAME),
|
||||||
|
{ok, 201, _} = request(
|
||||||
|
post,
|
||||||
|
uri([?ROOT]),
|
||||||
|
?KAFKA_BRIDGE(?BRIDGE_NAME),
|
||||||
|
Config
|
||||||
|
),
|
||||||
|
{ok, 201, #{<<"id">> := RuleId}} = request_json(
|
||||||
|
post,
|
||||||
|
uri(["rules"]),
|
||||||
|
#{
|
||||||
|
<<"name">> => <<"t_http_crud_apis">>,
|
||||||
|
<<"enable">> => true,
|
||||||
|
<<"actions">> => [BridgeID],
|
||||||
|
<<"sql">> => <<"SELECT * from \"t\"">>
|
||||||
|
},
|
||||||
|
Config
|
||||||
|
),
|
||||||
|
%% delete the bridge will also delete the actions from the rules
|
||||||
|
{ok, 204, _} = request(
|
||||||
|
delete,
|
||||||
|
uri([?ROOT, BridgeID]) ++ "?also_delete_dep_actions=true",
|
||||||
|
Config
|
||||||
|
),
|
||||||
|
{ok, 200, []} = request_json(get, uri([?ROOT]), Config),
|
||||||
|
?assertMatch(
|
||||||
|
{ok, 200, #{<<"actions">> := []}},
|
||||||
|
request_json(get, uri(["rules", RuleId]), Config)
|
||||||
|
),
|
||||||
|
{ok, 204, <<>>} = request(delete, uri(["rules", RuleId]), Config),
|
||||||
|
|
||||||
|
{ok, 201, _} = request(
|
||||||
|
post,
|
||||||
|
uri([?ROOT]),
|
||||||
|
?KAFKA_BRIDGE(?BRIDGE_NAME),
|
||||||
|
Config
|
||||||
|
),
|
||||||
|
{ok, 201, _} = request(
|
||||||
|
post,
|
||||||
|
uri(["rules"]),
|
||||||
|
#{
|
||||||
|
<<"name">> => <<"t_http_crud_apis">>,
|
||||||
|
<<"enable">> => true,
|
||||||
|
<<"actions">> => [BridgeID],
|
||||||
|
<<"sql">> => <<"SELECT * from \"t\"">>
|
||||||
|
},
|
||||||
|
Config
|
||||||
|
),
|
||||||
|
{ok, 400, _} = request(
|
||||||
|
delete,
|
||||||
|
uri([?ROOT, BridgeID]),
|
||||||
|
Config
|
||||||
|
),
|
||||||
|
{ok, 200, [_]} = request_json(get, uri([?ROOT]), Config),
|
||||||
|
%% Cleanup
|
||||||
|
{ok, 204, _} = request(
|
||||||
|
delete,
|
||||||
|
uri([?ROOT, BridgeID]) ++ "?also_delete_dep_actions=true",
|
||||||
|
Config
|
||||||
|
),
|
||||||
|
{ok, 200, []} = request_json(get, uri([?ROOT]), Config).
|
||||||
|
|
||||||
%%% helpers
|
%%% helpers
|
||||||
listen_on_random_port() ->
|
listen_on_random_port() ->
|
||||||
SockOpts = [binary, {active, false}, {packet, raw}, {reuseaddr, true}, {backlog, 1000}],
|
SockOpts = [binary, {active, false}, {packet, raw}, {reuseaddr, true}, {backlog, 1000}],
|
||||||
|
|
|
@ -79,6 +79,12 @@ desc_param_path_id.desc:
|
||||||
desc_param_path_id.label:
|
desc_param_path_id.label:
|
||||||
"""Bridge ID"""
|
"""Bridge ID"""
|
||||||
|
|
||||||
|
desc_qs_also_delete_dep_actions.desc:
|
||||||
|
"""Whether to cascade delete dependent actions."""
|
||||||
|
|
||||||
|
desc_qs_also_delete_dep_actions.label:
|
||||||
|
"""Cascade delete dependent actions?"""
|
||||||
|
|
||||||
desc_param_path_node.desc:
|
desc_param_path_node.desc:
|
||||||
"""The node name, e.g. 'emqx@127.0.0.1'."""
|
"""The node name, e.g. 'emqx@127.0.0.1'."""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue