Просмотр исходного кода

Merge pull request #5261 from DDDHuang/routes_api

refactor: routes api
DDDHuang 4 лет назад
Родитель
Сommit
9a64c9a464

+ 101 - 23
apps/emqx_management/src/emqx_mgmt_api_routes.erl

@@ -18,30 +18,108 @@
 
 -include_lib("emqx/include/emqx.hrl").
 
--rest_api(#{name   => list_routes,
-            method => 'GET',
-            path   => "/routes/",
-            func   => list,
-            descr  => "List routes"}).
-
--rest_api(#{name   => lookup_routes,
-            method => 'GET',
-            path   => "/routes/:bin:topic",
-            func   => lookup,
-            descr  => "Lookup routes to a topic"}).
-
--export([ list/2
-        , lookup/2
-        ]).
-
-list(Bindings, Params) when map_size(Bindings) == 0 ->
-    emqx_mgmt:return({ok, emqx_mgmt_api:paginate(emqx_route, Params, fun format/1)}).
-
-lookup(#{topic := Topic}, _Params) ->
-    Topic1 = emqx_mgmt_util:urldecode(Topic),
-    emqx_mgmt:return({ok, [format(R) || R <- emqx_mgmt:lookup_routes(Topic1)]}).
+%% API
+-behavior(minirest_api).
+
+-export([api_spec/0]).
+
+-export([ routes/2
+        , route/2]).
+
+-define(TOPIC_NOT_FOUND, 'TOPIC_NOT_FOUND').
+
+api_spec() ->
+    {
+        [routes_api(), route_api()],
+        [route_schema()]
+    }.
+
+route_schema() ->
+    #{
+        route => #{
+            type => object,
+            properties => #{
+                topic => #{
+                    type => string},
+                node => #{
+                    type => string,
+                    example => node()}}}}.
+
+routes_api() ->
+    Metadata = #{
+        get => #{
+            description => "EMQ X routes",
+            parameters => [
+                #{
+                    name => page,
+                    in => query,
+                    description => <<"Page">>,
+                    schema => #{type => integer},
+                    default => 1
+                },
+                #{
+                    name => limit,
+                    in => query,
+                    description => <<"Page size">>,
+                    schema => #{type => integer},
+                    default => emqx_mgmt:max_row_limit()
+                }],
+            responses => #{
+                <<"200">> =>
+                    emqx_mgmt_util:response_array_schema("List route info", <<"route">>)}}},
+    {"/routes", Metadata, routes}.
+
+route_api() ->
+    Metadata = #{
+        get => #{
+            description => "EMQ X routes",
+            parameters => [#{
+                name => topic,
+                in => path,
+                required => true,
+                description => <<"topic">>,
+                schema => #{type => string}
+            }],
+            responses => #{
+                <<"200">> =>
+                    emqx_mgmt_util:response_schema(<<"Route info">>, <<"route">>),
+                <<"404">> =>
+                    emqx_mgmt_util:not_found_schema(<<"Topic not found">>, [?TOPIC_NOT_FOUND])
+            }}},
+    {"/routes/:topic", Metadata, route}.
+
+%%%==============================================================================================
+%% parameters trans
+routes(get, Request) ->
+    Params = cowboy_req:parse_qs(Request),
+    list(Params).
+
+route(get, Request) ->
+    Topic = cowboy_req:binding(topic, Request),
+    lookup(#{topic => Topic}).
+
+%%%==============================================================================================
+%% api apply
+list(Params) ->
+    Data = emqx_mgmt_api:paginate(emqx_route, Params, fun format/1),
+    Response = emqx_json:encode(Data),
+    {200, Response}.
+
+lookup(#{topic := Topic}) ->
+    case emqx_mgmt:lookup_routes(Topic) of
+        [] ->
+            NotFound = #{code => ?TOPIC_NOT_FOUND, reason => <<"Topic not found">>},
+            Response = emqx_json:encode(NotFound),
+            {404, Response};
+        [Route] ->
+            Data = format(Route),
+            Response = emqx_json:encode(Data),
+            {200, Response}
+    end.
+
+%%%==============================================================================================
+%% internal
 format(#route{topic = Topic, dest = {_, Node}}) ->
     #{topic => Topic, node => Node};
 format(#route{topic = Topic, dest = Node}) ->
     #{topic => Topic, node => Node}.
-

+ 65 - 0
apps/emqx_management/test/emqx_mgmt_routes_api_SUITE.erl

@@ -0,0 +1,65 @@
+%%--------------------------------------------------------------------
+%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%--------------------------------------------------------------------
+-module(emqx_mgmt_routes_api_SUITE).
+
+-compile(export_all).
+-compile(nowarn_export_all).
+
+-include_lib("eunit/include/eunit.hrl").
+
+all() ->
+    emqx_ct:all(?MODULE).
+
+init_per_suite(Config) ->
+    ekka_mnesia:start(),
+    emqx_mgmt_auth:mnesia(boot),
+    emqx_ct_helpers:start_apps([emqx_management], fun set_special_configs/1),
+    Config.
+
+end_per_suite(_) ->
+    emqx_ct_helpers:stop_apps([emqx_management]).
+
+set_special_configs(emqx_management) ->
+    emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}],
+        applications =>[#{id => "admin", secret => "public"}]}),
+    ok;
+set_special_configs(_App) ->
+    ok.
+
+t_nodes_api(_) ->
+    Topic = <<"test_topic">>,
+    {ok, Client} = emqtt:start_link(#{username => <<"routes_username">>, clientid => <<"routes_cid">>}),
+    {ok, _} = emqtt:connect(Client),
+    {ok, _, _} = emqtt:subscribe(Client, Topic),
+
+    Path = emqx_mgmt_api_test_util:api_path(["routes"]),
+    {ok, Response} = emqx_mgmt_api_test_util:request_api(get, Path),
+    RoutesData = emqx_json:decode(Response, [return_maps]),
+    Meta = maps:get(<<"meta">>, RoutesData),
+    ?assertEqual(1, maps:get(<<"page">>, Meta)),
+    ?assertEqual(emqx_mgmt:max_row_limit(), maps:get(<<"limit">>, Meta)),
+    ?assertEqual(1, maps:get(<<"count">>, Meta)),
+    Data = maps:get(<<"data">>, RoutesData),
+    Route = erlang:hd(Data),
+    ?assertEqual(Topic, maps:get(<<"topic">>, Route)),
+    ?assertEqual(atom_to_binary(node(), utf8), maps:get(<<"node">>, Route)),
+
+    %% get routes/:topic
+    RoutePath = emqx_mgmt_api_test_util:api_path(["routes", Topic]),
+    {ok, RouteResponse} = emqx_mgmt_api_test_util:request_api(get, RoutePath),
+    RouteData = emqx_json:decode(RouteResponse, [return_maps]),
+    ?assertEqual(Topic, maps:get(<<"topic">>, RouteData)),
+    ?assertEqual(atom_to_binary(node(), utf8), maps:get(<<"node">>, RouteData)).