diff --git a/apps/emqx_bridge/i18n/emqx_bridge_api.conf b/apps/emqx_bridge/i18n/emqx_bridge_api.conf index 796cbb91c..8adda9355 100644 --- a/apps/emqx_bridge/i18n/emqx_bridge_api.conf +++ b/apps/emqx_bridge/i18n/emqx_bridge_api.conf @@ -2,8 +2,8 @@ emqx_bridge_api { desc_param_path_operation_cluster { desc { - en: """Operations can be one of: enable, disable, start, stop, restart""" - zh: """集群可用操作:启用、禁用、启动、停止、重新启动""" + en: """Operations can be one of: start, stop, restart""" + zh: """""" } label: { 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 { en: """List all created bridges""" @@ -112,8 +122,8 @@ emqx_bridge_api { desc_api7 { desc { - en: """Enable/Disable/Stop/Restart bridges on all nodes in the cluster.""" - zh: """在集群中的所有节点上启用/禁用/停止/重新启动 Bridge。""" + en: """Stop/Restart bridges on all nodes in the cluster.""" + zh: """""" } label: { en: "Cluster Bridge Operate" @@ -123,10 +133,8 @@ emqx_bridge_api { desc_api8 { desc { - en: """Stop/Restart bridges on a specific node. - NOTE: It's not allowed to disable/enable bridges on a single node.""" - zh: """在某个节点上停止/重新启动 Bridge。 -NOTE:不允许在单节点上启用/禁用 Bridge""" + en: """Stop/Restart bridges on a specific node.""" + zh: """在某个节点上停止/重新启动 Bridge。""" } label: { en: "Node Bridge Operate" @@ -144,4 +152,15 @@ NOTE:不允许在单节点上启用/禁用 Bridge""" zh: "" } } + + desc_enable_bridge { + desc { + en: """Enable or Disable bridges on all nodes in the cluster.""" + zh: """""" + } + label: { + en: "Cluster Bridge Enable" + zh: "" + } + } } diff --git a/apps/emqx_bridge/src/emqx_bridge_api.erl b/apps/emqx_bridge/src/emqx_bridge_api.erl index b05e31b11..f3247206e 100644 --- a/apps/emqx_bridge/src/emqx_bridge_api.erl +++ b/apps/emqx_bridge/src/emqx_bridge_api.erl @@ -36,6 +36,7 @@ -export([ '/bridges'/2, '/bridges/:id'/2, + '/bridges/:id/enable/:enable'/2, '/bridges/:id/:operation'/2, '/nodes/:node/bridges/:id/:operation'/2, '/bridges/:id/metrics'/2, @@ -67,6 +68,7 @@ paths() -> [ "/bridges", "/bridges/:id", + "/bridges/:id/enable/:enable", "/bridges/:id/:operation", "/nodes/:node/bridges/:id/:operation", "/bridges/:id/metrics", @@ -89,7 +91,7 @@ get_response_body_schema() -> param_path_operation_cluster() -> {operation, mk( - enum([enable, disable, stop, restart]), + enum([stop, restart]), #{ in => path, 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) -> [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") -> #{ 'operationId' => '/bridges/:id/:operation', post => #{ tags => [<<"bridges">>], - summary => <<"Enable/Disable/Stop/Restart Bridge">>, + summary => <<"Stop or Restart Bridge">>, description => ?DESC("desc_api7"), parameters => [ param_path_id(), @@ -515,6 +545,28 @@ lookup_from_local_node(BridgeType, BridgeName) -> Error -> Error 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, #{ bindings := #{id := Id, operation := Op} @@ -524,19 +576,6 @@ lookup_from_local_node(BridgeType, BridgeName) -> case operation_func(Op) of invalid -> {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 -> Nodes = mria_mnesia:running_nodes(), operation_to_all_nodes(Nodes, OperFunc, BridgeType, BridgeName) @@ -573,10 +612,12 @@ node_operation_func(_) -> invalid. operation_func(<<"stop">>) -> stop; operation_func(<<"restart">>) -> restart; -operation_func(<<"enable">>) -> enable; -operation_func(<<"disable">>) -> disable; operation_func(_) -> invalid. +enable_func(<<"true">>) -> enable; +enable_func(<<"false">>) -> disable; +enable_func(_) -> invalid. + operation_to_all_nodes(Nodes, OperFunc, BridgeType, BridgeName) -> RpcFunc = case OperFunc of diff --git a/apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl b/apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl index eb86b923f..4650ea1ad 100644 --- a/apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl +++ b/apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl @@ -498,19 +498,19 @@ t_enable_disable_bridges(Config) -> } = jsx:decode(Bridge), BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name), %% 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]), []), ?assertMatch(#{<<"status">> := <<"stopped">>}, jsx:decode(Bridge2)), %% 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]), []), ?assertMatch(#{<<"status">> := <<"connected">>}, jsx:decode(Bridge3)), %% 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]), []), ?assertMatch(#{<<"status">> := <<"connected">>}, jsx:decode(Bridge3)), %% 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), <<"">>), ?assertEqual( @@ -519,7 +519,7 @@ t_enable_disable_bridges(Config) -> ), %% 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]), []), ?assertMatch(#{<<"status">> := <<"connected">>}, jsx:decode(Bridge4)), %% delete the bridge @@ -674,5 +674,8 @@ operation_path(node, Oper, BridgeID) -> operation_path(cluster, Oper, BridgeID) -> uri(["bridges", BridgeID, Oper]). +enable_path(Enable, BridgeID) -> + uri(["bridges", BridgeID, "enable", Enable]). + str(S) when is_list(S) -> S; str(S) when is_binary(S) -> binary_to_list(S).