From 0eff5358caaeb8967526b08374294386ead14117 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Wed, 8 Dec 2021 10:52:27 +0800 Subject: [PATCH 1/7] chore(gw): add examples for gw&client api --- apps/emqx_gateway/src/emqx_gateway_api.erl | 129 +++++++++- .../src/emqx_gateway_api_clients.erl | 223 ++++++++++++++---- apps/emqx_gateway/src/emqx_gateway_conf.erl | 26 +- apps/emqx_gateway/src/emqx_gateway_schema.erl | 2 +- .../test/emqx_gateway_api_SUITE.erl | 32 +-- .../test/emqx_gateway_conf_SUITE.erl | 16 +- 6 files changed, 343 insertions(+), 85 deletions(-) diff --git a/apps/emqx_gateway/src/emqx_gateway_api.erl b/apps/emqx_gateway/src/emqx_gateway_api.erl index 596d42c9a..7c961223d 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api.erl @@ -79,9 +79,9 @@ gateway(post, Request) -> undefined -> error(badarg); _ -> GwConf = maps:without([<<"name">>], Body), - case emqx_gateway_conf:load_gateway(GwName, GwConf) of - ok -> - {204}; + case emqx_gateway_conf:load_gateway(GwName, GwConf) of + {ok, NGwConf} -> + {201, NGwConf}; {error, Reason} -> return_http_error(500, Reason) end @@ -131,8 +131,8 @@ gateway_insta(put, #{body := GwConf, }) -> with_gateway(Name0, fun(GwName, _) -> case emqx_gateway_conf:update_gateway(GwName, GwConf) of - ok -> - {204}; + {ok, Gateway} -> + {200, Gateway}; {error, Reason} -> return_http_error(500, Reason) end @@ -201,7 +201,7 @@ schema("/gateway/:name/stats") -> params_gateway_name_in_path() -> [{name, - mk(binary(), + mk(string(), #{ in => path , desc => <<"Gateway Name">> })} @@ -209,7 +209,7 @@ params_gateway_name_in_path() -> params_gateway_status_in_qs() -> [{status, - mk(binary(), + mk(string(), #{ in => query , nullable => true , desc => <<"Gateway Status">> @@ -270,7 +270,7 @@ fields(Gw) when Gw == stomp; Gw == mqttsn; Gw == coap; Gw == lwm2m; Gw == exproto -> [{name, - mk(string(), #{ desc => <<"Gateway Name">>})} + mk(hoconsc:union([Gw]), #{ desc => <<"Gateway Name">>})} ] ++ convert_listener_struct(emqx_gateway_schema:fields(Gw)); fields(Listener) when Listener == tcp_listener; Listener == ssl_listener; @@ -330,21 +330,130 @@ examples_gateway_confs() -> #{ summary => <<"A simple STOMP gateway configs">> , value => #{ enable => true + , name => <<"stomp">> , enable_stats => true , idle_timeout => <<"30s">> , mountpoint => <<"stomp/">> , frame => - #{ max_header => 10 - , make_header_length => 1024 + #{ max_headers => 10 + , max_headers_length => 1024 , max_body_length => 65535 } + , listeners => + [ #{ type => <<"tcp">> + , name => <<"default">> + , bind => <<"61613">> + , max_connections => 1024000 + , max_conn_rate => 1000 + } + ] } } , mqttsn_gateway => #{ summary => <<"A simple MQTT-SN gateway configs">> , value => #{ enable => true + , name => <<"mqttsn">> , enable_stats => true + , idle_timeout => <<"30s">> + , mountpoint => <<"mqttsn/">> + , gateway_id => 1 + , broadcast => true + , enable_qos3 => true + , predefined => + [ #{ id => <<"1001">> + , topic => <<"pred/1001">> + } + , #{ id => <<"1002">> + , topic => <<"pred/1002">> + } + ] + , listeners => + [ #{ type => <<"udp">> + , name => <<"default">> + , bind => <<"1884">> + , max_connections => 1024000 + , max_conn_rate => 1000 + } + ] + } + } + , coap_gateway => + #{ summary => <<"A simple CoAP gateway configs">> + , value => + #{ enable => true + , name => <<"coap">> + , enable_stats => true + , idle_timeout => <<"30s">> + , mountpoint => <<"coap/">> + , heartbeat => <<"30s">> + , connection_required => false + , notify_type => <<"qos">> + , subscribe_qos => <<"coap">> + , publish_qos => <<"coap">> + , listeners => + [ #{ type => <<"udp">> + , name => <<"coap">> + , bind => <<"5683">> + , max_connections => 1024000 + , max_conn_rate => 1000 + } + ] + } + } + , lwm2m_gateway => + #{ summary => <<"A simple LwM2M gateway configs">> + , value => + #{ enable => true + , name => <<"lwm2m">> + , enable_stats => true + , idle_timeout => <<"30s">> + , mountpoint => <<"lwm2m/">> + , xml_dir => <<"etc/lwm2m_xml">> + , lifetime_min => <<"1s">> + , lifetime_max => <<"86400s">> + , qmode_time_window => <<"22s">> + , auto_observe => false + , update_msg_publish_condition => <<"always">> + , translators => + #{ command => #{topic => <<"/dn/#">>} + , response => #{topic => <<"/up/resp">>} + , notify => #{topic => <<"/up/notify">>} + , register => #{topic => <<"/up/resp">>} + , update => #{topic => <<"/up/resp">>} + } + , listeners => + [ #{ type => <<"udp">> + , name => <<"lwm2m">> + , bind => <<"5783">> + , max_connections => 1024000 + , max_conn_rate => 1000 + } + ] + } + } + , exproto_gateway => + #{ summary => <<"A simple ExProto gateway configs">> + , value => + #{ enable => true + , name => <<"exproto">> + , enable_stats => true + , idle_timeout => <<"30s">> + , mountpoint => <<"exproto/">> + , server => + #{ bind => <<"9100">> + } + , handler => + #{ address => <<"http://127.0.0.1:9001">> + } + , listeners => + [ #{ type => <<"tcp">> + , name => <<"default">> + , bind => <<"7993">> + , max_connections => 1024000 + , max_conn_rate => 1000 + } + ] } } }. diff --git a/apps/emqx_gateway/src/emqx_gateway_api_clients.erl b/apps/emqx_gateway/src/emqx_gateway_api_clients.erl index c7a77eb02..7d6feacf8 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_clients.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_clients.erl @@ -412,10 +412,7 @@ schema("/gateway/:name/clients") -> #{ 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())}) + ?STANDARD_RESP(#{200 => schema_client_list()}) } }; schema("/gateway/:name/clients/:clientid") -> @@ -424,10 +421,7 @@ schema("/gateway/:name/clients/:clientid") -> #{ description => <<"Get the gateway client infomation">> , parameters => params_client_insta() , responses => - ?STANDARD_RESP( - #{ 200 => emqx_dashboard_swagger:schema_with_examples( - ref(client), - examples_client())}) + ?STANDARD_RESP(#{200 => schema_client()}) } , delete => #{ description => <<"Kick out the gateway client">> @@ -443,9 +437,9 @@ schema("/gateway/:name/clients/:clientid/subscriptions") -> , parameters => params_client_insta() , responses => ?STANDARD_RESP( - #{ 200 => emqx_dashboard_swagger:schema_with_examples( - hoconsc:array(ref(subscription)), - examples_subsctiption_list())}) + #{200 => emqx_dashboard_swagger:schema_with_examples( + hoconsc:array(ref(subscription)), + examples_subsctiption_list())}) } , post => #{ description => <<"Create a subscription membership">> @@ -567,13 +561,85 @@ params_topic_name_in_path() -> %%-------------------------------------------------------------------- %% schemas +schema_client_list() -> + emqx_dashboard_swagger:schema_with_examples( + hoconsc:union([hoconsc:array(ref(?MODULE, stomp_client)), + hoconsc:array(ref(?MODULE, mqttsn_client)), + hoconsc:array(ref(?MODULE, coap_client)), + hoconsc:array(ref(?MODULE, lwm2m_client)), + hoconsc:array(ref(?MODULE, exproto_client)) + ]), + examples_client_list() + ). + +schema_client() -> + emqx_dashboard_swagger:schema_with_examples( + hoconsc:union([ref(?MODULE, stomp_client), + ref(?MODULE, mqttsn_client), + ref(?MODULE, coap_client), + ref(?MODULE, lwm2m_client), + ref(?MODULE, exproto_client) + ]), + examples_client() + ). + roots() -> - [ client + [ stomp_client + , mqttsn_client + , coap_client + , lwm2m_client + , exproto_client , subscription ]. -fields(client) -> - %% XXX: enum for every protocol's client +fields(test) -> + [{key, mk(string(), #{ desc => <<"Desc">>})}]; + +fields(stomp_client) -> + common_client_props(); +fields(mqttsn_client) -> + common_client_props(); +fields(coap_client) -> + common_client_props(); +fields(lwm2m_client) -> + [ {endpoint_name, + mk(string(), + #{ desc => <<"The LwM2M client endpoint name">>})} + , {lifetime, + mk(integer(), + #{ desc => <<"Life time">>})} + ] ++ common_client_props(); +fields(exproto_client) -> + common_client_props(); + +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.">>})} + ]. + +common_client_props() -> [ {node, mk(string(), #{ desc => <<"Name of the node to which the client is " @@ -699,45 +765,114 @@ fields(client) -> , {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.">>})} ]. %%-------------------------------------------------------------------- %% examples examples_client_list() -> - #{}. + #{ general_client_list => + #{ summary => <<"General Client List">> + , value => [example_general_client()] + } + , lwm2m_client_list => + #{ summary => <<"LwM2M Client List">> + , value => [example_lwm2m_client()] + } + }. examples_client() -> - #{}. + #{ general_client => + #{ summary => <<"General Client Info">> + , value => example_general_client() + } + , lwm2m_client => + #{ summary => <<"LwM2M Client Info">> + , value => example_lwm2m_client() + } + }. examples_subsctiption_list() -> - #{}. + #{ general_subscription_list => + #{ summary => <<"A General Subscription List">> + , value => [example_general_subscription()] + } + , stomp_subscription_list => + #{ summary => <<"The Stomp Subscription List">> + , value => [example_stomp_subscription] + } + }. examples_subsctiption() -> - #{}. + #{ general_subscription => + #{ summary => <<"A General Subscription">> + , value => example_general_subscription() + } + , stomp_subscription => + #{ summary => <<"A Stomp Subscription">> + , value => example_stomp_subscription() + } + }. + +example_lwm2m_client() -> + maps:merge( + example_general_client(), + #{ proto_name => <<"LwM2M">> + , proto_ver => <<"1.0">> + , endpoint_name => <<"urn:imei:154928475237123">> + , lifetime => 86400 + }). + +example_general_client() -> + #{ clientid => <<"MzAyMzEzNTUwNzk1NDA1MzYyMzIwNzUxNjQwMTY1NzQ0NjE">> + , username => <<"guest">> + , node => <<"emqx@127.0.0.1">> + , proto_name => "STOMP" + , proto_ver => <<"1.0">> + , ip_address => <<"127.0.0.1">> + , port => 50675 + , clean_start => true + , connected => true + , is_bridge => false + , keepalive => 0 + , expiry_interval => 0 + , subscriptions_cnt => 0 + , subscriptions_max => <<"infinity">> + , awaiting_rel_cnt => 0 + , awaiting_rel_max => <<"infinity">> + , mqueue_len => 0 + , mqueue_max => <<"infinity">> + , mqueue_dropped => 0 + , inflight_cnt => 0 + , inflight_max => <<"infinity">> + , heap_size => 4185 + , recv_oct => 56 + , recv_cnt => 1 + , recv_pkt => 1 + , recv_msg => 0 + , send_oct => 61 + , send_cnt => 1 + , send_pkt => 1 + , send_msg => 0 + , reductions => 72022 + , mailbox_len => 0 + , created_at => <<"2021-12-07T10:44:02.721+08:00">> + , connected_at => <<"2021-12-07T10:44:02.721+08:00">> + , disconnected_at => null + }. + +example_stomp_subscription() -> + maps:merge( + example_general_subscription(), + #{ topic => <<"stomp/topic">> + , sub_props => #{subid => <<"10">>} + }). + +example_general_subscription() -> + #{ topic => <<"test/topic">> + , qos => 1 + , nl => 0 + , rap => 0 + , rh => 0 + , sub_props => #{} + }. diff --git a/apps/emqx_gateway/src/emqx_gateway_conf.erl b/apps/emqx_gateway/src/emqx_gateway_conf.erl index a799b7fb7..da06e3a6d 100644 --- a/apps/emqx_gateway/src/emqx_gateway_conf.erl +++ b/apps/emqx_gateway/src/emqx_gateway_conf.erl @@ -79,15 +79,14 @@ unload() -> %%-------------------------------------------------------------------- %% APIs --spec load_gateway(atom_or_bin(), map()) -> ok_or_err(). +-spec load_gateway(atom_or_bin(), map()) -> map_or_err(). load_gateway(GwName, Conf) -> NConf = case maps:take(<<"listeners">>, Conf) of error -> Conf; {Ls, Conf1} -> Conf1#{<<"listeners">> => unconvert_listeners(Ls)} end, - %% TODO: - ret_ok_err(update({?FUNCTION_NAME, bin(GwName), NConf})). + ret_gw(GwName, update({?FUNCTION_NAME, bin(GwName), NConf})). %% @doc convert listener array to map unconvert_listeners(Ls) when is_list(Ls) -> @@ -108,13 +107,12 @@ maps_key_take([K | Ks], M, Acc) -> maps_key_take(Ks, M1, [V | Acc]) end. --spec update_gateway(atom_or_bin(), map()) -> ok_or_err(). +-spec update_gateway(atom_or_bin(), map()) -> map_or_err(). update_gateway(GwName, Conf0) -> Exclude0 = [listeners, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM], Exclude1 = [atom_to_binary(K, utf8) || K <- Exclude0], Conf = maps:without(Exclude0 ++ Exclude1, Conf0), - - ret_ok_err(update({?FUNCTION_NAME, bin(GwName), Conf})). + ret_gw(GwName, update({?FUNCTION_NAME, bin(GwName), Conf})). %% FIXME: delete cert files ?? @@ -261,6 +259,22 @@ bin(B) when is_binary(B) -> ret_ok_err({ok, _}) -> ok; ret_ok_err(Err) -> Err. +ret_gw(GwName, {ok, #{raw_config := GwConf}}) -> + GwConf1 = emqx_map_lib:deep_get([bin(GwName)], GwConf), + LsConf = emqx_map_lib:deep_get( + [bin(GwName), <<"listeners">>], + GwConf, #{}), + NLsConf = + lists:foldl(fun({LType, SubConf}, Acc) -> + NLConfs = + lists:map(fun({LName, LConf}) -> + do_convert_listener2(GwName, LType, LName, LConf) + end, proplists:from_map(SubConf)), + [NLConfs|Acc] + end, [], proplists:from_map(LsConf)), + {ok, maps:merge(GwConf1, #{<<"listeners">> => NLsConf})}; +ret_gw(_GwName, Err) -> Err. + ret_authn(GwName, {ok, #{raw_config := GwConf}}) -> Authn = emqx_map_lib:deep_get( [bin(GwName), <<"authentication">>], diff --git a/apps/emqx_gateway/src/emqx_gateway_schema.erl b/apps/emqx_gateway/src/emqx_gateway_schema.erl index 2215c3a96..18b195c5b 100644 --- a/apps/emqx_gateway/src/emqx_gateway_schema.erl +++ b/apps/emqx_gateway/src/emqx_gateway_schema.erl @@ -221,7 +221,7 @@ fields(lwm2m) -> })} , {lifetime_min, sc(duration(), - #{ default => "1s" + #{ default => "15s" , desc => "Minimum value of lifetime allowed to be set by the LwM2M client" })} , {lifetime_max, diff --git a/apps/emqx_gateway/test/emqx_gateway_api_SUITE.erl b/apps/emqx_gateway/test/emqx_gateway_api_SUITE.erl index 3ae9bcc12..18a380984 100644 --- a/apps/emqx_gateway/test/emqx_gateway_api_SUITE.erl +++ b/apps/emqx_gateway/test/emqx_gateway_api_SUITE.erl @@ -59,7 +59,7 @@ t_gateway(_) -> lists:foreach(fun assert_gw_unloaded/1, Gateways), {400, BadReq} = request(get, "/gateway/uname_gateway"), assert_bad_request(BadReq), - {204, _} = request(post, "/gateway", #{name => <<"stomp">>}), + {201, _} = request(post, "/gateway", #{name => <<"stomp">>}), {200, StompGw1} = request(get, "/gateway/stomp"), assert_feilds_apperence([name, status, enable, created_at, started_at], StompGw1), @@ -81,12 +81,12 @@ t_gateway_stomp(_) -> #{name => <<"def">>, type => <<"tcp">>, bind => <<"61613">>} ] }, - {204, _} = request(post, "/gateway", GwConf), + {201, _} = request(post, "/gateway", GwConf), {200, ConfResp} = request(get, "/gateway/stomp"), assert_confs(GwConf, ConfResp), %% put GwConf2 = emqx_map_lib:deep_merge(GwConf, #{frame => #{max_headers => 10}}), - {204, _} = request(put, "/gateway/stomp", maps:without([name], GwConf2)), + {200, _} = request(put, "/gateway/stomp", maps:without([name], GwConf2)), {200, ConfResp2} = request(get, "/gateway/stomp"), assert_confs(GwConf2, ConfResp2), {204, _} = request(delete, "/gateway/stomp"). @@ -104,12 +104,12 @@ t_gateway_mqttsn(_) -> #{name => <<"def">>, type => <<"udp">>, bind => <<"1884">>} ] }, - {204, _} = request(post, "/gateway", GwConf), + {201, _} = request(post, "/gateway", GwConf), {200, ConfResp} = request(get, "/gateway/mqttsn"), assert_confs(GwConf, ConfResp), %% put GwConf2 = emqx_map_lib:deep_merge(GwConf, #{predefined => []}), - {204, _} = request(put, "/gateway/mqttsn", maps:without([name], GwConf2)), + {200, _} = request(put, "/gateway/mqttsn", maps:without([name], GwConf2)), {200, ConfResp2} = request(get, "/gateway/mqttsn"), assert_confs(GwConf2, ConfResp2), {204, _} = request(delete, "/gateway/mqttsn"). @@ -125,12 +125,12 @@ t_gateway_coap(_) -> #{name => <<"def">>, type => <<"udp">>, bind => <<"5683">>} ] }, - {204, _} = request(post, "/gateway", GwConf), + {201, _} = request(post, "/gateway", GwConf), {200, ConfResp} = request(get, "/gateway/coap"), assert_confs(GwConf, ConfResp), %% put GwConf2 = emqx_map_lib:deep_merge(GwConf, #{heartbeat => <<"10s">>}), - {204, _} = request(put, "/gateway/coap", maps:without([name], GwConf2)), + {200, _} = request(put, "/gateway/coap", maps:without([name], GwConf2)), {200, ConfResp2} = request(get, "/gateway/coap"), assert_confs(GwConf2, ConfResp2), {204, _} = request(delete, "/gateway/coap"). @@ -156,12 +156,12 @@ t_gateway_lwm2m(_) -> #{name => <<"def">>, type => <<"udp">>, bind => <<"5783">>} ] }, - {204, _} = request(post, "/gateway", GwConf), + {201, _} = request(post, "/gateway", GwConf), {200, ConfResp} = request(get, "/gateway/lwm2m"), assert_confs(GwConf, ConfResp), %% put GwConf2 = emqx_map_lib:deep_merge(GwConf, #{qmode_time_window => <<"10s">>}), - {204, _} = request(put, "/gateway/lwm2m", maps:without([name], GwConf2)), + {200, _} = request(put, "/gateway/lwm2m", maps:without([name], GwConf2)), {200, ConfResp2} = request(get, "/gateway/lwm2m"), assert_confs(GwConf2, ConfResp2), {204, _} = request(delete, "/gateway/lwm2m"). @@ -177,19 +177,19 @@ t_gateway_exproto(_) -> #{name => <<"def">>, type => <<"tcp">>, bind => <<"7993">>} ] }, - {204, _} = request(post, "/gateway", GwConf), + {201, _} = request(post, "/gateway", GwConf), {200, ConfResp} = request(get, "/gateway/exproto"), assert_confs(GwConf, ConfResp), %% put GwConf2 = emqx_map_lib:deep_merge(GwConf, #{server => #{bind => <<"9200">>}}), - {204, _} = request(put, "/gateway/exproto", maps:without([name], GwConf2)), + {200, _} = request(put, "/gateway/exproto", maps:without([name], GwConf2)), {200, ConfResp2} = request(get, "/gateway/exproto"), assert_confs(GwConf2, ConfResp2), {204, _} = request(delete, "/gateway/exproto"). t_authn(_) -> GwConf = #{name => <<"stomp">>}, - {204, _} = request(post, "/gateway", GwConf), + {201, _} = request(post, "/gateway", GwConf), {204, _} = request(get, "/gateway/stomp/authentication"), AuthConf = #{mechanism => <<"password-based">>, @@ -212,7 +212,7 @@ t_authn(_) -> t_authn_data_mgmt(_) -> GwConf = #{name => <<"stomp">>}, - {204, _} = request(post, "/gateway", GwConf), + {201, _} = request(post, "/gateway", GwConf), {204, _} = request(get, "/gateway/stomp/authentication"), AuthConf = #{mechanism => <<"password-based">>, @@ -256,7 +256,7 @@ t_authn_data_mgmt(_) -> t_listeners(_) -> GwConf = #{name => <<"stomp">>}, - {204, _} = request(post, "/gateway", GwConf), + {201, _} = request(post, "/gateway", GwConf), {404, _} = request(get, "/gateway/stomp/listeners"), LisConf = #{name => <<"def">>, type => <<"tcp">>, @@ -289,7 +289,7 @@ t_listeners_authn(_) -> type => <<"tcp">>, bind => <<"61613">> }]}, - {204, _} = request(post, "/gateway", GwConf), + {201, _} = request(post, "/gateway", GwConf), {200, ConfResp} = request(get, "/gateway/stomp"), assert_confs(GwConf, ConfResp), @@ -316,7 +316,7 @@ t_listeners_authn_data_mgmt(_) -> type => <<"tcp">>, bind => <<"61613">> }]}, - {204, _} = request(post, "/gateway", GwConf), + {201, _} = request(post, "/gateway", GwConf), {200, ConfResp} = request(get, "/gateway/stomp"), assert_confs(GwConf, ConfResp), diff --git a/apps/emqx_gateway/test/emqx_gateway_conf_SUITE.erl b/apps/emqx_gateway/test/emqx_gateway_conf_SUITE.erl index 916913856..f3859532e 100644 --- a/apps/emqx_gateway/test/emqx_gateway_conf_SUITE.erl +++ b/apps/emqx_gateway/test/emqx_gateway_conf_SUITE.erl @@ -244,12 +244,12 @@ t_load_unload_gateway(_) -> StompConf2 = compose(?CONF_STOMP_BAISC_2, ?CONF_STOMP_AUTHN_1, ?CONF_STOMP_LISTENER_1), - ok = emqx_gateway_conf:load_gateway(stomp, StompConf1), + {ok, _} = emqx_gateway_conf:load_gateway(stomp, StompConf1), {error, already_exist} = emqx_gateway_conf:load_gateway(stomp, StompConf1), assert_confs(StompConf1, emqx:get_raw_config([gateway, stomp])), - ok = emqx_gateway_conf:update_gateway(stomp, StompConf2), + {ok, _} = emqx_gateway_conf:update_gateway(stomp, StompConf2), assert_confs(StompConf2, emqx:get_raw_config([gateway, stomp])), ok = emqx_gateway_conf:unload_gateway(stomp), @@ -265,7 +265,7 @@ t_load_unload_gateway(_) -> t_load_remove_authn(_) -> StompConf = compose_listener(?CONF_STOMP_BAISC_1, ?CONF_STOMP_LISTENER_1), - ok = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), + {ok, _} = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), assert_confs(StompConf, emqx:get_raw_config([gateway, stomp])), {ok, _} = emqx_gateway_conf:add_authn(<<"stomp">>, ?CONF_STOMP_AUTHN_1), @@ -292,7 +292,7 @@ t_load_remove_authn(_) -> t_load_remove_listeners(_) -> StompConf = compose_authn(?CONF_STOMP_BAISC_1, ?CONF_STOMP_AUTHN_1), - ok = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), + {ok, _} = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), assert_confs(StompConf, emqx:get_raw_config([gateway, stomp])), {ok, _} = emqx_gateway_conf:add_listener( @@ -338,7 +338,7 @@ t_load_remove_listener_authn(_) -> ?CONF_STOMP_AUTHN_2 ), - ok = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), + {ok, _} = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), assert_confs(StompConf, emqx:get_raw_config([gateway, stomp])), {ok, _} = emqx_gateway_conf:add_authn( @@ -368,7 +368,7 @@ t_load_gateway_with_certs_content(_) -> ?CONF_STOMP_BAISC_1, ?CONF_STOMP_LISTENER_SSL ), - ok = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), + {ok, _} = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), assert_confs(StompConf, emqx:get_raw_config([gateway, stomp])), SslConf = emqx_map_lib:deep_get( [<<"listeners">>, <<"ssl">>, <<"default">>, <<"ssl">>], @@ -388,7 +388,7 @@ t_load_gateway_with_certs_content(_) -> % ?CONF_STOMP_BAISC_1, % ?CONF_STOMP_LISTENER_SSL_PATH % ), -% ok = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), +% {ok, _} = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), % assert_confs(StompConf, emqx:get_raw_config([gateway, stomp])), % SslConf = emqx_map_lib:deep_get( % [<<"listeners">>, <<"ssl">>, <<"default">>, <<"ssl">>], @@ -402,7 +402,7 @@ t_load_gateway_with_certs_content(_) -> t_add_listener_with_certs_content(_) -> StompConf = ?CONF_STOMP_BAISC_1, - ok = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), + {ok, _} = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), assert_confs(StompConf, emqx:get_raw_config([gateway, stomp])), {ok, _} = emqx_gateway_conf:add_listener( From a937a3d4dcb3dd17cb83ae03a74282db7874aae3 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Wed, 8 Dec 2021 11:13:00 +0800 Subject: [PATCH 2/7] chore(gw): fix bad argument type --- apps/emqx_gateway/src/emqx_gateway_api.erl | 20 ++++++------- .../src/emqx_gateway_api_clients.erl | 30 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/apps/emqx_gateway/src/emqx_gateway_api.erl b/apps/emqx_gateway/src/emqx_gateway_api.erl index 7c961223d..34165d492 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api.erl @@ -201,7 +201,7 @@ schema("/gateway/:name/stats") -> params_gateway_name_in_path() -> [{name, - mk(string(), + mk(binary(), #{ in => path , desc => <<"Gateway Name">> })} @@ -209,7 +209,7 @@ params_gateway_name_in_path() -> params_gateway_status_in_qs() -> [{status, - mk(string(), + mk(binary(), #{ in => query , nullable => true , desc => <<"Gateway Status">> @@ -226,20 +226,20 @@ roots() -> fields(gateway_overview) -> [ {name, - mk(string(), + mk(binary(), #{ desc => <<"Gateway Name">>})} , {status, mk(hoconsc:enum([running, stopped, unloaded]), #{ desc => <<"The Gateway status">>})} , {created_at, - mk(string(), + mk(binary(), #{desc => <<"The Gateway created datetime">>})} , {started_at, - mk(string(), + mk(binary(), #{ nullable => true , desc => <<"The Gateway started datetime">>})} , {stopped_at, - mk(string(), + mk(binary(), #{ nullable => true , desc => <<"The Gateway stopped datetime">>})} , {max_connections, @@ -256,7 +256,7 @@ fields(gateway_overview) -> ]; fields(gateway_listener_overview) -> [ {id, - mk(string(), + mk(binary(), #{ desc => <<"Listener ID">>})} , {running, mk(boolean(), @@ -277,14 +277,14 @@ fields(Listener) when Listener == tcp_listener; Listener == udp_listener; Listener == dtls_listener -> [ {id, - mk(string(), + mk(binary(), #{ nullable => true , desc => <<"Listener ID">>})} , {type, mk(hoconsc:union([tcp, ssl, udp, dtls]), #{ desc => <<"Listener type">>})} , {name, - mk(string(), + mk(binary(), #{ desc => <<"Listener Name">>})} , {running, mk(boolean(), @@ -293,7 +293,7 @@ fields(Listener) when Listener == tcp_listener; ] ++ emqx_gateway_schema:fields(Listener); fields(gateway_stats) -> - [{key, mk(string(), #{})}]. + [{key, mk(binary(), #{})}]. schema_gateways_conf() -> %% XXX: We need convert the emqx_gateway_schema's listener map diff --git a/apps/emqx_gateway/src/emqx_gateway_api_clients.erl b/apps/emqx_gateway/src/emqx_gateway_api_clients.erl index 7d6feacf8..b67961e12 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_clients.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_clients.erl @@ -593,7 +593,7 @@ roots() -> ]. fields(test) -> - [{key, mk(string(), #{ desc => <<"Desc">>})}]; + [{key, mk(binary(), #{ desc => <<"Desc">>})}]; fields(stomp_client) -> common_client_props(); @@ -603,7 +603,7 @@ fields(coap_client) -> common_client_props(); fields(lwm2m_client) -> [ {endpoint_name, - mk(string(), + mk(binary(), #{ desc => <<"The LwM2M client endpoint name">>})} , {lifetime, mk(integer(), @@ -614,7 +614,7 @@ fields(exproto_client) -> fields(subscription) -> [ {topic, - mk(string(), + mk(binary(), #{ desc => <<"Topic Fillter">>})} , {qos, mk(integer(), @@ -634,30 +634,30 @@ fields(subscription) -> ]; fields(extra_sub_props) -> [ {subid, - mk(string(), + mk(binary(), #{ desc => <<"Only stomp protocol, an uniquely identity for " "the subscription. range: 1-65535.">>})} ]. common_client_props() -> [ {node, - mk(string(), + mk(binary(), #{ desc => <<"Name of the node to which the client is " "connected">>})} , {clientid, - mk(string(), + mk(binary(), #{ desc => <<"Client identifier">>})} , {username, - mk(string(), + mk(binary(), #{ desc => <<"Username of client when connecting">>})} , {proto_name, - mk(string(), + mk(binary(), #{ desc => <<"Client protocol name">>})} , {proto_ver, - mk(string(), + mk(binary(), #{ desc => <<"Protocol version used by the client">>})} , {ip_address, - mk(string(), + mk(binary(), #{ desc => <<"Client's IP address">>})} , {port, mk(integer(), @@ -667,10 +667,10 @@ common_client_props() -> #{ desc => <<"Indicates whether the client is connected via " "bridge">>})} , {connected_at, - mk(string(), + mk(binary(), #{ desc => <<"Client connection time">>})} , {disconnected_at, - mk(string(), + mk(binary(), #{ desc => <<"Client offline time, This field is only valid and " "returned when connected is false">>})} , {connected, @@ -681,10 +681,10 @@ common_client_props() -> %% want it %% %, {will_msg, - % mk(string(), + % mk(binary(), % #{ desc => <<"Client will message">>})} %, {zone, - % mk(string(), + % mk(binary(), % #{ desc => <<"Indicate the configuration group used by the " % "client">>})} , {keepalive, @@ -699,7 +699,7 @@ common_client_props() -> #{ desc => <<"Session expiration interval, with the unit of " "second">>})} , {created_at, - mk(string(), + mk(binary(), #{ desc => <<"Session creation time">>})} , {subscriptions_cnt, mk(integer(), From e48f10d2d65a531af8ff99cb0b7e47d7db1482e5 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Wed, 8 Dec 2021 11:33:11 +0800 Subject: [PATCH 3/7] chore(gw): ignore needless args --- apps/emqx_gateway/src/emqx_gateway_api.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/emqx_gateway/src/emqx_gateway_api.erl b/apps/emqx_gateway/src/emqx_gateway_api.erl index 34165d492..8f9cb99f7 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api.erl @@ -126,10 +126,12 @@ gateway_insta(get, #{bindings := #{name := Name0}}) -> error : badarg -> return_http_error(400, "Bad gateway name") end; -gateway_insta(put, #{body := GwConf, +gateway_insta(put, #{body := GwConf0, bindings := #{name := Name0} }) -> with_gateway(Name0, fun(GwName, _) -> + %% XXX: Clear the unused fields + GwConf = maps:without([<<"name">>], GwConf0), case emqx_gateway_conf:update_gateway(GwName, GwConf) of {ok, Gateway} -> {200, Gateway}; From 7c261b53562e1d2b1db73dad04597c17a30f407c Mon Sep 17 00:00:00 2001 From: JianBo He Date: Wed, 8 Dec 2021 17:18:39 +0800 Subject: [PATCH 4/7] chore(gw): improve examples for _gateway_api --- apps/emqx_gateway/src/emqx_gateway_api.erl | 174 +++++++++++++++++- .../src/exproto/emqx_exproto_impl.erl | 2 +- 2 files changed, 169 insertions(+), 7 deletions(-) diff --git a/apps/emqx_gateway/src/emqx_gateway_api.erl b/apps/emqx_gateway/src/emqx_gateway_api.erl index 8f9cb99f7..5036286b4 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api.erl @@ -153,10 +153,14 @@ schema("/gateway") -> #{ description => <<"Get gateway list">> , parameters => params_gateway_status_in_qs() , responses => - ?STANDARD_RESP(#{200 => ref(gateway_overview)}) + ?STANDARD_RESP( + #{200 => emqx_dashboard_swagger:schema_with_example( + hoconsc:array(ref(gateway_overview)), + examples_gateway_overview())}) }, post => #{ description => <<"Load a gateway">> + %% TODO: distinguish create & response swagger schema , 'requestBody' => schema_gateways_conf() , responses => ?STANDARD_RESP(#{201 => schema_gateways_conf()}) @@ -179,7 +183,7 @@ schema("/gateway/:name") -> put => #{ description => <<"Update the gateway configurations/status">> , parameters => params_gateway_name_in_path() - , 'requestBody' => schema_gateways_conf() + , 'requestBody' => schema_update_gateways_conf() , responses => ?STANDARD_RESP(#{200 => schema_gateways_conf()}) } @@ -210,6 +214,7 @@ params_gateway_name_in_path() -> ]. params_gateway_status_in_qs() -> + %% FIXME: enum in swagger ?? [{status, mk(binary(), #{ in => query @@ -274,6 +279,14 @@ fields(Gw) when Gw == stomp; Gw == mqttsn; [{name, mk(hoconsc:union([Gw]), #{ desc => <<"Gateway Name">>})} ] ++ convert_listener_struct(emqx_gateway_schema:fields(Gw)); + +fields(Gw) when Gw == update_stomp; Gw == update_mqttsn; + Gw == update_coap; Gw == update_lwm2m; + Gw == update_exproto -> + "update_" ++ GwStr = atom_to_list(Gw), + Gw1 = list_to_existing_atom(GwStr), + remove_listener_and_authn(emqx_gateway_schema:fields(Gw1)); + fields(Listener) when Listener == tcp_listener; Listener == ssl_listener; Listener == udp_listener; @@ -297,9 +310,17 @@ fields(Listener) when Listener == tcp_listener; fields(gateway_stats) -> [{key, mk(binary(), #{})}]. +schema_update_gateways_conf() -> + emqx_dashboard_swagger:schema_with_examples( + hoconsc:union([ref(?MODULE, update_stomp), + ref(?MODULE, update_mqttsn), + ref(?MODULE, update_coap), + ref(?MODULE, update_lwm2m), + ref(?MODULE, update_exproto)]), + examples_update_gateway_confs() + ). + schema_gateways_conf() -> - %% XXX: We need convert the emqx_gateway_schema's listener map - %% structure to array emqx_dashboard_swagger:schema_with_examples( hoconsc:union([ref(?MODULE, stomp), ref(?MODULE, mqttsn), ref(?MODULE, coap), ref(?MODULE, lwm2m), @@ -316,6 +337,11 @@ convert_listener_struct(Schema) -> }), lists:keystore(listeners, 1, Schema1, {listeners, ListenerSchema}). +remove_listener_and_authn(Schmea) -> + lists:keydelete( + authentication, 1, + lists:keydelete(listeners, 1, Schmea)). + listeners_schema(?R_REF(_Mod, tcp_listeners)) -> hoconsc:array(hoconsc:union([ref(tcp_listener), ref(ssl_listener)])); listeners_schema(?R_REF(_Mod, udp_listeners)) -> @@ -327,6 +353,57 @@ listeners_schema(?R_REF(_Mod, udp_tcp_listeners)) -> %%-------------------------------------------------------------------- %% examples +examples_gateway_overview() -> + [ #{ name => <<"coap">> + , status => <<"unloaded">> + } + , #{ name => <<"exproto">> + , status => <<"unloaded">> + } + , #{ name => <<"lwm2m">> + , status => <<"running">> + , current_connections => 0 + , max_connections => 1024000 + , listeners => + [ #{ id => <<"lwm2m:udp:default">> + , type => <<"udp">> + , name => <<"default">> + , running => true + } + ] + , created_at => <<"2021-12-08T14:41:26.171+08:00">> + , started_at => <<"2021-12-08T14:41:26.202+08:00">> + } + , #{ name => <<"mqttsn">> + , status => <<"stopped">> + , current_connections => 0 + , max_connections => 1024000 + , listeners => + [ #{ id => <<"mqttsn:udp:default">> + , name => <<"default">> + , running => false + , type => <<"udp">> + } + ] + , created_at => <<"2021-12-08T14:41:45.071+08:00">> + , stopped_at => <<"2021-12-08T14:56:35.576+08:00">> + } + , #{ name => <<"stomp">> + , status => <<"running">> + , current_connections => 0 + , max_connections => 1024000 + , listeners => + [ #{ id => <<"stomp:tcp:default">> + , name => <<"default">> + , running => true + , type => <<"tcp">> + } + ] + , created_at => <<"2021-12-08T14:42:15.272+08:00">> + , started_at => <<"2021-12-08T14:42:15.274+08:00">> + } + ]. + examples_gateway_confs() -> #{ stomp_gateway => #{ summary => <<"A simple STOMP gateway configs">> @@ -395,7 +472,7 @@ examples_gateway_confs() -> , publish_qos => <<"coap">> , listeners => [ #{ type => <<"udp">> - , name => <<"coap">> + , name => <<"default">> , bind => <<"5683">> , max_connections => 1024000 , max_conn_rate => 1000 @@ -426,7 +503,7 @@ examples_gateway_confs() -> } , listeners => [ #{ type => <<"udp">> - , name => <<"lwm2m">> + , name => <<"default">> , bind => <<"5783">> , max_connections => 1024000 , max_conn_rate => 1000 @@ -460,5 +537,90 @@ examples_gateway_confs() -> } }. +examples_update_gateway_confs() -> + #{ stomp_gateway => + #{ summary => <<"A simple STOMP gateway configs">> + , value => + #{ enable => true + , enable_stats => true + , idle_timeout => <<"30s">> + , mountpoint => <<"stomp2/">> + , frame => + #{ max_headers => 100 + , max_headers_length => 10240 + , max_body_length => 655350 + } + } + } + , mqttsn_gateway => + #{ summary => <<"A simple MQTT-SN gateway configs">> + , value => + #{ enable => true + , enable_stats => true + , idle_timeout => <<"30s">> + , mountpoint => <<"mqttsn2/">> + , gateway_id => 1 + , broadcast => true + , enable_qos3 => false + , predefined => + [ #{ id => <<"1003">> + , topic => <<"pred/1003">> + } + ] + } + } + , coap_gateway => + #{ summary => <<"A simple CoAP gateway configs">> + , value => + #{ enable => true + , enable_stats => true + , idle_timeout => <<"30s">> + , mountpoint => <<"coap2/">> + , heartbeat => <<"30s">> + , connection_required => false + , notify_type => <<"qos">> + , subscribe_qos => <<"coap">> + , publish_qos => <<"coap">> + } + } + , lwm2m_gateway => + #{ summary => <<"A simple LwM2M gateway configs">> + , value => + #{ enable => true + , enable_stats => true + , idle_timeout => <<"30s">> + , mountpoint => <<"lwm2m2/">> + , xml_dir => <<"etc/lwm2m_xml">> + , lifetime_min => <<"1s">> + , lifetime_max => <<"86400s">> + , qmode_time_window => <<"22s">> + , auto_observe => false + , update_msg_publish_condition => <<"always">> + , translators => + #{ command => #{topic => <<"/dn/#">>} + , response => #{topic => <<"/up/resp">>} + , notify => #{topic => <<"/up/notify">>} + , register => #{topic => <<"/up/resp">>} + , update => #{topic => <<"/up/resp">>} + } + } + } + , exproto_gateway => + #{ summary => <<"A simple ExProto gateway configs">> + , value => + #{ enable => true + , enable_stats => true + , idle_timeout => <<"30s">> + , mountpoint => <<"exproto2/">> + , server => + #{ bind => <<"9100">> + } + , handler => + #{ address => <<"http://127.0.0.1:9001">> + } + } + } + }. + examples_gateway_stats() -> #{}. diff --git a/apps/emqx_gateway/src/exproto/emqx_exproto_impl.erl b/apps/emqx_gateway/src/exproto/emqx_exproto_impl.erl index 82eb0b52c..d0ac84322 100644 --- a/apps/emqx_gateway/src/exproto/emqx_exproto_impl.erl +++ b/apps/emqx_gateway/src/exproto/emqx_exproto_impl.erl @@ -61,7 +61,7 @@ start_grpc_server(GwName, Options = #{bind := ListenOn}) -> end, case grpc:start_server(GwName, ListenOn, Services, SvrOptions) of {ok, _SvrPid} -> - console_print("Start ~ts gRPC server on ~p successfully.", + console_print("Start ~ts gRPC server on ~p successfully.~n", [GwName, ListenOn]); {error, Reason} -> ?ELOG("Falied to start ~ts gRPC server on ~p, reason: ~p", From 028f1fa71c63e661e70c1295d9d363fa94f3b230 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Thu, 9 Dec 2021 11:33:09 +0800 Subject: [PATCH 5/7] chore(gw): fix listener examples --- .../src/emqx_gateway_api_listeners.erl | 69 +++++++++++++++---- apps/emqx_gateway/src/emqx_gateway_http.erl | 4 ++ 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl index e73dd707e..142469313 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl @@ -233,6 +233,8 @@ schema("/gateway/:name/listeners") -> post => #{ description => <<"Create the gateway listener">> , parameters => params_gateway_name_in_path() + %% XXX: How to distinguish the different listener supported by + %% different types of gateways? , 'requestBody' => emqx_dashboard_swagger:schema_with_examples( ref(listener), examples_listener()) @@ -288,7 +290,7 @@ schema("/gateway/:name/listeners/:id/authentication") -> , responses => ?STANDARD_RESP( #{ 200 => schema_authn() - , 204 => <<"Authentication does not initiated">> + , 204 => <<"Authentication or listener does not existed">> }) }, post => @@ -487,7 +489,6 @@ fields(ssl_listener_opts) -> , {keyfile, binary()} , {verify, binary()} , {fail_if_no_peer_cert, boolean()} - , {server_name_indication, boolean()} , {depth, integer()} , {password, binary()} , {handshake_timeout, binary()} @@ -586,7 +587,9 @@ examples_listener() -> #{ tcp_listener=> #{ summary => <<"A simple tcp listener example">> , value => - #{ bind => <<"61613">> + #{ name => <<"tcp-def">> + , type => <<"tcp">> + , bind => <<"22210">> , acceptors => 16 , max_connections => 1024000 , max_conn_rate => 1000 @@ -607,7 +610,9 @@ examples_listener() -> , ssl_listener => #{ summary => <<"A simple ssl listener example">> , value => - #{ bind => <<"61614">> + #{ name => <<"ssl-def">> + , type => <<"ssl">> + , bind => <<"22211">> , acceptors => 16 , max_connections => 1024000 , max_conn_rate => 1000 @@ -620,7 +625,6 @@ examples_listener() -> , keyfile => <<"etc/certs/key.pem">> , verify => <<"verify_none">> , fail_if_no_peer_cert => false - , server_name_indication => disable } , tcp => #{ active_n => 100 @@ -631,7 +635,9 @@ examples_listener() -> , udp_listener => #{ summary => <<"A simple udp listener example">> , value => - #{ bind => <<"0.0.0.0:1884">> + #{ name => <<"udp-def">> + , type => udp + , bind => <<"22212">> , udp => #{ active_n => 100 , recbuf => <<"10KB">> @@ -644,32 +650,67 @@ examples_listener() -> , dtls_listener => #{ summary => <<"A simple dtls listener example">> , value => - #{ bind => <<"5684">> + #{ name => <<"dtls-def">> + , type => <<"dtls">> + , bind => <<"22213">> , acceptors => 16 , max_connections => 1024000 , max_conn_rate => 1000 , access_rules => [<<"allow all">>] - , ssl => + , dtls => #{ 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 => + , udp => #{ active_n => 100 , backlog => 1024 } } } , dtls_listener_with_psk_ciphers => - #{ summary => <<"todo">> + #{ summary => <<"A dtls listener with PSK example">> , value => - #{} + #{ name => <<"dtls-psk">> + , type => <<"dtls">> + , bind => <<"22214">> + , acceptors => 16 + , max_connections => 1024000 + , max_conn_rate => 1000 + , dtls => + #{ versions => [<<"dtlsv1.2">>, <<"dtlsv1">>] + , cacertfile => <<"etc/certs/cacert.pem">> + , certfile => <<"etc/certs/cert.pem">> + , keyfile => <<"etc/certs/key.pem">> + , verify => <<"verify_none">> + , user_lookup_fun => <<"emqx_tls_psk:lookup">> + , ciphers => +<<"RSA-PSK-AES256-GCM-SHA384,RSA-PSK-AES256-CBC-SHA384,RSA-PSK-AES128-GCM-SHA256," + "RSA-PSK-AES128-CBC-SHA256,RSA-PSK-AES256-CBC-SHA,RSA-PSK-AES128-CBC-SHA">> + , fail_if_no_peer_cert => false + } + } } , lisetner_with_authn => - #{ summary => <<"todo">> - , value => #{}} + #{ summary => <<"A tcp listener with authentication example">> + , value => + #{ name => <<"tcp-with-authn">> + , type => <<"tcp">> + , bind => <<"22215">> + , acceptors => 16 + , max_connections => 1024000 + , max_conn_rate => 1000 + , authentication => + #{ backend => <<"built-in-database">> + , mechanism => <<"password-based">> + , password_hash_algorithm => + #{ name => <<"sha256">> + } + , user_id_type => <<"username">> + } + } + } }. diff --git a/apps/emqx_gateway/src/emqx_gateway_http.erl b/apps/emqx_gateway/src/emqx_gateway_http.erl index 2a9840a9c..434a0bc49 100644 --- a/apps/emqx_gateway/src/emqx_gateway_http.erl +++ b/apps/emqx_gateway/src/emqx_gateway_http.erl @@ -223,6 +223,8 @@ remove_authn(GwName, ListenerId) -> confexp(ok) -> ok; confexp({ok, Res}) -> {ok, Res}; +confexp({error, badarg}) -> + error({update_conf_error, badarg}); confexp({error, not_found}) -> error({update_conf_error, not_found}); confexp({error, already_exist}) -> @@ -372,6 +374,8 @@ with_gateway(GwName0, Fun) -> lists:join(".", lists:map(fun to_list/1, Path0))), return_http_error(404, "Resource not found. path: " ++ Path); %% Exceptions from: confexp/1 + error : {update_conf_error, badarg} -> + return_http_error(400, "Bad arguments"); error : {update_conf_error, not_found} -> return_http_error(404, "Resource not found"); error : {update_conf_error, already_exist} -> From e66262858130c5a6d4007f9c789ed2efc0318fe7 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Thu, 9 Dec 2021 14:50:28 +0800 Subject: [PATCH 6/7] chore(gw): fix bad default value in query-string --- apps/emqx_gateway/src/emqx_gateway_api.erl | 2 ++ .../src/emqx_gateway_api_authn.erl | 4 ++++ .../src/emqx_gateway_api_clients.erl | 18 +++++++++++------- .../src/emqx_gateway_api_listeners.erl | 5 +++++ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/apps/emqx_gateway/src/emqx_gateway_api.erl b/apps/emqx_gateway/src/emqx_gateway_api.erl index 5036286b4..7bf52b4ec 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api.erl @@ -210,6 +210,7 @@ params_gateway_name_in_path() -> mk(binary(), #{ in => path , desc => <<"Gateway Name">> + , example => <<"">> })} ]. @@ -220,6 +221,7 @@ params_gateway_status_in_qs() -> #{ in => query , nullable => true , desc => <<"Gateway Status">> + , example => <<"">> })} ]. diff --git a/apps/emqx_gateway/src/emqx_gateway_api_authn.erl b/apps/emqx_gateway/src/emqx_gateway_api_authn.erl index d9bdf4d7c..701890633 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_authn.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_authn.erl @@ -272,6 +272,7 @@ params_gateway_name_in_path() -> mk(binary(), #{ in => path , desc => <<"Gateway Name">> + , example => <<"">> })} ]. @@ -279,6 +280,7 @@ params_userid_in_path() -> [{uid, mk(binary(), #{ in => path , desc => <<"User ID">> + , example => <<"">> })} ]. @@ -287,11 +289,13 @@ params_paging_in_qs() -> #{ in => query , nullable => true , desc => <<"Page Index">> + , example => 1 })}, {limit, mk(integer(), #{ in => query , nullable => true , desc => <<"Page Limit">> + , example => 100 })} ]. diff --git a/apps/emqx_gateway/src/emqx_gateway_api_clients.erl b/apps/emqx_gateway/src/emqx_gateway_api_clients.erl index b67961e12..9fe36d25e 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_clients.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_clients.erl @@ -475,7 +475,7 @@ params_client_insta() -> ++ params_gateway_name_in_path(). params_client_searching_in_qs() -> - M = #{in => query, nullable => true}, + M = #{in => query, nullable => true, example => <<"">>}, [ {node, mk(binary(), M#{desc => <<"Match the client's node name">>})} @@ -526,12 +526,16 @@ params_paging() -> mk(integer(), #{ in => query , nullable => true - , desc => <<"Page Index">>})} - , {limit, - mk(integer(), - #{ in => query - , desc => <<"Page Limit">> - , nullable => true})} + , desc => <<"Page Index">> + , example => 1 + })} + , {limit, + mk(integer(), + #{ in => query + , desc => <<"Page Limit">> + , nullable => true + , example => 100 + })} ]. params_gateway_name_in_path() -> diff --git a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl index 142469313..fbf923700 100644 --- a/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl +++ b/apps/emqx_gateway/src/emqx_gateway_api_listeners.erl @@ -410,6 +410,7 @@ params_gateway_name_in_path() -> mk(binary(), #{ in => path , desc => <<"Gateway Name">> + , example => <<"">> })} ]. @@ -418,6 +419,7 @@ params_listener_id_in_path() -> mk(binary(), #{ in => path , desc => <<"Listener ID">> + , example => <<"">> })} ]. @@ -425,6 +427,7 @@ params_userid_in_path() -> [{uid, mk(binary(), #{ in => path , desc => <<"User ID">> + , example => <<"">> })} ]. @@ -433,11 +436,13 @@ params_paging_in_qs() -> #{ in => query , nullable => true , desc => <<"Page Index">> + , example => 1 })}, {limit, mk(integer(), #{ in => query , nullable => true , desc => <<"Page Limit">> + , example => 100 })} ]. From fa11c6df9cc7c236fba99c88fa54eb3736ff2c9f Mon Sep 17 00:00:00 2001 From: JianBo He Date: Thu, 9 Dec 2021 16:37:19 +0800 Subject: [PATCH 7/7] test(gw): fix bad test cases --- apps/emqx_gateway/src/emqx_gateway_cli.erl | 6 +++--- apps/emqx_gateway/src/emqx_gateway_conf.erl | 4 ++-- apps/emqx_gateway/test/emqx_gateway_api_SUITE.erl | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/emqx_gateway/src/emqx_gateway_cli.erl b/apps/emqx_gateway/src/emqx_gateway_cli.erl index a441b384e..03d55e27e 100644 --- a/apps/emqx_gateway/src/emqx_gateway_cli.erl +++ b/apps/emqx_gateway/src/emqx_gateway_cli.erl @@ -69,7 +69,7 @@ gateway(["load", Name, Conf]) -> bin(Name), emqx_json:decode(Conf, [return_maps]) ) of - ok -> + {ok, _} -> print("ok~n"); {error, Reason} -> print("Error: ~p~n", [Reason]) @@ -88,7 +88,7 @@ gateway(["stop", Name]) -> bin(Name), #{<<"enable">> => <<"false">>} ) of - ok -> + {ok, _} -> print("ok~n"); {error, Reason} -> print("Error: ~p~n", [Reason]) @@ -99,7 +99,7 @@ gateway(["start", Name]) -> bin(Name), #{<<"enable">> => <<"true">>} ) of - ok -> + {ok, _} -> print("ok~n"); {error, Reason} -> print("Error: ~p~n", [Reason]) diff --git a/apps/emqx_gateway/src/emqx_gateway_conf.erl b/apps/emqx_gateway/src/emqx_gateway_conf.erl index da06e3a6d..351093e0f 100644 --- a/apps/emqx_gateway/src/emqx_gateway_conf.erl +++ b/apps/emqx_gateway/src/emqx_gateway_conf.erl @@ -269,9 +269,9 @@ ret_gw(GwName, {ok, #{raw_config := GwConf}}) -> NLConfs = lists:map(fun({LName, LConf}) -> do_convert_listener2(GwName, LType, LName, LConf) - end, proplists:from_map(SubConf)), + end, maps:to_list(SubConf)), [NLConfs|Acc] - end, [], proplists:from_map(LsConf)), + end, [], maps:to_list(LsConf)), {ok, maps:merge(GwConf1, #{<<"listeners">> => NLsConf})}; ret_gw(_GwName, Err) -> Err. diff --git a/apps/emqx_gateway/test/emqx_gateway_api_SUITE.erl b/apps/emqx_gateway/test/emqx_gateway_api_SUITE.erl index 18a380984..f91347a6e 100644 --- a/apps/emqx_gateway/test/emqx_gateway_api_SUITE.erl +++ b/apps/emqx_gateway/test/emqx_gateway_api_SUITE.erl @@ -86,7 +86,7 @@ t_gateway_stomp(_) -> assert_confs(GwConf, ConfResp), %% put GwConf2 = emqx_map_lib:deep_merge(GwConf, #{frame => #{max_headers => 10}}), - {200, _} = request(put, "/gateway/stomp", maps:without([name], GwConf2)), + {200, _} = request(put, "/gateway/stomp", maps:without([name, listeners], GwConf2)), {200, ConfResp2} = request(get, "/gateway/stomp"), assert_confs(GwConf2, ConfResp2), {204, _} = request(delete, "/gateway/stomp"). @@ -109,7 +109,7 @@ t_gateway_mqttsn(_) -> assert_confs(GwConf, ConfResp), %% put GwConf2 = emqx_map_lib:deep_merge(GwConf, #{predefined => []}), - {200, _} = request(put, "/gateway/mqttsn", maps:without([name], GwConf2)), + {200, _} = request(put, "/gateway/mqttsn", maps:without([name, listeners], GwConf2)), {200, ConfResp2} = request(get, "/gateway/mqttsn"), assert_confs(GwConf2, ConfResp2), {204, _} = request(delete, "/gateway/mqttsn"). @@ -130,7 +130,7 @@ t_gateway_coap(_) -> assert_confs(GwConf, ConfResp), %% put GwConf2 = emqx_map_lib:deep_merge(GwConf, #{heartbeat => <<"10s">>}), - {200, _} = request(put, "/gateway/coap", maps:without([name], GwConf2)), + {200, _} = request(put, "/gateway/coap", maps:without([name, listeners], GwConf2)), {200, ConfResp2} = request(get, "/gateway/coap"), assert_confs(GwConf2, ConfResp2), {204, _} = request(delete, "/gateway/coap"). @@ -161,7 +161,7 @@ t_gateway_lwm2m(_) -> assert_confs(GwConf, ConfResp), %% put GwConf2 = emqx_map_lib:deep_merge(GwConf, #{qmode_time_window => <<"10s">>}), - {200, _} = request(put, "/gateway/lwm2m", maps:without([name], GwConf2)), + {200, _} = request(put, "/gateway/lwm2m", maps:without([name, listeners], GwConf2)), {200, ConfResp2} = request(get, "/gateway/lwm2m"), assert_confs(GwConf2, ConfResp2), {204, _} = request(delete, "/gateway/lwm2m"). @@ -182,7 +182,7 @@ t_gateway_exproto(_) -> assert_confs(GwConf, ConfResp), %% put GwConf2 = emqx_map_lib:deep_merge(GwConf, #{server => #{bind => <<"9200">>}}), - {200, _} = request(put, "/gateway/exproto", maps:without([name], GwConf2)), + {200, _} = request(put, "/gateway/exproto", maps:without([name, listeners], GwConf2)), {200, ConfResp2} = request(get, "/gateway/exproto"), assert_confs(GwConf2, ConfResp2), {204, _} = request(delete, "/gateway/exproto").