Merge pull request #6411 from HJianBo/gw-improve-apis-3

More precise gateway http-api examples
This commit is contained in:
JianBo He 2021-12-10 16:52:49 +08:00 committed by GitHub
commit 5224da57dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 616 additions and 134 deletions

View File

@ -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
@ -126,13 +126,15 @@ 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 ->
{204};
{ok, Gateway} ->
{200, Gateway};
{error, Reason} ->
return_http_error(500, Reason)
end
@ -151,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()})
@ -177,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()})
}
@ -204,15 +210,18 @@ params_gateway_name_in_path() ->
mk(binary(),
#{ in => path
, desc => <<"Gateway Name">>
, example => <<"">>
})}
].
params_gateway_status_in_qs() ->
%% FIXME: enum in swagger ??
[{status,
mk(binary(),
#{ in => query
, nullable => true
, desc => <<"Gateway Status">>
, example => <<"">>
})}
].
@ -226,20 +235,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 +265,7 @@ fields(gateway_overview) ->
];
fields(gateway_listener_overview) ->
[ {id,
mk(string(),
mk(binary(),
#{ desc => <<"Listener ID">>})}
, {running,
mk(boolean(),
@ -270,21 +279,29 @@ 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(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;
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,11 +310,19 @@ fields(Listener) when Listener == tcp_listener;
] ++ emqx_gateway_schema:fields(Listener);
fields(gateway_stats) ->
[{key, mk(string(), #{})}].
[{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),
@ -314,6 +339,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)) ->
@ -325,18 +355,202 @@ 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">>
, value =>
#{ enable => true
, name => <<"stomp">>
, enable_stats => true
, idle_timeout => <<"30s">>
, mountpoint => <<"stomp/">>
, frame =>
#{ 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 => <<"default">>
, 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 => <<"default">>
, 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
}
]
}
}
}.
examples_update_gateway_confs() ->
#{ stomp_gateway =>
#{ summary => <<"A simple STOMP gateway configs">>
, value =>
#{ enable => true
, enable_stats => true
, idle_timeout => <<"30s">>
, mountpoint => <<"stomp/">>
, mountpoint => <<"stomp2/">>
, frame =>
#{ max_header => 10
, make_header_length => 1024
, max_body_length => 65535
#{ max_headers => 100
, max_headers_length => 10240
, max_body_length => 655350
}
}
}
@ -345,6 +559,67 @@ examples_gateway_confs() ->
, 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">>
}
}
}
}.

View File

@ -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
})}
].

View File

@ -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">>
@ -481,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">>})}
@ -532,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() ->
@ -567,31 +565,103 @@ 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(binary(), #{ 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(binary(),
#{ 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(binary(),
#{ 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(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(),
@ -601,10 +671,10 @@ fields(client) ->
#{ 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,
@ -615,10 +685,10 @@ fields(client) ->
%% 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,
@ -633,7 +703,7 @@ fields(client) ->
#{ desc => <<"Session expiration interval, with the unit of "
"second">>})}
, {created_at,
mk(string(),
mk(binary(),
#{ desc => <<"Session creation time">>})}
, {subscriptions_cnt,
mk(integer(),
@ -699,45 +769,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 => #{}
}.

View File

@ -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 =>
@ -408,6 +410,7 @@ params_gateway_name_in_path() ->
mk(binary(),
#{ in => path
, desc => <<"Gateway Name">>
, example => <<"">>
})}
].
@ -416,6 +419,7 @@ params_listener_id_in_path() ->
mk(binary(),
#{ in => path
, desc => <<"Listener ID">>
, example => <<"">>
})}
].
@ -423,6 +427,7 @@ params_userid_in_path() ->
[{uid, mk(binary(),
#{ in => path
, desc => <<"User ID">>
, example => <<"">>
})}
].
@ -431,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
})}
].
@ -487,7 +494,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 +592,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 +615,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 +630,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 +640,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 +655,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">>
}
}
}
}.

View File

@ -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])

View File

@ -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, maps:to_list(SubConf)),
[NLConfs|Acc]
end, [], maps:to_list(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">>],

View File

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

View File

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

View File

@ -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",

View File

@ -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, listeners], 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, listeners], 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, listeners], 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, listeners], 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, listeners], 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),

View File

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