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

refactor(listener): GET /listeners API returns full config of listeners

Shawn 4 лет назад
Родитель
Сommit
ca327b7c55

+ 29 - 17
apps/emqx/src/emqx_map_lib.erl

@@ -24,13 +24,16 @@
         , safe_atom_key_map/1
         , unsafe_atom_key_map/1
         , jsonable_map/1
-        , jsonable_value/1
-        , deep_convert/2
+        , jsonable_map/2
+        , binary_string/1
+        , deep_convert/3
         ]).
 
 -export_type([config_key/0, config_key_path/0]).
 -type config_key() :: atom() | binary().
 -type config_key_path() :: [config_key()].
+-type convert_fun() :: fun((K::any(), V::any(), Args::list()) ->
+    {K1::any(), V1::any()} | drop).
 
 %%-----------------------------------------------------------------
 -spec deep_get(config_key_path(), map()) -> term().
@@ -100,15 +103,17 @@ deep_merge(BaseMap, NewMap) ->
         end, #{}, BaseMap),
     maps:merge(MergedBase, maps:with(NewKeys, NewMap)).
 
--spec deep_convert(map(), fun((K::any(), V::any()) -> {K1::any(), V1::any()})) -> map().
-deep_convert(Map, ConvFun) when is_map(Map) ->
+-spec deep_convert(map(), convert_fun(), Args::list()) -> map().
+deep_convert(Map, ConvFun, Args) when is_map(Map) ->
     maps:fold(fun(K, V, Acc) ->
-            {K1, V1} = ConvFun(K, deep_convert(V, ConvFun)),
-            Acc#{K1 => V1}
+            case apply(ConvFun, [K, deep_convert(V, ConvFun, Args) | Args]) of
+                drop -> Acc;
+                {K1, V1} -> Acc#{K1 => V1}
+            end
         end, #{}, Map);
-deep_convert(ListV, ConvFun) when is_list(ListV) ->
-    [deep_convert(V, ConvFun) || V <- ListV];
-deep_convert(Val, _) -> Val.
+deep_convert(ListV, ConvFun, Args) when is_list(ListV) ->
+    [deep_convert(V, ConvFun, Args) || V <- ListV];
+deep_convert(Val, _, _Args) -> Val.
 
 -spec unsafe_atom_key_map(#{binary() | atom() => any()}) -> #{atom() => any()}.
 unsafe_atom_key_map(Map) ->
@@ -120,17 +125,24 @@ safe_atom_key_map(Map) ->
 
 -spec jsonable_map(map() | list()) -> map() | list().
 jsonable_map(Map) ->
-    deep_convert(Map, fun(K, V) ->
-            {jsonable_value(K), jsonable_value(V)}
-        end).
+    jsonable_map(Map, fun(K, V) -> {K, V} end).
+
+jsonable_map(Map, JsonableFun) ->
+    deep_convert(Map, fun binary_string_kv/3, [JsonableFun]).
+
+binary_string_kv(K, V, JsonableFun) ->
+    case JsonableFun(K, V) of
+        drop -> drop;
+        {K1, V1} -> {binary_string(K1), binary_string(V1)}
+    end.
 
-jsonable_value([]) -> [];
-jsonable_value(Val) when is_list(Val) ->
+binary_string([]) -> [];
+binary_string(Val) when is_list(Val) ->
     case io_lib:printable_unicode_list(Val) of
         true -> unicode:characters_to_binary(Val);
-        false -> Val
+        false -> [binary_string(V) || V <- Val]
     end;
-jsonable_value(Val) ->
+binary_string(Val) ->
     Val.
 
 %%---------------------------------------------------------------------------
@@ -138,4 +150,4 @@ covert_keys_to_atom(BinKeyMap, Conv) ->
     deep_convert(BinKeyMap, fun
             (K, V) when is_atom(K) -> {K, V};
             (K, V) when is_binary(K) -> {Conv(K), V}
-        end).
+        end, []).

+ 1 - 1
apps/emqx_management/src/emqx_mgmt_api_configs.erl

@@ -188,7 +188,7 @@ gen_schema(_Conf) ->
     #{type => string}.
 
 with_default_value(Type, Value) ->
-    Type#{example => emqx_map_lib:jsonable_value(Value)}.
+    Type#{example => emqx_map_lib:binary_string(Value)}.
 
 path_join(Path) ->
     path_join(Path, "/").

+ 44 - 38
apps/emqx_management/src/emqx_mgmt_api_listeners.erl

@@ -24,14 +24,9 @@
         , list_listeners_by_id/2
         , list_listeners_on_node/2
         , get_listener_by_id_on_node/2
-        , manage_listeners/2]).
-
--import(emqx_mgmt_util, [ schema/1
-                        , object_schema/2
-                        , object_array_schema/2
-                        , error_schema/2
-                        , properties/1
-                        ]).
+        , manage_listeners/2
+        , jsonable_resp/2
+        ]).
 
 -export([format/1]).
 
