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
, 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_value([]) -> [];
jsonable_value(Val) when is_list(Val) ->
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.
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, []).

View File

@ -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, "/").

View File

@ -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.