Skip to content

Commit

Permalink
fix: consistently return 404 in case bridge is not found or invalid
Browse files Browse the repository at this point in the history
Also: fix some typos and be more verbose
  • Loading branch information
sstrigler committed Mar 1, 2023
1 parent d0f43be commit 3f324f8
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 30 deletions.
59 changes: 34 additions & 25 deletions apps/emqx_bridge/src/emqx_bridge_api.erl
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,22 @@

-export([lookup_from_local_node/2]).

-define(NOT_FOUND(Reason), {404, error_msg('NOT_FOUND', Reason)}).

-define(BRIDGE_NOT_FOUND(Type, Name),
?NOT_FOUND(
<<"Bridge lookup failed: bridge named '", BridgeName/binary, "' of type ",
(atom_to_binary(BridgeType))/binary, " does not exist.">>
)
).

-define(TRY_PARSE_ID(ID, EXPR),
try emqx_bridge_resource:parse_bridge_id(Id) of
{BridgeType, BridgeName} ->
EXPR
catch
throw:{invalid_bridge_id, Reason} ->
{400,
error_msg(
'INVALID_ID',
<<"Invalid bride ID, ", Reason/binary>>
)}
?NOT_FOUND(<<"Invalid bridge ID, ", Reason/binary>>)
end
).