@@ -53,17 +48,23 @@ api_spec() ->
         []
     }.
 
-properties() ->
-    properties([
-        {node, string, <<"Node">>},
-        {id, string, <<"Identifier">>},
-        {acceptors, integer, <<"Number of Acceptor process">>},
-        {max_conn, integer, <<"Maximum number of allowed connection">>},
-        {type, string, <<"Listener type">>},
-        {listen_on, string, <<"Listener port">>},
-        {running, boolean, <<"Open or close">>},
-        {auth, boolean, <<"Has auth">>}
-    ]).
+-define(TYPES, [tcp, ssl, ws, wss, quic]).
+req_schema() ->
+    Schema = [emqx_mgmt_api_configs:gen_schema(
+        emqx:get_raw_config([listeners, T, default], #{}))
+     || T <- ?TYPES],
+    #{oneOf => Schema}.
+
+resp_schema() ->
+    #{oneOf := Schema} = req_schema(),
+    AddMetadata = fun(Prop) ->
+        Prop#{running => #{type => boolean},
+              id => #{type => string},
+              node => #{type => string}}
+    end,
+    Schema1 = [S#{properties => AddMetadata(Prop)}
+               || S = #{properties := Prop} <- Schema],
+    #{oneOf => Schema1}.
 
 api_list_listeners() ->
     Metadata = #{
@@ -71,7 +72,7 @@ api_list_listeners() ->
             description => <<"List listeners from all nodes in the cluster">>,
             responses => #{
                 <<"200">> =>
-                    object_array_schema(properties(), <<"List listeners successfully">>)}}},
+                    emqx_mgmt_util:array_schema(resp_schema(), <<"List listeners successfully">>)}}},
     {"/listeners", Metadata, list_listeners}.
 
 api_list_listeners_by_id() ->
@@ -81,9 +82,9 @@ api_list_listeners_by_id() ->
             parameters => [param_path_id()],
             responses => #{
                 <<"404">> =>
-                    error_schema(?LISTENER_NOT_FOUND, ['BAD_LISTENER_ID']),
+                    emqx_mgmt_util:error_schema(?LISTENER_NOT_FOUND, ['BAD_LISTENER_ID']),
                 <<"200">> =>
-                    object_array_schema(properties(), <<"List listeners successfully">>)}}},
+                    emqx_mgmt_util:array_schema(resp_schema(), <<"List listeners successfully">>)}}},
     {"/listeners/:id", Metadata, list_listeners_by_id}.
 
 api_list_listeners_on_node() ->
@@ -92,7 +93,7 @@ api_list_listeners_on_node() ->
             description => <<"List listeners in one node">>,
             parameters => [param_path_node()],
             responses => #{
-                <<"200">> => object_schema(properties(), <<"List listeners successfully">>)}}},
+                <<"200">> => emqx_mgmt_util:object_schema(resp_schema(), <<"List listeners successfully">>)}}},
     {"/nodes/:node/listeners", Metadata, list_listeners_on_node}.
 
 api_get_listener_by_id_on_node() ->
