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

This commit is contained in:
Shawn 2021-08-29 15:23:18 +08:00
parent c0eaa30064
commit ca327b7c55
3 changed files with 74 additions and 56 deletions

View File

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

View File

@ -188,7 +188,7 @@ gen_schema(_Conf) ->
#{type => string}. #{type => string}.
with_default_value(Type, Value) -> 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) ->
path_join(Path, "/"). path_join(Path, "/").

View File

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