Expand Down Expand Up @@ -338,7 +343,7 @@ schema("/bridges/:id") ->
responses => #{
200 => get_response_body_schema(),
404 => error_schema('NOT_FOUND', "Bridge not found"),
400 => error_schema(['BAD_REQUEST', 'INVALID_ID'], "Update bridge failed")
400 => error_schema('BAD_REQUEST', "Update bridge failed")
}
},
delete => #{
Expand All @@ -348,7 +353,6 @@ schema("/bridges/:id") ->
parameters => [param_path_id()],
responses => #{
204 => <<"Bridge deleted">>,
400 => error_schema(['INVALID_ID'], "Update bridge failed"),
404 => error_schema('NOT_FOUND', "Bridge not found"),
403 => error_schema('FORBIDDEN_REQUEST', "Forbidden operation"),
503 => error_schema('SERVICE_UNAVAILABLE', "Service unavailable")
Expand Down Expand Up @@ -379,7 +383,8 @@ schema("/bridges/:id/metrics/reset") ->
parameters => [param_path_id()],
responses => #{
204 => <<"Reset success">>,
400 => error_schema(['BAD_REQUEST'], "RPC Call Failed")
400 => error_schema(['BAD_REQUEST'], "RPC Call Failed"),
404 => error_schema('NOT_FOUND', "Bridge not found")
}
}
};
Expand All @@ -395,7 +400,7 @@ schema("/bridges/:id/enable/:enable") ->
responses =>
#{
204 => <<"Success">>,
400 => error_schema('INVALID_ID', "Bad bridge ID"),
404 => error_schema('NOT_FOUND', "Bridge not found or operation invalid"),
503 => error_schema('SERVICE_UNAVAILABLE', "Service unavailable")
}
}
Expand All @@ -413,7 +418,7 @@ schema("/bridges/:id/:operation") ->
],
responses => #{
204 => <<"Operation success">>,
400 => error_schema('INVALID_ID', "Bad bridge ID"),
404 => error_schema('NOT_FOUND', "Bridge not found or operation invalid"),
501 => error_schema('NOT_IMPLEMENTED', "Not Implemented"),
503 => error_schema('SERVICE_UNAVAILABLE', "Service unavailable")
}
Expand All @@ -433,7 +438,7 @@ schema("/nodes/:node/bridges/:id/:operation") ->
],
responses => #{
204 => <<"Operation success">>,
400 => error_schema('INVALID_ID', "Bad bridge ID"),
404 => error_schema('NOT_FOUND', "Bridge not found or operation invalid"),
403 => error_schema('FORBIDDEN_REQUEST', "forbidden operation"),
501 => error_schema('NOT_IMPLEMENTED', "Not Implemented"),
503 => error_schema('SERVICE_UNAVAILABLE', "Service unavailable")
Expand Down Expand Up @@ -493,24 +498,26 @@ schema("/bridges_probe") ->
{400, Error}
end;
{error, not_found} ->
{404, error_msg('NOT_FOUND', <<"bridge not found">>)}
?BRIDGE_NOT_FOUND(BridgeType, BridgeName)
end
);
'/bridges/:id'(delete, #{bindings := #{id := Id}, query_string := Qs}) ->
AlsoDeleteActs =
case maps:get(<<"also_delete_dep_actions">>, Qs, <<"false">>) of
<<"true">> -> true;
true -> true;
_ -> false
end,
?TRY_PARSE_ID(
Id,
case emqx_bridge:lookup(BridgeType, BridgeName) of
{ok, _} ->
AlsoDeleteActs =
case maps:get(<<"also_delete_dep_actions">>, Qs, <<"false">>) of
<<"true">> -> true;
true -> true;
_ -> false
end,
case emqx_bridge:check_deps_and_remove(BridgeType, BridgeName, AlsoDeleteActs) of
{ok, _} ->
204;
{error, {rules_deps_on_this_bridge, RuleIds}} ->
%% [FIXME] this should be a 400 since '403' is about
%% authorization and not application logic.
{403,
error_msg(
'FORBIDDEN_REQUEST',
Expand All @@ -522,7 +529,7 @@ schema("/bridges_probe") ->
{500, error_msg('INTERNAL_ERROR', Reason)}
end;
{error, not_found} ->
{404, error_msg('NOT_FOUND', <<"Bridge not found">>)}
?BRIDGE_NOT_FOUND(BridgeType, BridgeName)
end
).

Expand Down Expand Up @@ -570,7 +577,7 @@ do_lookup_from_all_nodes(BridgeType, BridgeName, SuccCode, FormatFun) ->
{ok, [{ok, _} | _] = Results} ->
{SuccCode, FormatFun([R || {ok, R} <- Results])};
{ok, [{error, not_found} | _]} ->
{404, error_msg('NOT_FOUND', <<"not_found">>)};
?BRIDGE_NOT_FOUND(BridgeType, BridgeName);
{error, ErrL} ->
{500, error_msg('INTERNAL_ERROR', ErrL)}
end.
Expand All @@ -586,13 +593,13 @@ lookup_from_local_node(BridgeType, BridgeName) ->
Id,
case enable_func(Enable) of
invalid ->
{400, error_msg('BAD_REQUEST', <<"invalid operation">>)};
?NOT_FOUND(<<"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">>)};
?BRIDGE_NOT_FOUND(BridgeType, BridgeName);
{error, {_, _, timeout}} ->
{503, error_msg('SERVICE_UNAVAILABLE', <<"request timeout">>)};
{error, timeout} ->
Expand All @@ -611,7 +618,7 @@ lookup_from_local_node(BridgeType, BridgeName) ->
Id,
case operation_to_all_func(Op) of
invalid ->
{400, error_msg('BAD_REQUEST', <<"invalid operation">>)};
?NOT_FOUND(<<"Invalid operation: ", Op/binary>>);
OperFunc ->
Nodes = mria_mnesia:running_nodes(),
call_operation(all, OperFunc, [Nodes, BridgeType, BridgeName])
Expand All @@ -626,11 +633,13 @@ lookup_from_local_node(BridgeType, BridgeName) ->
Id,
case node_operation_func(Op) of
invalid ->
{400, error_msg('BAD_REQUEST', <<"invalid operation">>)};
?NOT_FOUND(<<"Invalid operation: ", Op/binary>>);
OperFunc ->
ConfMap = emqx:get_config([bridges, BridgeType, BridgeName]),
case maps:get(enable, ConfMap, false) of
false ->
%% [FIXME] `403` is about authorization not application
%% logic.
{403,
error_msg(
'FORBIDDEN_REQUEST',
Expand All @@ -643,7 +652,7 @@ lookup_from_local_node(BridgeType, BridgeName) ->
TargetNode, BridgeType, BridgeName
]);
{error, _} ->
{400, error_msg('INVALID_NODE', <<"invalid node">>)}
?NOT_FOUND(<<"Invalid node name: ", Node/binary>>)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion apps/emqx_bridge/src/emqx_bridge_resource.erl
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ parse_bridge_id(BridgeId) ->
{to_type_atom(Type), validate_name(Name)};
_ ->
invalid_bridge_id(
<<"should be of forst {type}:{name}, but got ", BridgeId/binary>>
<<"should be of pattern {type}:{name}, but got ", BridgeId/binary>>
)
end.

Expand Down
8 changes: 4 additions & 4 deletions apps/emqx_bridge/test/emqx_bridge_api_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -311,17 +311,17 @@ t_http_crud_apis(Config) ->
),
?assertMatch(
#{
<<"code">> := _,
<<"message">> := <<"bridge not found">>
<<"code">> := <<"NOT_FOUND">>,
<<"message">> := _
},
jsx:decode(ErrMsg2)
),
%% Deleting a non-existing bridge should result in an error
{ok, 404, ErrMsg3} = request(delete, uri(["bridges", BridgeID]), []),
?assertMatch(
#{
<<"code">> := _,
<<"message">> := <<"Bridge not found">>
<<"code">> := <<"NOT_FOUND">>,
<<"message">> := _
},
jsx:decode(ErrMsg3)
),
Expand Down
1 change: 1 addition & 0 deletions changes/ce/fix-10050.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ensure Bridge API returns `404` status code consistently for resources that don't exist.
1 change: 1 addition & 0 deletions changes/ce/fix-10050.zh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
确保 Bridge API 对不存在的资源一致返回 `404` 状态代码。

0 comments on commit 3f324f8

Please sign in to comment.