diff --git a/apps/emqx_gateway/etc/emqx_gateway.conf.example b/apps/emqx_gateway/etc/emqx_gateway.conf.example
index 03a9a7868..49977c473 100644
--- a/apps/emqx_gateway/etc/emqx_gateway.conf.example
+++ b/apps/emqx_gateway/etc/emqx_gateway.conf.example
@@ -70,9 +70,9 @@ gateway.stomp {
## SSL options
## See ${example_common_ssl_options} for more information
ssl.versions = ["tlsv1.3", "tlsv1.2", "tlsv1.1", "tlsv1"]
- ssl.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
- ssl.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
ssl.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
+ ssl.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
+ ssl.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
#ssl.verify = verify_none
#ssl.fail_if_no_peer_cert = false
#ssl.server_name_indication = disable
diff --git a/apps/emqx_gateway/src/emqx_gateway_api.erl b/apps/emqx_gateway/src/emqx_gateway_api.erl
index b7e96770b..4836950d3 100644
--- a/apps/emqx_gateway/src/emqx_gateway_api.erl
+++ b/apps/emqx_gateway/src/emqx_gateway_api.erl
@@ -318,20 +318,26 @@ listeners_schema(?R_REF(_Mod, udp_tcp_listeners)) ->
%% examples
examples_gateway_confs() ->
- #{ a_stomp_gateway =>
- #{ enable => true
- , enable_stats => true
- , idle_timeout => <<"30s">>
- , mountpoint => <<"stomp/">>
- , frame =>
- #{ max_header => 10
- , make_header_length => 1024
- , max_body_length => 65535
+ #{ stomp_gateway =>
+ #{ summary => <<"A simple STOMP gateway configs">>
+ , value =>
+ #{ enable => true
+ , enable_stats => true
+ , idle_timeout => <<"30s">>
+ , mountpoint => <<"stomp/">>
+ , frame =>
+ #{ max_header => 10
+ , make_header_length => 1024
+ , max_body_length => 65535
+ }
}
}
- , a_mqttsn_gateway =>
- #{ enable => true
- , enable_stats => true
+ , mqttsn_gateway =>
+ #{ summary => <<"A simple MQTT-SN gateway configs">>
+ , value =>
+ #{ enable => true
+ , enable_stats => true
+ }
}
}.
diff --git a/apps/emqx_gateway/src/emqx_gateway_api_clients.erl b/apps/emqx_gateway/src/emqx_gateway_api_clients.erl
index 40e058e32..c7a77eb02 100644
--- a/apps/emqx_gateway/src/emqx_gateway_api_clients.erl
+++ b/apps/emqx_gateway/src/emqx_gateway_api_clients.erl
@@ -16,12 +16,30 @@
-module(emqx_gateway_api_clients).
--behaviour(minirest_api).
-
+-include("emqx_gateway_http.hrl").
+-include_lib("typerefl/include/types.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
+-include_lib("emqx/include/emqx_placeholder.hrl").
-include_lib("emqx/include/logger.hrl").
-%% minirest behaviour callbacks
--export([api_spec/0]).
+-behaviour(minirest_api).
+
+-import(hoconsc, [mk/2, ref/1, ref/2]).
+
+-import(emqx_gateway_http,
+ [ return_http_error/2
+ , with_gateway/2
+ ]).
+
+%% minirest/dashbaord_swagger behaviour callbacks
+-export([ api_spec/0
+ , paths/0
+ , schema/1
+ ]).
+
+-export([ roots/0
+ , fields/1
+ ]).
%% http handlers
-export([ clients/2
@@ -34,27 +52,18 @@
, format_channel_info/1
]).
--import(emqx_gateway_http,
- [ return_http_error/2
- , with_gateway/2
- , schema_bad_request/0
- , schema_not_found/0
- , schema_internal_error/0
- , schema_no_content/0
- ]).
-
%%--------------------------------------------------------------------
%% APIs
%%--------------------------------------------------------------------
api_spec() ->
- {metadata(apis()), []}.
+ emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
-apis() ->
- [ {"/gateway/:name/clients", clients}
- , {"/gateway/:name/clients/:clientid", clients_insta}
- , {"/gateway/:name/clients/:clientid/subscriptions", subscriptions}
- , {"/gateway/:name/clients/:clientid/subscriptions/:topic", subscriptions}
+paths() ->
+ [ "/gateway/:name/clients"
+ , "/gateway/:name/clients/:clientid"
+ , "/gateway/:name/clients/:clientid/subscriptions"
+ , "/gateway/:name/clients/:clientid/subscriptions/:topic"
].
-define(CLIENT_QS_SCHEMA,
@@ -88,14 +97,16 @@ clients(get, #{ bindings := #{name := Name0}
TabName = emqx_gateway_cm:tabname(info, GwName),
case maps:get(<<"node">>, Params, undefined) of
undefined ->
- Response = emqx_mgmt_api:cluster_query(Params, TabName,
- ?CLIENT_QS_SCHEMA, ?query_fun),
+ Response = emqx_mgmt_api:cluster_query(
+ Params, TabName,
+ ?CLIENT_QS_SCHEMA, ?query_fun),
emqx_mgmt_util:generate_response(Response);
Node1 ->
Node = binary_to_atom(Node1, utf8),
ParamsWithoutNode = maps:without([<<"node">>], Params),
- Response = emqx_mgmt_api:node_query(Node, ParamsWithoutNode,
- TabName, ?CLIENT_QS_SCHEMA, ?query_fun),
+ Response = emqx_mgmt_api:node_query(
+ Node, ParamsWithoutNode,
+ TabName, ?CLIENT_QS_SCHEMA, ?query_fun),
emqx_mgmt_util:generate_response(Response)
end
end).
@@ -105,8 +116,9 @@ clients_insta(get, #{ bindings := #{name := Name0,
}) ->
ClientId = emqx_mgmt_util:urldecode(ClientId0),
with_gateway(Name0, fun(GwName, _) ->
- case emqx_gateway_http:lookup_client(GwName, ClientId,
- {?MODULE, format_channel_info}) of
+ case emqx_gateway_http:lookup_client(
+ GwName, ClientId,
+ {?MODULE, format_channel_info}) of
[ClientInfo] ->
{200, ClientInfo};
[ClientInfo | _More] ->
@@ -154,7 +166,8 @@ subscriptions(post, #{ bindings := #{name := Name0,
{undefined, _} ->
return_http_error(400, "Miss topic property");
{Topic, QoS} ->
- case emqx_gateway_http:client_subscribe(GwName, ClientId, Topic, QoS) of
+ case emqx_gateway_http:client_subscribe(
+ GwName, ClientId, Topic, QoS) of
{error, Reason} ->
return_http_error(404, Reason);
ok ->
@@ -204,8 +217,9 @@ query(Tab, {Qs, []}, Continuation, Limit) ->
query(Tab, {Qs, Fuzzy}, Continuation, Limit) ->
Ms = qs2ms(Qs),
FuzzyFilterFun = fuzzy_filter_fun(Fuzzy),
- emqx_mgmt_api:select_table_with_count(Tab, {Ms, FuzzyFilterFun}, Continuation, Limit,
- fun format_channel_info/1).
+ emqx_mgmt_api:select_table_with_count(
+ Tab, {Ms, FuzzyFilterFun}, Continuation, Limit,
+ fun format_channel_info/1).
qs2ms(Qs) ->
{MtchHead, Conds} = qs2ms(Qs, 2, {#{}, []}),
@@ -218,8 +232,10 @@ qs2ms([{Key, '=:=', Value} | Rest], N, {MtchHead, Conds}) ->
NMtchHead = emqx_mgmt_util:merge_maps(MtchHead, ms(Key, Value)),
qs2ms(Rest, N, {NMtchHead, Conds});
qs2ms([Qs | Rest], N, {MtchHead, Conds}) ->
- Holder = binary_to_atom(iolist_to_binary(["$", integer_to_list(N)]), utf8),
- NMtchHead = emqx_mgmt_util:merge_maps(MtchHead, ms(element(1, Qs), Holder)),
+ Holder = binary_to_atom(
+ iolist_to_binary(["$", integer_to_list(N)]), utf8),
+ NMtchHead = emqx_mgmt_util:merge_maps(
+ MtchHead, ms(element(1, Qs), Holder)),
NConds = put_conds(Qs, Holder, Conds),
qs2ms(Rest, N+1, {NMtchHead, NConds}).
@@ -271,12 +287,14 @@ escape(B) when is_binary(B) ->
run_fuzzy_filter(_, []) ->
true;
-run_fuzzy_filter(E = {_, #{clientinfo := ClientInfo}, _}, [{Key, _, RE} | Fuzzy]) ->
+run_fuzzy_filter(E = {_, #{clientinfo := ClientInfo}, _},
+ [{Key, _, RE} | Fuzzy]) ->
Val = case maps:get(Key, ClientInfo, "") of
undefined -> "";
V -> V
end,
- re:run(Val, RE, [{capture, none}]) == match andalso run_fuzzy_filter(E, Fuzzy).
+ re:run(Val, RE, [{capture, none}]) == match
+ andalso run_fuzzy_filter(E, Fuzzy).
%%--------------------------------------------------------------------
%% format funcs
@@ -294,15 +312,19 @@ format_channel_info({_, Infos, Stats} = R) ->
, {port, {peername, ConnInfo, fun peer_to_port/1}}
, {is_bridge, ClientInfo, false}
, {connected_at,
- {connected_at, ConnInfo, fun emqx_gateway_utils:unix_ts_to_rfc3339/1}}
+ {connected_at, ConnInfo,
+ fun emqx_gateway_utils:unix_ts_to_rfc3339/1}}
, {disconnected_at,
- {disconnected_at, ConnInfo, fun emqx_gateway_utils:unix_ts_to_rfc3339/1}}
- , {connected, {conn_state, Infos, fun conn_state_to_connected/1}}
+ {disconnected_at, ConnInfo,
+ fun emqx_gateway_utils:unix_ts_to_rfc3339/1}}
+ , {connected, {conn_state, Infos,
+ fun conn_state_to_connected/1}}
, {keepalive, ClientInfo, 0}
, {clean_start, ConnInfo, true}
, {expiry_interval, ConnInfo, 0}
, {created_at,
- {created_at, SessInfo, fun emqx_gateway_utils:unix_ts_to_rfc3339/1}}
+ {created_at, SessInfo,
+ fun emqx_gateway_utils:unix_ts_to_rfc3339/1}}
, {subscriptions_cnt, Stats, 0}
, {subscriptions_max, Stats, infinity}
, {inflight_cnt, Stats, 0}
@@ -384,275 +406,338 @@ conn_state_to_connected(_) -> false.
%% Swagger defines
%%--------------------------------------------------------------------
-metadata(APIs) ->
- metadata(APIs, []).
-metadata([], APIAcc) ->
- lists:reverse(APIAcc);
-metadata([{Path, Fun} | More], APIAcc) ->
- Methods = [get, post, put, delete, patch],
- Mds = lists:foldl(fun(M, Acc) ->
- try
- Acc#{M => swagger(Path, M)}
- catch
- error : function_clause ->
- Acc
- end
- end, #{}, Methods),
- metadata(More, [{Path, Mds, Fun} | APIAcc]).
-
-swagger("/gateway/:name/clients", get) ->
- #{ description => <<"Get the gateway clients">>
- , parameters => params_client_query()
- , responses =>
- #{ <<"400">> => schema_bad_request()
- , <<"404">> => schema_not_found()
- , <<"500">> => schema_internal_error()
- , <<"200">> => schema_clients_list()
- }
+schema("/gateway/:name/clients") ->
+ #{ 'operationId' => clients
+ , get =>
+ #{ description => <<"Get the gateway client list">>
+ , parameters => params_client_query()
+ , responses =>
+ ?STANDARD_RESP(
+ #{ 200 => emqx_dashboard_swagger:schema_with_examples(
+ hoconsc:array(ref(client)),
+ examples_client_list())})
+ }
};
-swagger("/gateway/:name/clients/:clientid", get) ->
- #{ description => <<"Get the gateway client infomation">>
- , parameters => params_client_insta()
- , responses =>
- #{ <<"400">> => schema_bad_request()
- , <<"404">> => schema_not_found()
- , <<"500">> => schema_internal_error()
- , <<"200">> => schema_client()
- }
+schema("/gateway/:name/clients/:clientid") ->
+ #{ 'operationId' => clients_insta
+ , get =>
+ #{ description => <<"Get the gateway client infomation">>
+ , parameters => params_client_insta()
+ , responses =>
+ ?STANDARD_RESP(
+ #{ 200 => emqx_dashboard_swagger:schema_with_examples(
+ ref(client),
+ examples_client())})
+ }
+ , delete =>
+ #{ description => <<"Kick out the gateway client">>
+ , parameters => params_client_insta()
+ , responses =>
+ ?STANDARD_RESP(#{204 => <<"Kicked">>})
+ }
};
-swagger("/gateway/:name/clients/:clientid", delete) ->
- #{ description => <<"Kick out the gateway client">>
- , parameters => params_client_insta()
- , responses =>
- #{ <<"400">> => schema_bad_request()
- , <<"404">> => schema_not_found()
- , <<"500">> => schema_internal_error()
- , <<"204">> => schema_no_content()
- }
+schema("/gateway/:name/clients/:clientid/subscriptions") ->
+ #{ 'operationId' => subscriptions
+ , get =>
+ #{ description => <<"Get the gateway client subscriptions">>
+ , parameters => params_client_insta()
+ , responses =>
+ ?STANDARD_RESP(
+ #{ 200 => emqx_dashboard_swagger:schema_with_examples(
+ hoconsc:array(ref(subscription)),
+ examples_subsctiption_list())})
+ }
+ , post =>
+ #{ description => <<"Create a subscription membership">>
+ , parameters => params_client_insta()
+ %% FIXME:
+ , requestBody => emqx_dashboard_swagger:schema_with_examples(
+ ref(subscription),
+ examples_subsctiption())
+ , responses =>
+ ?STANDARD_RESP(
+ #{ 201 => emqx_dashboard_swagger:schema_with_examples(
+ ref(subscription),
+ examples_subsctiption())})
+ }
};
-swagger("/gateway/:name/clients/:clientid/subscriptions", get) ->
- #{ description => <<"Get the gateway client subscriptions">>
- , parameters => params_client_insta()
- , responses =>
- #{ <<"400">> => schema_bad_request()
- , <<"404">> => schema_not_found()
- , <<"500">> => schema_internal_error()
- , <<"200">> => schema_subscription_list()
- }
- };
-swagger("/gateway/:name/clients/:clientid/subscriptions", post) ->
- #{ description => <<"Get the gateway client subscriptions">>
- , parameters => params_client_insta()
- , requestBody => schema_subscription()
- , responses =>
- #{ <<"400">> => schema_bad_request()
- , <<"404">> => schema_not_found()
- , <<"500">> => schema_internal_error()
- , <<"204">> => schema_no_content()
- }
- };
-swagger("/gateway/:name/clients/:clientid/subscriptions/:topic", delete) ->
- #{ description => <<"Unsubscribe the topic for client">>
- , parameters => params_topic_name_in_path() ++ params_client_insta()
- , responses =>
- #{ <<"400">> => schema_bad_request()
- , <<"404">> => schema_not_found()
- , <<"500">> => schema_internal_error()
- , <<"204">> => schema_no_content()
- }
+schema("/gateway/:name/clients/:clientid/subscriptions/:topic") ->
+ #{ 'operationId' => subscriptions
+ , delete =>
+ #{ description => <<"Delete a subscriptions membership">>
+ , parameters => params_topic_name_in_path() ++ params_client_insta()
+ , responses =>
+ ?STANDARD_RESP(#{204 => <<"Unsubscribed">>})
+ }
}.
params_client_query() ->
params_gateway_name_in_path()
++ params_client_searching_in_qs()
- ++ emqx_mgmt_util:page_params().
+ ++ params_paging().
params_client_insta() ->
params_clientid_in_path()
++ params_gateway_name_in_path().
params_client_searching_in_qs() ->
- queries(
- [ {node, string}
- , {clientid, string}
- , {username, string}
- , {ip_address, string}
- , {conn_state, string}
- , {proto_ver, string}
- , {clean_start, boolean}
- , {like_clientid, string}
- , {like_username, string}
- , {gte_created_at, string}
- , {lte_created_at, string}
- , {gte_connected_at, string}
- , {lte_connected_at, string}
- ]).
+ M = #{in => query, nullable => true},
+ [ {node,
+ mk(binary(),
+ M#{desc => <<"Match the client's node name">>})}
+ , {clientid,
+ mk(binary(),
+ M#{desc => <<"Match the client's ID">>})}
+ , {username,
+ mk(binary(),
+ M#{desc => <<"Match the client's Username">>})}
+ , {ip_address,
+ mk(binary(),
+ M#{desc => <<"Match the client's ip address">>})}
+ , {conn_state,
+ mk(binary(),
+ M#{desc => <<"Match the client's connection state">>})}
+ , {proto_ver,
+ mk(binary(),
+ M#{desc => <<"Match the client's protocol version">>})}
+ , {clean_start,
+ mk(boolean(),
+ M#{desc => <<"Match the client's clean start flag">>})}
+ , {like_clientid,
+ mk(binary(),
+ M#{desc => <<"Use sub-string to match client's ID">>})}
+ , {like_username,
+ mk(binary(),
+ M#{desc => <<"Use sub-string to match client's username">>})}
+ , {gte_created_at,
+ mk(binary(),
+ M#{desc => <<"Match the session created datetime greater than "
+ "a certain value">>})}
+ , {lte_created_at,
+ mk(binary(),
+ M#{desc => <<"Match the session created datetime less than "
+ "a certain value">>})}
+ , {gte_connected_at,
+ mk(binary(),
+ M#{desc => <<"Match the client socket connected datetime greater "
+ "than a certain value">>})}
+ , {lte_connected_at,
+ mk(binary(),
+ M#{desc => <<"Match the client socket connected datatime less than "
+ " a certain value">>})}
+ ].
+
+params_paging() ->
+ [ {page,
+ mk(integer(),
+ #{ in => query
+ , nullable => true
+ , desc => <<"Page Index">>})}
+ , {limit,
+ mk(integer(),
+ #{ in => query
+ , desc => <<"Page Limit">>
+ , nullable => true})}
+ ].
params_gateway_name_in_path() ->
- [#{ name => name
- , in => path
- , schema => #{type => string}
- , required => true
- }].
+ [{name,
+ mk(binary(),
+ #{ in => path
+ , desc => <<"Gateway Name">>
+ })}
+ ].
params_clientid_in_path() ->
- [#{ name => clientid
- , in => path
- , schema => #{type => string}
- , required => true
- }].
+ [{clientid,
+ mk(binary(),
+ #{ in => path
+ , desc => <<"Client ID">>
+ })}
+ ].
params_topic_name_in_path() ->
- [#{ name => topic
- , in => path
- , schema => #{type => string}
- , required => true
- }].
-
-queries(Ls) ->
- lists:map(fun({K, Type}) ->
- #{name => K, in => query,
- schema => #{type => Type},
- required => false
- }
- end, Ls).
+ [{topic,
+ mk(binary(),
+ #{ in => path
+ , desc => <<"Topic Filter/Name">>
+ })}
+ ].
%%--------------------------------------------------------------------
%% schemas
-schema_clients_list() ->
- emqx_mgmt_util:page_schema(
- #{ type => object
- , properties => properties_client()
- }
- ).
+roots() ->
+ [ client
+ , subscription
+ ].
-schema_client() ->
- emqx_mgmt_util:schema(
- #{ type => object
- , properties => properties_client()
- }).
-
-schema_subscription_list() ->
- emqx_mgmt_util:array_schema(
- #{ type => object
- , properties => properties_subscription()
- },
- <<"Client subscriptions">>
- ).
-
-schema_subscription() ->
- emqx_mgmt_util:schema(
- #{ type => object
- , properties => properties_subscription()
- }
- ).
+fields(client) ->
+ %% XXX: enum for every protocol's client
+ [ {node,
+ mk(string(),
+ #{ desc => <<"Name of the node to which the client is "
+ "connected">>})}
+ , {clientid,
+ mk(string(),
+ #{ desc => <<"Client identifier">>})}
+ , {username,
+ mk(string(),
+ #{ desc => <<"Username of client when connecting">>})}
+ , {proto_name,
+ mk(string(),
+ #{ desc => <<"Client protocol name">>})}
+ , {proto_ver,
+ mk(string(),
+ #{ desc => <<"Protocol version used by the client">>})}
+ , {ip_address,
+ mk(string(),
+ #{ desc => <<"Client's IP address">>})}
+ , {port,
+ mk(integer(),
+ #{ desc => <<"Client's port">>})}
+ , {is_bridge,
+ mk(boolean(),
+ #{ desc => <<"Indicates whether the client is connected via "
+ "bridge">>})}
+ , {connected_at,
+ mk(string(),
+ #{ desc => <<"Client connection time">>})}
+ , {disconnected_at,
+ mk(string(),
+ #{ desc => <<"Client offline time, This field is only valid and "
+ "returned when connected is false">>})}
+ , {connected,
+ mk(boolean(),
+ #{ desc => <<"Whether the client is connected">>})}
+ %% FIXME: the will_msg attribute is not a general attribute
+ %% for every protocol. But it should be returned to frontend if someone
+ %% want it
+ %%
+ %, {will_msg,
+ % mk(string(),
+ % #{ desc => <<"Client will message">>})}
+ %, {zone,
+ % mk(string(),
+ % #{ desc => <<"Indicate the configuration group used by the "
+ % "client">>})}
+ , {keepalive,
+ mk(integer(),
+ #{ desc => <<"keepalive time, with the unit of second">>})}
+ , {clean_start,
+ mk(boolean(),
+ #{ desc => <<"Indicate whether the client is using a brand "
+ "new session">>})}
+ , {expiry_interval,
+ mk(integer(),
+ #{ desc => <<"Session expiration interval, with the unit of "
+ "second">>})}
+ , {created_at,
+ mk(string(),
+ #{ desc => <<"Session creation time">>})}
+ , {subscriptions_cnt,
+ mk(integer(),
+ #{ desc => <<"Number of subscriptions established by this "
+ "client">>})}
+ , {subscriptions_max,
+ mk(integer(),
+ #{ desc => <<"Maximum number of subscriptions allowed by this "
+ "client">>})}
+ , {inflight_cnt,
+ mk(integer(),
+ #{ desc => <<"Current length of inflight">>})}
+ , {inflight_max,
+ mk(integer(),
+ #{ desc => <<"Maximum length of inflight">>})}
+ , {mqueue_len,
+ mk(integer(),
+ #{ desc => <<"Current length of message queue">>})}
+ , {mqueue_max,
+ mk(integer(),
+ #{ desc => <<"Maximum length of message queue">>})}
+ , {mqueue_dropped,
+ mk(integer(),
+ #{ desc => <<"Number of messages dropped by the message queue "
+ "due to exceeding the length">>})}
+ , {awaiting_rel_cnt,
+ mk(integer(),
+ #{ desc => <<"Number of awaiting PUBREC packet">>})}
+ , {awaiting_rel_max,
+ mk(integer(),
+ #{ desc => <<"Maximum allowed number of awaiting PUBREC "
+ "packet">>})}
+ , {recv_oct,
+ mk(integer(),
+ #{ desc => <<"Number of bytes received by EMQ X Broker">>})}
+ , {recv_cnt,
+ mk(integer(),
+ #{ desc => <<"Number of TCP packets received">>})}
+ , {recv_pkt,
+ mk(integer(),
+ #{ desc => <<"Number of MQTT packets received">>})}
+ , {recv_msg,
+ mk(integer(),
+ #{ desc => <<"Number of PUBLISH packets received">>})}
+ , {send_oct,
+ mk(integer(),
+ #{ desc => <<"Number of bytes sent">>})}
+ , {send_cnt,
+ mk(integer(),
+ #{ desc => <<"Number of TCP packets sent">>})}
+ , {send_pkt,
+ mk(integer(),
+ #{ desc => <<"Number of MQTT packets sent">>})}
+ , {send_msg,
+ mk(integer(),
+ #{ desc => <<"Number of PUBLISH packets sent">>})}
+ , {mailbox_len,
+ mk(integer(),
+ #{ desc => <<"Process mailbox size">>})}
+ , {heap_size,
+ mk(integer(),
+ #{ desc => <<"Process heap size with the unit of byte">>})}
+ , {reductions,
+ mk(integer(),
+ #{ desc => <<"Erlang reduction">>})}
+ ];
+fields(subscription) ->
+ [ {topic,
+ mk(string(),
+ #{ desc => <<"Topic Fillter">>})}
+ , {qos,
+ mk(integer(),
+ #{ desc => <<"QoS level, enum: 0, 1, 2">>})}
+ , {nl,
+ mk(integer(), %% FIXME: why not boolean?
+ #{ desc => <<"No Local option, enum: 0, 1">>})}
+ , {rap,
+ mk(integer(),
+ #{ desc => <<"Retain as Published option, enum: 0, 1">>})}
+ , {rh,
+ mk(integer(),
+ #{ desc => <<"Retain Handling option, enum: 0, 1, 2">>})}
+ , {sub_props,
+ mk(ref(extra_sub_props),
+ #{desc => <<"Subscription properties">>})}
+ ];
+fields(extra_sub_props) ->
+ [ {subid,
+ mk(string(),
+ #{ desc => <<"Only stomp protocol, an uniquely identity for "
+ "the subscription. range: 1-65535.">>})}
+ ].
%%--------------------------------------------------------------------
-%% properties defines
+%% examples
-properties_client() ->
- %% FIXME: enum for every protocol's client
- emqx_mgmt_util:properties(
- [ {node, string,
- <<"Name of the node to which the client is connected">>}
- , {clientid, string,
- <<"Client identifier">>}
- , {username, string,
- <<"Username of client when connecting">>}
- , {proto_name, string,
- <<"Client protocol name">>}
- , {proto_ver, string,
- <<"Protocol version used by the client">>}
- , {ip_address, string,
- <<"Client's IP address">>}
- , {port, integer,
- <<"Client's port">>}
- , {is_bridge, boolean,
- <<"Indicates whether the client is connectedvia bridge">>}
- , {connected_at, string,
- <<"Client connection time">>}
- , {disconnected_at, string,
- <<"Client offline time, This field is only valid and returned "
- "when connected is false">>}
- , {connected, boolean,
- <<"Whether the client is connected">>}
- %% FIXME: the will_msg attribute is not a general attribute
- %% for every protocol. But it should be returned to frontend if someone
- %% want it
- %%
- %, {will_msg, string,
- % <<"Client will message">>}
- %, {zone, string,
- % <<"Indicate the configuration group used by the client">>}
- , {keepalive, integer,
- <<"keepalive time, with the unit of second">>}
- , {clean_start, boolean,
- <<"Indicate whether the client is using a brand new session">>}
- , {expiry_interval, integer,
- <<"Session expiration interval, with the unit of second">>}
- , {created_at, string,
- <<"Session creation time">>}
- , {subscriptions_cnt, integer,
- <<"Number of subscriptions established by this client">>}
- , {subscriptions_max, integer,
- <<"v4 api name [max_subscriptions] Maximum number of "
- "subscriptions allowed by this client">>}
- , {inflight_cnt, integer,
- <<"Current length of inflight">>}
- , {inflight_max, integer,
- <<"v4 api name [max_inflight]. Maximum length of inflight">>}
- , {mqueue_len, integer,
- <<"Current length of message queue">>}
- , {mqueue_max, integer,
- <<"v4 api name [max_mqueue]. Maximum length of message queue">>}
- , {mqueue_dropped, integer,
- <<"Number of messages dropped by the message queue due to "
- "exceeding the length">>}
- , {awaiting_rel_cnt, integer,
- <<"v4 api name [awaiting_rel] Number of awaiting PUBREC packet">>}
- , {awaiting_rel_max, integer,
- <<"v4 api name [max_awaiting_rel]. Maximum allowed number of "
- "awaiting PUBREC packet">>}
- , {recv_oct, integer,
- <<"Number of bytes received by EMQ X Broker (the same below)">>}
- , {recv_cnt, integer,
- <<"Number of TCP packets received">>}
- , {recv_pkt, integer,
- <<"Number of MQTT packets received">>}
- , {recv_msg, integer,
- <<"Number of PUBLISH packets received">>}
- , {send_oct, integer,
- <<"Number of bytes sent">>}
- , {send_cnt, integer,
- <<"Number of TCP packets sent">>}
- , {send_pkt, integer,
- <<"Number of MQTT packets sent">>}
- , {send_msg, integer,
- <<"Number of PUBLISH packets sent">>}
- , {mailbox_len, integer,
- <<"Process mailbox size">>}
- , {heap_size, integer,
- <<"Process heap size with the unit of byte">>}
- , {reductions, integer,
- <<"Erlang reduction">>}
- ]).
+examples_client_list() ->
+ #{}.
-properties_subscription() ->
- ExtraProps = [ {subid, string,
- <<"Only stomp protocol, an uniquely identity for "
- "the subscription. range: 1-65535.">>}
- ],
- emqx_mgmt_util:properties(
- [ {topic, string,
- <<"Topic Fillter">>}
- , {qos, integer,
- <<"QoS level, enum: 0, 1, 2">>}
- , {nl, integer, %% FIXME: why not boolean?
- <<"No Local option, enum: 0, 1">>}
- , {rap, integer,
- <<"Retain as Published option, enum: 0, 1">>}
- , {rh, integer,
- <<"Retain Handling option, enum: 0, 1, 2">>}
- , {sub_props, object, ExtraProps}
- ]).
+examples_client() ->
+ #{}.
+
+examples_subsctiption_list() ->
+ #{}.
+
+examples_subsctiption() ->
+ #{}.
diff --git a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl
index 44e3842cc..e73dd707e 100644
--- a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl
+++ b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl
@@ -225,7 +225,7 @@ schema("/gateway/:name/listeners") ->
, parameters => params_gateway_name_in_path()
, responses =>
?STANDARD_RESP(
- #{ 200 => emqx_dashboard_swagger:schema_with_examples(
+ #{ 200 => emqx_dashboard_swagger:schema_with_example(
hoconsc:array(ref(listener)),
examples_listener_list())
})
@@ -240,7 +240,7 @@ schema("/gateway/:name/listeners") ->
?STANDARD_RESP(
#{ 201 => emqx_dashboard_swagger:schema_with_examples(
ref(listener),
- examples_listener_list())
+ examples_listener())
})
}
};
@@ -580,7 +580,96 @@ common_listener_opts() ->
%% examples
examples_listener_list() ->
- #{stomp_listeners => [examples_listener()]}.
+ [Config || #{value := Config} <- maps:values(examples_listener())].
examples_listener() ->
- #{}.
+ #{ tcp_listener=>
+ #{ summary => <<"A simple tcp listener example">>
+ , value =>
+ #{ bind => <<"61613">>
+ , acceptors => 16
+ , max_connections => 1024000
+ , max_conn_rate => 1000
+ , tcp =>
+ #{ active_n => 100
+ , backlog => 1024
+ , send_timeout => <<"15s">>
+ , send_timeout_close => true
+ , recbuf => <<"10KB">>
+ , sndbuf => <<"10KB">>
+ , buffer => <<"10KB">>
+ , high_watermark => <<"1MB">>
+ , nodelay => false
+ , reuseaddr => true
+ }
+ }
+ }
+ , ssl_listener =>
+ #{ summary => <<"A simple ssl listener example">>
+ , value =>
+ #{ bind => <<"61614">>
+ , acceptors => 16
+ , max_connections => 1024000
+ , max_conn_rate => 1000
+ , access_rules => [<<"allow all">>]
+ , ssl =>
+ #{ versions => [<<"tlsv1.3">>, <<"tlsv1.2">>,
+ <<"tlsv1.1">>, <<"tlsv1">>]
+ , cacertfile => <<"etc/certs/cacert.pem">>
+ , certfile => <<"etc/certs/cert.pem">>
+ , keyfile => <<"etc/certs/key.pem">>
+ , verify => <<"verify_none">>
+ , fail_if_no_peer_cert => false
+ , server_name_indication => disable
+ }
+ , tcp =>
+ #{ active_n => 100
+ , backlog => 1024
+ }
+ }
+ }
+ , udp_listener =>
+ #{ summary => <<"A simple udp listener example">>
+ , value =>
+ #{ bind => <<"0.0.0.0:1884">>
+ , udp =>
+ #{ active_n => 100
+ , recbuf => <<"10KB">>
+ , sndbuf => <<"10KB">>
+ , buffer => <<"10KB">>
+ , reuseaddr => true
+ }
+ }
+ }
+ , dtls_listener =>
+ #{ summary => <<"A simple dtls listener example">>
+ , value =>
+ #{ bind => <<"5684">>
+ , acceptors => 16
+ , max_connections => 1024000
+ , max_conn_rate => 1000
+ , access_rules => [<<"allow all">>]
+ , ssl =>
+ #{ versions => [<<"dtlsv1.2">>, <<"dtlsv1">>]
+ , cacertfile => <<"etc/certs/cacert.pem">>
+ , certfile => <<"etc/certs/cert.pem">>
+ , keyfile => <<"etc/certs/key.pem">>
+ , verify => <<"verify_none">>
+ , fail_if_no_peer_cert => false
+ , server_name_indication => disable
+ }
+ , tcp =>
+ #{ active_n => 100
+ , backlog => 1024
+ }
+ }
+ }
+ , dtls_listener_with_psk_ciphers =>
+ #{ summary => <<"todo">>
+ , value =>
+ #{}
+ }
+ , lisetner_with_authn =>
+ #{ summary => <<"todo">>
+ , value => #{}}
+ }.
diff --git a/apps/emqx_gateway/src/emqx_gateway_schema.erl b/apps/emqx_gateway/src/emqx_gateway_schema.erl
index 5f87131d7..2215c3a96 100644
--- a/apps/emqx_gateway/src/emqx_gateway_schema.erl
+++ b/apps/emqx_gateway/src/emqx_gateway_schema.erl
@@ -143,9 +143,9 @@ The client just sends its PUBLISH messages to a GW"
sc(hoconsc:array(ref(mqttsn_predefined)),
#{ default => []
, desc =>
-"The Pre-defined topic ids and topic names.
+<<"The Pre-defined topic ids and topic names.
A 'pre-defined' topic id is a topic id whose mapping to a topic name
-is known in advance by both the client's application and the gateway"
+is known in advance by both the client’s application and the gateway">>
})}
, {listeners, sc(ref(udp_listeners))}
] ++ gateway_common_options();