@@ -102,10 +103,10 @@ api_get_listener_by_id_on_node() ->
             parameters => [param_path_node(), param_path_id()],
             responses => #{
                 <<"404">> =>
-                    error_schema(?NODE_LISTENER_NOT_FOUND,
+                    emqx_mgmt_util:error_schema(?NODE_LISTENER_NOT_FOUND,
                         ['BAD_NODE_NAME', 'BAD_LISTENER_ID']),
                 <<"200">> =>
-                    object_schema(properties(), <<"Get listener successfully">>)}}},
+                    emqx_mgmt_util:object_schema(resp_schema(), <<"Get listener successfully">>)}}},
     {"/nodes/:node/listeners/:id", Metadata, get_listener_by_id_on_node}.
 
 api_manage_listeners() ->
@@ -116,9 +117,9 @@ api_manage_listeners() ->
                 param_path_id(),
                 param_path_operation()],
             responses => #{
-                <<"500">> => error_schema(<<"Operation Failed">>, ['INTERNAL_ERROR']),
-                <<"200">> => schema(<<"Operation success">>)}}},
-    {"/listeners/:id/:operation", Metadata, manage_listeners}.
+                <<"500">> => emqx_mgmt_util:error_schema(<<"Operation Failed">>, ['INTERNAL_ERROR']),
+                <<"200">> => emqx_mgmt_util:schema(<<"Operation success">>)}}},
+    {"/listeners/:id/operation/:operation", Metadata, manage_listeners}.
 
 api_manage_listeners_on_node() ->
     Metadata = #{
@@ -129,9 +130,9 @@ api_manage_listeners_on_node() ->
                 param_path_id(),
                 param_path_operation()],
             responses => #{
-                <<"500">> => error_schema(<<"Operation Failed">>, ['INTERNAL_ERROR']),
-                <<"200">> => schema(<<"Operation success">>)}}},
-    {"/nodes/:node/listeners/:id/:operation", Metadata, manage_listeners}.
+                <<"500">> => emqx_mgmt_util:error_schema(<<"Operation Failed">>, ['INTERNAL_ERROR']),
+                <<"200">> => emqx_mgmt_util:schema(<<"Operation success">>)}}},
+    {"/nodes/:node/listeners/:id/operation/:operation", Metadata, manage_listeners}.
 
 %%%==============================================================================================
 %% parameters
@@ -247,16 +248,12 @@ format({error, Reason}) ->
     {error, Reason};
 
 format({ID, Conf}) ->
-    {Type, _Name} = emqx_listeners:parse_listener_id(ID),
-    #{
+    emqx_map_lib:jsonable_map(Conf#{
         id              => ID,
         node            => maps:get(node, Conf),
-        acceptors       => maps:get(acceptors, Conf),
-        max_conn        => maps:get(max_connections, Conf),
-        type            => Type,
-        listen_on       => list_to_binary(esockd:to_string(maps:get(bind, Conf))),
         running         => trans_running(Conf)
-    }.
+    }, fun ?MODULE:jsonable_resp/2).
+
 trans_running(Conf) ->
     case maps:get(running, Conf) of
         {error, _} ->
@@ -265,6 +262,15 @@ trans_running(Conf) ->
             Running
     end.
 
+jsonable_resp(bind, Port) when is_integer(Port) ->
+    {bind, Port};
+jsonable_resp(bind, {Addr, Port}) when is_tuple(Addr); is_integer(Port)->
+    {bind, inet:ntoa(Addr) ++ ":" ++ integer_to_list(Port)};
+jsonable_resp(user_lookup_fun, _) ->
+    drop;
+jsonable_resp(K, V) ->
+    {K, V}.
+
 atom(B) when is_binary(B) -> binary_to_atom(B, utf8);
 atom(S) when is_list(S) -> list_to_atom(S);
 atom(A) when is_atom(A) -> A.