feat(emqx_bridge): add separate endpoint for enable/disable of bridge

In order to improve the consistency with other API endpoints, we move
the enable/disable operations to a separate endpoint
/bridges/{id}/enable/[true,false].
This commit is contained in:
Erik Timan 2023-01-12 19:32:38 +01:00
parent 860e21d40f
commit 42f42de4d9
3 changed files with 93 additions and 30 deletions

View File

@ -2,8 +2,8 @@ emqx_bridge_api {
desc_param_path_operation_cluster { desc_param_path_operation_cluster {
desc { desc {
en: """Operations can be one of: enable, disable, start, stop, restart""" en: """Operations can be one of: start, stop, restart"""
zh: """集群可用操作:启用、禁用、启动、停止、重新启动""" zh: """"""
} }
label: { label: {
en: "Cluster Operation" en: "Cluster Operation"
@ -44,6 +44,16 @@ emqx_bridge_api {
} }
} }
desc_param_path_enable {
desc {
en: """Whether or not the bridge is enabled"""
zh: """"""
}
label: {
en: "Enable bridge"
zh: ""
}
}
desc_api1 { desc_api1 {
desc { desc {
en: """List all created bridges""" en: """List all created bridges"""
@ -112,8 +122,8 @@ emqx_bridge_api {
desc_api7 { desc_api7 {
desc { desc {
en: """Enable/Disable/Stop/Restart bridges on all nodes in the cluster.""" en: """Stop/Restart bridges on all nodes in the cluster."""
zh: """在集群中的所有节点上启用/禁用/停止/重新启动 Bridge。""" zh: """"""
} }
label: { label: {
en: "Cluster Bridge Operate" en: "Cluster Bridge Operate"
@ -123,10 +133,8 @@ emqx_bridge_api {
desc_api8 { desc_api8 {
desc { desc {
en: """Stop/Restart bridges on a specific node. en: """Stop/Restart bridges on a specific node."""
NOTE: It's not allowed to disable/enable bridges on a single node.""" zh: """在某个节点上停止/重新启动 Bridge。"""
zh: """在某个节点上停止/重新启动 Bridge。
NOTE不允许在单节点上启用/禁用 Bridge"""
} }
label: { label: {
en: "Node Bridge Operate" en: "Node Bridge Operate"
@ -144,4 +152,15 @@ NOTE不允许在单节点上启用/禁用 Bridge"""
zh: "" zh: ""
} }
} }
desc_enable_bridge {
desc {
en: """Enable or Disable bridges on all nodes in the cluster."""
zh: """"""
}
label: {
en: "Cluster Bridge Enable"
zh: ""
}
}
} }

View File

@ -36,6 +36,7 @@
-export([ -export([
'/bridges'/2, '/bridges'/2,
'/bridges/:id'/2, '/bridges/:id'/2,
'/bridges/:id/enable/:enable'/2,
'/bridges/:id/:operation'/2, '/bridges/:id/:operation'/2,
'/nodes/:node/bridges/:id/:operation'/2, '/nodes/:node/bridges/:id/:operation'/2,
'/bridges/:id/metrics'/2, '/bridges/:id/metrics'/2,
@ -67,6 +68,7 @@ paths() ->
[ [
"/bridges", "/bridges",
"/bridges/:id", "/bridges/:id",
"/bridges/:id/enable/:enable",
"/bridges/:id/:operation", "/bridges/:id/:operation",
"/nodes/:node/bridges/:id/:operation", "/nodes/:node/bridges/:id/:operation",
"/bridges/:id/metrics", "/bridges/:id/metrics",
@ -89,7 +91,7 @@ get_response_body_schema() ->
param_path_operation_cluster() -> param_path_operation_cluster() ->
{operation, {operation,
mk( mk(
enum([enable, disable, stop, restart]), enum([stop, restart]),
#{ #{
in => path, in => path,
required => true, required => true,
@ -134,6 +136,17 @@ param_path_id() ->
} }
)}. )}.
param_path_enable() ->
{enable,
mk(
boolean(),
#{
in => path,
desc => ?DESC("desc_param_path_enable"),
example => true
}
)}.
bridge_info_array_example(Method, WithMetrics) -> bridge_info_array_example(Method, WithMetrics) ->
[Config || #{value := Config} <- maps:values(bridge_info_examples(Method, WithMetrics))]. [Config || #{value := Config} <- maps:values(bridge_info_examples(Method, WithMetrics))].
@ -367,12 +380,29 @@ schema("/bridges/:id/metrics/reset") ->
} }
} }
}; };
schema("/bridges/:id/enable/:enable") ->
#{
'operationId' => '/bridges/:id/enable/:enable',
put =>
#{
tags => [<<"bridges">>],
summary => <<"Enable or Disable Bridge">>,
desc => ?DESC("desc_enable_bridge"),
parameters => [param_path_id(), param_path_enable()],
responses =>
#{
204 => <<"Success">>,
400 => error_schema('INVALID_ID', "Bad bridge ID"),
503 => error_schema('SERVICE_UNAVAILABLE', "Service unavailable")
}
}
};
schema("/bridges/:id/:operation") -> schema("/bridges/:id/:operation") ->
#{ #{
'operationId' => '/bridges/:id/:operation', 'operationId' => '/bridges/:id/:operation',
post => #{ post => #{
tags => [<<"bridges">>], tags => [<<"bridges">>],
summary => <<"Enable/Disable/Stop/Restart Bridge">>, summary => <<"Stop or Restart Bridge">>,
description => ?DESC("desc_api7"), description => ?DESC("desc_api7"),
parameters => [ parameters => [
param_path_id(), param_path_id(),
@ -515,6 +545,28 @@ lookup_from_local_node(BridgeType, BridgeName) ->
Error -> Error Error -> Error
end. end.
'/bridges/:id/enable/:enable'(put, #{bindings := #{id := Id, enable := Enable}}) ->
?TRY_PARSE_ID(
Id,
case enable_func(Enable) of
invalid ->
{400, error_msg('BAD_REQUEST', <<"invalid operation">>)};
OperFunc ->
case emqx_bridge:disable_enable(OperFunc, BridgeType, BridgeName) of
{ok, _} ->
{204};
{error, {pre_config_update, _, bridge_not_found}} ->
{404, error_msg('NOT_FOUND', <<"bridge not found">>)};
{error, {_, _, timeout}} ->
{503, error_msg('SERVICE_UNAVAILABLE', <<"request timeout">>)};
{error, timeout} ->
{503, error_msg('SERVICE_UNAVAILABLE', <<"request timeout">>)};
{error, Reason} ->
{500, error_msg('INTERNAL_ERROR', Reason)}
end
end
).
'/bridges/:id/:operation'(post, #{ '/bridges/:id/:operation'(post, #{
bindings := bindings :=
#{id := Id, operation := Op} #{id := Id, operation := Op}
@ -524,19 +576,6 @@ lookup_from_local_node(BridgeType, BridgeName) ->
case operation_func(Op) of case operation_func(Op) of
invalid -> invalid ->
{400, error_msg('BAD_REQUEST', <<"invalid operation">>)}; {400, error_msg('BAD_REQUEST', <<"invalid operation">>)};
OperFunc when OperFunc == enable; OperFunc == disable ->
case emqx_bridge:disable_enable(OperFunc, BridgeType, BridgeName) of
{ok, _} ->
{200};
{error, {pre_config_update, _, bridge_not_found}} ->
{404, error_msg('NOT_FOUND', <<"bridge not found">>)};
{error, {_, _, timeout}} ->
{503, error_msg('SERVICE_UNAVAILABLE', <<"request timeout">>)};
{error, timeout} ->
{503, error_msg('SERVICE_UNAVAILABLE', <<"request timeout">>)};
{error, Reason} ->
{500, error_msg('INTERNAL_ERROR', Reason)}
end;
OperFunc -> OperFunc ->
Nodes = mria_mnesia:running_nodes(), Nodes = mria_mnesia:running_nodes(),
operation_to_all_nodes(Nodes, OperFunc, BridgeType, BridgeName) operation_to_all_nodes(Nodes, OperFunc, BridgeType, BridgeName)
@ -573,10 +612,12 @@ node_operation_func(_) -> invalid.
operation_func(<<"stop">>) -> stop; operation_func(<<"stop">>) -> stop;
operation_func(<<"restart">>) -> restart; operation_func(<<"restart">>) -> restart;
operation_func(<<"enable">>) -> enable;
operation_func(<<"disable">>) -> disable;
operation_func(_) -> invalid. operation_func(_) -> invalid.
enable_func(<<"true">>) -> enable;
enable_func(<<"false">>) -> disable;
enable_func(_) -> invalid.
operation_to_all_nodes(Nodes, OperFunc, BridgeType, BridgeName) -> operation_to_all_nodes(Nodes, OperFunc, BridgeType, BridgeName) ->
RpcFunc = RpcFunc =
case OperFunc of case OperFunc of

View File

@ -498,19 +498,19 @@ t_enable_disable_bridges(Config) ->
} = jsx:decode(Bridge), } = jsx:decode(Bridge),
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name), BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name),
%% disable it %% disable it
{ok, 200, <<>>} = request(post, operation_path(cluster, disable, BridgeID), <<"">>), {ok, 204, <<>>} = request(put, enable_path(false, BridgeID), <<"">>),
{ok, 200, Bridge2} = request(get, uri(["bridges", BridgeID]), []), {ok, 200, Bridge2} = request(get, uri(["bridges", BridgeID]), []),
?assertMatch(#{<<"status">> := <<"stopped">>}, jsx:decode(Bridge2)), ?assertMatch(#{<<"status">> := <<"stopped">>}, jsx:decode(Bridge2)),
%% enable again %% enable again
{ok, 200, <<>>} = request(post, operation_path(cluster, enable, BridgeID), <<"">>), {ok, 204, <<>>} = request(put, enable_path(true, BridgeID), <<"">>),
{ok, 200, Bridge3} = request(get, uri(["bridges", BridgeID]), []), {ok, 200, Bridge3} = request(get, uri(["bridges", BridgeID]), []),
?assertMatch(#{<<"status">> := <<"connected">>}, jsx:decode(Bridge3)), ?assertMatch(#{<<"status">> := <<"connected">>}, jsx:decode(Bridge3)),
%% enable an already started bridge %% enable an already started bridge
{ok, 200, <<>>} = request(post, operation_path(cluster, enable, BridgeID), <<"">>), {ok, 204, <<>>} = request(put, enable_path(true, BridgeID), <<"">>),
{ok, 200, Bridge3} = request(get, uri(["bridges", BridgeID]), []), {ok, 200, Bridge3} = request(get, uri(["bridges", BridgeID]), []),
?assertMatch(#{<<"status">> := <<"connected">>}, jsx:decode(Bridge3)), ?assertMatch(#{<<"status">> := <<"connected">>}, jsx:decode(Bridge3)),
%% disable it again %% disable it again
{ok, 200, <<>>} = request(post, operation_path(cluster, disable, BridgeID), <<"">>), {ok, 204, <<>>} = request(put, enable_path(false, BridgeID), <<"">>),
{ok, 403, Res} = request(post, operation_path(node, restart, BridgeID), <<"">>), {ok, 403, Res} = request(post, operation_path(node, restart, BridgeID), <<"">>),
?assertEqual( ?assertEqual(
@ -519,7 +519,7 @@ t_enable_disable_bridges(Config) ->
), ),
%% enable a stopped bridge %% enable a stopped bridge
{ok, 200, <<>>} = request(post, operation_path(cluster, enable, BridgeID), <<"">>), {ok, 204, <<>>} = request(put, enable_path(true, BridgeID), <<"">>),
{ok, 200, Bridge4} = request(get, uri(["bridges", BridgeID]), []), {ok, 200, Bridge4} = request(get, uri(["bridges", BridgeID]), []),
?assertMatch(#{<<"status">> := <<"connected">>}, jsx:decode(Bridge4)), ?assertMatch(#{<<"status">> := <<"connected">>}, jsx:decode(Bridge4)),
%% delete the bridge %% delete the bridge
@ -674,5 +674,8 @@ operation_path(node, Oper, BridgeID) ->
operation_path(cluster, Oper, BridgeID) -> operation_path(cluster, Oper, BridgeID) ->
uri(["bridges", BridgeID, Oper]). uri(["bridges", BridgeID, Oper]).
enable_path(Enable, BridgeID) ->
uri(["bridges", BridgeID, "enable", Enable]).
str(S) when is_list(S) -> S; str(S) when is_list(S) -> S;
str(S) when is_binary(S) -> binary_to_list(S). str(S) when is_binary(S) -> binary_to_list(S).