feat: update to openapi 3.0.0
This commit is contained in:
parent
ed1cf33b9d
commit
92ae5663a6
|
@ -71,144 +71,152 @@ apis() ->
|
||||||
, subscribe_api()].
|
, subscribe_api()].
|
||||||
|
|
||||||
schemas() ->
|
schemas() ->
|
||||||
ClientDef = #{
|
Client = #{
|
||||||
<<"node">> => #{
|
client => #{
|
||||||
type => <<"string">>,
|
type => object,
|
||||||
description => <<"Name of the node to which the client is connected">>},
|
properties => #{
|
||||||
<<"clientid">> => #{
|
node => #{
|
||||||
type => <<"string">>,
|
type => string,
|
||||||
description => <<"Client identifier">>},
|
description => <<"Name of the node to which the client is connected">>},
|
||||||
<<"username">> => #{
|
clientid => #{
|
||||||
type => <<"string">>,
|
type => string,
|
||||||
description => <<"User name of client when connecting">>},
|
description => <<"Client identifier">>},
|
||||||
<<"proto_name">> => #{
|
username => #{
|
||||||
type => <<"string">>,
|
type => string,
|
||||||
description => <<"Client protocol name">>},
|
description => <<"User name of client when connecting">>},
|
||||||
<<"proto_ver">> => #{
|
proto_name => #{
|
||||||
type => <<"integer">>,
|
type => string,
|
||||||
description => <<"Protocol version used by the client">>},
|
description => <<"Client protocol name">>},
|
||||||
<<"ip_address">> => #{
|
proto_ver => #{
|
||||||
type => <<"string">>,
|
type => integer,
|
||||||
description => <<"Client's IP address">>},
|
description => <<"Protocol version used by the client">>},
|
||||||
<<"is_bridge">> => #{
|
ip_address => #{
|
||||||
type => <<"boolean">>,
|
type => string,
|
||||||
description => <<"Indicates whether the client is connectedvia bridge">>},
|
description => <<"Client's IP address">>},
|
||||||
<<"connected_at">> => #{
|
is_bridge => #{
|
||||||
type => <<"string">>,
|
type => boolean,
|
||||||
description => <<"Client connection time">>},
|
description => <<"Indicates whether the client is connectedvia bridge">>},
|
||||||
<<"disconnected_at">> => #{
|
connected_at => #{
|
||||||
type => <<"string">>,
|
type => string,
|
||||||
description => <<"Client offline time, This field is only valid and returned when connected is false">>},
|
description => <<"Client connection time">>},
|
||||||
<<"connected">> => #{
|
disconnected_at => #{
|
||||||
type => <<"boolean">>,
|
type => string,
|
||||||
description => <<"Whether the client is connected">>},
|
description => <<"Client offline time, This field is only valid and returned when connected is false">>},
|
||||||
<<"will_msg">> => #{
|
connected => #{
|
||||||
type => <<"string">>,
|
type => boolean,
|
||||||
description => <<"Client will message">>},
|
description => <<"Whether the client is connected">>},
|
||||||
<<"zone">> => #{
|
will_msg => #{
|
||||||
type => <<"string">>,
|
type => string,
|
||||||
description => <<"Indicate the configuration group used by the client">>},
|
description => <<"Client will message">>},
|
||||||
<<"keepalive">> => #{
|
zone => #{
|
||||||
type => <<"integer">>,
|
type => string,
|
||||||
description => <<"keepalive time, with the unit of second">>},
|
description => <<"Indicate the configuration group used by the client">>},
|
||||||
<<"clean_start">> => #{
|
keepalive => #{
|
||||||
type => <<"boolean">>,
|
type => integer,
|
||||||
description => <<"Indicate whether the client is using a brand new session">>},
|
description => <<"keepalive time, with the unit of second">>},
|
||||||
<<"expiry_interval">> => #{
|
clean_start => #{
|
||||||
type => <<"integer">>,
|
type => boolean,
|
||||||
description => <<"Session expiration interval, with the unit of second">>},
|
description => <<"Indicate whether the client is using a brand new session">>},
|
||||||
<<"created_at">> => #{
|
expiry_interval => #{
|
||||||
type => <<"string">>,
|
type => integer,
|
||||||
description => <<"Session creation time">>},
|
description => <<"Session expiration interval, with the unit of second">>},
|
||||||
<<"subscriptions_cnt">> => #{
|
created_at => #{
|
||||||
type => <<"integer">>,
|
type => string,
|
||||||
description => <<"Number of subscriptions established by this client.">>},
|
description => <<"Session creation time">>},
|
||||||
<<"subscriptions_max">> => #{
|
subscriptions_cnt => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"v4 api name [max_subscriptions] Maximum number of subscriptions allowed by this client">>},
|
description => <<"Number of subscriptions established by this client.">>},
|
||||||
<<"inflight_cnt">> => #{
|
subscriptions_max => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"Current length of inflight">>},
|
description => <<"v4 api name [max_subscriptions] Maximum number of subscriptions allowed by this client">>},
|
||||||
<<"inflight_max">> => #{
|
inflight_cnt => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"v4 api name [max_inflight]. Maximum length of inflight">>},
|
description => <<"Current length of inflight">>},
|
||||||
<<"mqueue_len">> => #{
|
inflight_max => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"Current length of message queue">>},
|
description => <<"v4 api name [max_inflight]. Maximum length of inflight">>},
|
||||||
<<"mqueue_max">> => #{
|
mqueue_len => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"v4 api name [max_mqueue]. Maximum length of message queue">>},
|
description => <<"Current length of message queue">>},
|
||||||
<<"mqueue_dropped">> => #{
|
mqueue_max => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"Number of messages dropped by the message queue due to exceeding the length">>},
|
description => <<"v4 api name [max_mqueue]. Maximum length of message queue">>},
|
||||||
<<"awaiting_rel_cnt">> => #{
|
mqueue_dropped => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"v4 api name [awaiting_rel] Number of awaiting PUBREC packet">>},
|
description => <<"Number of messages dropped by the message queue due to exceeding the length">>},
|
||||||
<<"awaiting_rel_max">> => #{
|
awaiting_rel_cnt => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"v4 api name [max_awaiting_rel]. Maximum allowed number of awaiting PUBREC packet">>},
|
description => <<"v4 api name [awaiting_rel] Number of awaiting PUBREC packet">>},
|
||||||
<<"recv_oct">> => #{
|
awaiting_rel_max => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"Number of bytes received by EMQ X Broker (the same below)">>},
|
description => <<"v4 api name [max_awaiting_rel]. Maximum allowed number of awaiting PUBREC packet">>},
|
||||||
<<"recv_cnt">> => #{
|
recv_oct => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"Number of TCP packets received">>},
|
description => <<"Number of bytes received by EMQ X Broker (the same below)">>},
|
||||||
<<"recv_pkt">> => #{
|
recv_cnt => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"Number of MQTT packets received">>},
|
description => <<"Number of TCP packets received">>},
|
||||||
<<"recv_msg">> => #{
|
recv_pkt => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"Number of PUBLISH packets received">>},
|
description => <<"Number of MQTT packets received">>},
|
||||||
<<"send_oct">> => #{
|
recv_msg => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"Number of bytes sent">>},
|
description => <<"Number of PUBLISH packets received">>},
|
||||||
<<"send_cnt">> => #{
|
send_oct => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"Number of TCP packets sent">>},
|
description => <<"Number of bytes sent">>},
|
||||||
<<"send_pkt">> => #{
|
send_cnt => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"Number of MQTT packets sent">>},
|
description => <<"Number of TCP packets sent">>},
|
||||||
<<"send_msg">> => #{
|
send_pkt => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"Number of PUBLISH packets sent">>},
|
description => <<"Number of MQTT packets sent">>},
|
||||||
<<"mailbox_len">> => #{
|
send_msg => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"Process mailbox size">>},
|
description => <<"Number of PUBLISH packets sent">>},
|
||||||
<<"heap_size">> => #{
|
mailbox_len => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
description => <<"Process heap size with the unit of byte">>
|
description => <<"Process mailbox size">>},
|
||||||
},
|
heap_size => #{
|
||||||
<<"reductions">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Process heap size with the unit of byte">>
|
||||||
description => <<"Erlang reduction">>}},
|
},
|
||||||
ACLCacheDefinitionProperties = #{
|
reductions => #{
|
||||||
<<"topic">> => #{
|
type => integer,
|
||||||
type => <<"string">>,
|
description => <<"Erlang reduction">>}
|
||||||
description => <<"Topic name">>},
|
}
|
||||||
<<"access">> => #{
|
}
|
||||||
type => <<"string">>,
|
},
|
||||||
enum => [<<"subscribe">>, <<"publish">>],
|
AclCache = #{
|
||||||
description => <<"Access type">>},
|
acl_cache => #{
|
||||||
<<"result">> => #{
|
type => object,
|
||||||
type => <<"string">>,
|
properties => #{
|
||||||
enum => [<<"allow">>, <<"deny">>],
|
topic => #{
|
||||||
default => <<"allow">>,
|
type => string,
|
||||||
description => <<"Allow or deny">>},
|
description => <<"Topic name">>},
|
||||||
<<"updated_time">> => #{
|
access => #{
|
||||||
type => <<"integer">>,
|
type => string,
|
||||||
description => <<"Update time">>}},
|
enum => [<<"subscribe">>, <<"publish">>],
|
||||||
[{<<"client">>, ClientDef}, {<<"acl_cache">>, ACLCacheDefinitionProperties}].
|
description => <<"Access type">>},
|
||||||
|
result => #{
|
||||||
|
type => string,
|
||||||
|
enum => [<<"allow">>, <<"deny">>],
|
||||||
|
default => <<"allow">>,
|
||||||
|
description => <<"Allow or deny">>},
|
||||||
|
updated_time => #{
|
||||||
|
type => integer,
|
||||||
|
description => <<"Update time">>}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[Client, AclCache].
|
||||||
|
|
||||||
clients_api() ->
|
clients_api() ->
|
||||||
Metadata = #{
|
Metadata = #{
|
||||||
get => #{
|
get => #{
|
||||||
description => "List clients",
|
description => "List clients",
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"200">> => #{
|
<<"200">> => emqx_mgmt_util:response_array_schema(<<"List clients 200 OK">>, <<"client">>)}}},
|
||||||
description => <<"List clients 200 OK">>,
|
|
||||||
schema => #{
|
|
||||||
type => array,
|
|
||||||
items => minirest:ref(<<"client">>)}}}}},
|
|
||||||
{"/clients", Metadata, clients}.
|
{"/clients", Metadata, clients}.
|
||||||
|
|
||||||
client_api() ->
|
client_api() ->
|
||||||
|
@ -218,25 +226,23 @@ client_api() ->
|
||||||
parameters => [#{
|
parameters => [#{
|
||||||
name => clientid,
|
name => clientid,
|
||||||
in => path,
|
in => path,
|
||||||
type => string,
|
schema => #{type => string},
|
||||||
required => true,
|
required => true,
|
||||||
default => 123456}],
|
example => 123456}],
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
|
<<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
|
||||||
<<"200">> => #{
|
<<"200">> => emqx_mgmt_util:response_schema(<<"List clients 200 OK">>, <<"client">>)}},
|
||||||
description => <<"Get clients 200 OK">>,
|
|
||||||
schema => minirest:ref(<<"client">>)}}},
|
|
||||||
delete => #{
|
delete => #{
|
||||||
description => "Kick out client by client ID",
|
description => "Kick out client by client ID",
|
||||||
parameters => [#{
|
parameters => [#{
|
||||||
name => clientid,
|
name => clientid,
|
||||||
in => path,
|
in => path,
|
||||||
type => string,
|
schema => #{type => string},
|
||||||
required => true,
|
required => true,
|
||||||
default => 123456}],
|
example => 123456}],
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
|
<<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
|
||||||
<<"200">> => #{description => <<"Kick out clients OK">>}}}},
|
<<"200">> => emqx_mgmt_util:response_schema(<<"List clients 200 OK">>, <<"client">>)}}},
|
||||||
{"/clients/:clientid", Metadata, client}.
|
{"/clients/:clientid", Metadata, client}.
|
||||||
|
|
||||||
clients_acl_cache_api() ->
|
clients_acl_cache_api() ->
|
||||||
|
@ -246,26 +252,23 @@ clients_acl_cache_api() ->
|
||||||
parameters => [#{
|
parameters => [#{
|
||||||
name => clientid,
|
name => clientid,
|
||||||
in => path,
|
in => path,
|
||||||
type => string,
|
schema => #{type => string},
|
||||||
required => true,
|
required => true,
|
||||||
default => 123456}],
|
example => 123456}],
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
|
<<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
|
||||||
<<"200">> => #{
|
<<"200">> => emqx_mgmt_util:response_schema(<<"List clients 200 OK">>, <<"acl_cache">>)}},
|
||||||
description => <<"List 200 OK">>,
|
|
||||||
schema => minirest:ref(<<"acl_cache">>)}}},
|
|
||||||
delete => #{
|
delete => #{
|
||||||
description => "Clean client acl cache",
|
description => "Clean client acl cache",
|
||||||
parameters => [#{
|
parameters => [#{
|
||||||
name => clientid,
|
name => clientid,
|
||||||
in => path,
|
in => path,
|
||||||
type => string,
|
schema => #{type => string},
|
||||||
required => true,
|
required => true,
|
||||||
default => 123456}],
|
example => 123456}],
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"404">> => emqx_mgmt_util:not_found_schema(<<"client id not found">>),
|
<<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
|
||||||
<<"200">> => #{
|
<<"200">> => emqx_mgmt_util:response_schema(<<"Delete clients 200 OK">>)}}},
|
||||||
description => <<"Clean acl cache 200 OK">>}}}},
|
|
||||||
{"/clients/:clientid/acl_cache", Metadata, acl_cache}.
|
{"/clients/:clientid/acl_cache", Metadata, acl_cache}.
|
||||||
|
|
||||||
subscribe_api() ->
|
subscribe_api() ->
|
||||||
|
@ -276,51 +279,47 @@ subscribe_api() ->
|
||||||
#{
|
#{
|
||||||
name => clientid,
|
name => clientid,
|
||||||
in => path,
|
in => path,
|
||||||
type => string,
|
schema => #{type => string},
|
||||||
required => true,
|
required => true,
|
||||||
default => 123456
|
example => 123456
|
||||||
},
|
|
||||||
#{
|
|
||||||
name => topic_data,
|
|
||||||
in => body,
|
|
||||||
schema => #{
|
|
||||||
type => object,
|
|
||||||
properties => #{
|
|
||||||
<<"topic">> => #{
|
|
||||||
type => <<"string">>,
|
|
||||||
example => <<"topic_1">>,
|
|
||||||
description => <<"Topic">>},
|
|
||||||
<<"qos">> => #{
|
|
||||||
type => <<"integer">>,
|
|
||||||
enum => [0, 1, 2],
|
|
||||||
example => 0,
|
|
||||||
description => <<"QOS">>}}}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
'requestBody' => emqx_mgmt_util:request_body_schema(#{
|
||||||
|
type => object,
|
||||||
|
properties => #{
|
||||||
|
<<"topic">> => #{
|
||||||
|
type => string,
|
||||||
|
example => <<"topic_1">>,
|
||||||
|
description => <<"Topic">>},
|
||||||
|
<<"qos">> => #{
|
||||||
|
type => integer,
|
||||||
|
enum => [0, 1, 2],
|
||||||
|
example => 0,
|
||||||
|
description => <<"QoS">>}}}),
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
|
<<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
|
||||||
<<"200">> => #{description => <<"subscribe ok">>}}},
|
<<"200">> => emqx_mgmt_util:response_schema(<<"subscribe ok">>)}},
|
||||||
delete => #{
|
delete => #{
|
||||||
description => "unsubscribe",
|
description => "unsubscribe",
|
||||||
parameters => [
|
parameters => [
|
||||||
#{
|
#{
|
||||||
name => clientid,
|
name => clientid,
|
||||||
in => path,
|
in => path,
|
||||||
type => string,
|
schema => #{type => string},
|
||||||
required => true,
|
required => true,
|
||||||
default => 123456
|
example => 123456
|
||||||
},
|
},
|
||||||
#{
|
#{
|
||||||
name => topic,
|
name => topic,
|
||||||
in => query,
|
in => query,
|
||||||
type => string,
|
schema => #{type => string},
|
||||||
required => true,
|
required => true,
|
||||||
default => <<"topic_1">>
|
example => <<"topic_1">>
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
|
<<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
|
||||||
<<"200">> => #{description => <<"unsubscribe ok">>}}}},
|
<<"200">> => emqx_mgmt_util:response_schema(<<"unsubscribe ok">>)}}},
|
||||||
{"/clients/:clientid/subscribe", Metadata, subscribe}.
|
{"/clients/:clientid/subscribe", Metadata, subscribe}.
|
||||||
|
|
||||||
%%%==============================================================================================
|
%%%==============================================================================================
|
||||||
|
|
|
@ -26,263 +26,266 @@ api_spec() ->
|
||||||
{[metrics_api()], [metrics_schema()]}.
|
{[metrics_api()], [metrics_schema()]}.
|
||||||
|
|
||||||
metrics_schema() ->
|
metrics_schema() ->
|
||||||
DefinitionName = <<"metrics">>,
|
#{
|
||||||
DefinitionProperties = #{
|
metrics => #{
|
||||||
<<"actions.failure">> => #{
|
type => object,
|
||||||
type => <<"integer">>,
|
properties => #{
|
||||||
description => <<"Number of failure executions of the rule engine action">>},
|
'actions.failure' => #{
|
||||||
<<"actions.success">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of failure executions of the rule engine action">>},
|
||||||
description => <<"Number of successful executions of the rule engine action">>},
|
'actions.success' => #{
|
||||||
<<"bytes.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of successful executions of the rule engine action">>},
|
||||||
description => <<"Number of bytes received by EMQ X Broker">>},
|
'bytes.received' => #{
|
||||||
<<"bytes.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of bytes received by EMQ X Broker">>},
|
||||||
description => <<"Number of bytes sent by EMQ X Broker on this connection">>},
|
'bytes.sent' => #{
|
||||||
<<"client.authenticate">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of bytes sent by EMQ X Broker on this connection">>},
|
||||||
description => <<"Number of client authentications">>},
|
'client.authenticate' => #{
|
||||||
<<"client.auth.anonymous">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of client authentications">>},
|
||||||
description => <<"Number of clients who log in anonymously">>},
|
'client.auth.anonymous' => #{
|
||||||
<<"client.connect">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of clients who log in anonymously">>},
|
||||||
description => <<"Number of client connections">>},
|
'client.connect' => #{
|
||||||
<<"client.connack">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of client connections">>},
|
||||||
description => <<"Number of CONNACK packet sent">>},
|
'client.connack' => #{
|
||||||
<<"client.connected">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of CONNACK packet sent">>},
|
||||||
description => <<"Number of successful client connections">>},
|
'client.connected' => #{
|
||||||
<<"client.disconnected">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of successful client connections">>},
|
||||||
description => <<"Number of client disconnects">>},
|
'client.disconnected' => #{
|
||||||
<<"client.check_acl">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of client disconnects">>},
|
||||||
description => <<"Number of ACL rule checks">>},
|
'client.check_acl' => #{
|
||||||
<<"client.subscribe">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of ACL rule checks">>},
|
||||||
description => <<"Number of client subscriptions">>},
|
'client.subscribe' => #{
|
||||||
<<"client.unsubscribe">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of client subscriptions">>},
|
||||||
description => <<"Number of client unsubscriptions">>},
|
'client.unsubscribe' => #{
|
||||||
<<"delivery.dropped.too_large">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of client unsubscriptions">>},
|
||||||
description => <<"The number of messages that were dropped because the length exceeded the limit when sending">>},
|
'delivery.dropped.too_large' => #{
|
||||||
<<"delivery.dropped.queue_full">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"The number of messages that were dropped because the length exceeded the limit when sending">>},
|
||||||
description => <<"Number of messages with a non-zero QoS that were dropped because the message queue was full when sending">>},
|
'delivery.dropped.queue_full' => #{
|
||||||
<<"delivery.dropped.qos0_msg">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of messages with a non-zero QoS that were dropped because the message queue was full when sending">>},
|
||||||
description => <<"Number of messages with QoS 0 that were dropped because the message queue was full when sending">>},
|
'delivery.dropped.qos0_msg' => #{
|
||||||
<<"delivery.dropped.expired">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of messages with QoS 0 that were dropped because the message queue was full when sending">>},
|
||||||
description => <<"Number of messages dropped due to message expiration on sending">>},
|
'delivery.dropped.expired' => #{
|
||||||
<<"delivery.dropped.no_local">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of messages dropped due to message expiration on sending">>},
|
||||||
description => <<"Number of messages that were dropped due to the No Local subscription option when sending">>},
|
'delivery.dropped.no_local' => #{
|
||||||
<<"delivery.dropped">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of messages that were dropped due to the No Local subscription option when sending">>},
|
||||||
description => <<"Total number of discarded messages when sending">>},
|
'delivery.dropped' => #{
|
||||||
<<"messages.delayed">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Total number of discarded messages when sending">>},
|
||||||
description => <<"Number of delay- published messages stored by EMQ X Broker">>},
|
'messages.delayed' => #{
|
||||||
<<"messages.delivered">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of delay- published messages stored by EMQ X Broker">>},
|
||||||
description => <<"Number of messages forwarded to the subscription process internally by EMQ X Broker">>},
|
'messages.delivered' => #{
|
||||||
<<"messages.dropped">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of messages forwarded to the subscription process internally by EMQ X Broker">>},
|
||||||
description => <<"Total number of messages dropped by EMQ X Broker before forwarding to the subscription process">>},
|
'messages.dropped' => #{
|
||||||
<<"messages.dropped.expired">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Total number of messages dropped by EMQ X Broker before forwarding to the subscription process">>},
|
||||||
description => <<"Number of messages dropped due to message expiration when receiving">>},
|
'messages.dropped.expired' => #{
|
||||||
<<"messages.dropped.no_subscribers">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of messages dropped due to message expiration when receiving">>},
|
||||||
description => <<"Number of messages dropped due to no subscribers">>},
|
'messages.dropped.no_subscribers' => #{
|
||||||
<<"messages.forward">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of messages dropped due to no subscribers">>},
|
||||||
description => <<"Number of messages forwarded to other nodes">>},
|
'messages.forward' => #{
|
||||||
<<"messages.publish">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of messages forwarded to other nodes">>},
|
||||||
description => <<"Number of messages published in addition to system messages">>},
|
'messages.publish' => #{
|
||||||
<<"messages.qos0.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of messages published in addition to system messages">>},
|
||||||
description => <<"Number of QoS 0 messages received from clients">>},
|
'messages.qos0.received' => #{
|
||||||
<<"messages.qos1.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of QoS 0 messages received from clients">>},
|
||||||
description => <<"Number of QoS 1 messages received from clients">>},
|
'messages.qos1.received' => #{
|
||||||
<<"messages.qos2.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of QoS 1 messages received from clients">>},
|
||||||
description => <<"Number of QoS 2 messages received from clients">>},
|
'messages.qos2.received' => #{
|
||||||
<<"messages.qos0.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of QoS 2 messages received from clients">>},
|
||||||
description => <<"Number of QoS 0 messages sent to clients">>},
|
'messages.qos0.sent' => #{
|
||||||
<<"messages.qos1.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of QoS 0 messages sent to clients">>},
|
||||||
description => <<"Number of QoS 1 messages sent to clients">>},
|
'messages.qos1.sent' => #{
|
||||||
<<"messages.qos2.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of QoS 1 messages sent to clients">>},
|
||||||
description => <<"Number of QoS 2 messages sent to clients">>},
|
'messages.qos2.sent' => #{
|
||||||
<<"messages.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of QoS 2 messages sent to clients">>},
|
||||||
description => <<"Number of messages received from the client, equal to the sum of messages.qos0.received,messages.qos1.received and messages.qos2.received">>},
|
'messages.received' => #{
|
||||||
<<"messages.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of messages received from the client, equal to the sum of messages.qos0.received,messages.qos1.received and messages.qos2.received">>},
|
||||||
description => <<"Number of messages sent to the client, equal to the sum of messages.qos0.sent,messages.qos1.sent and messages.qos2.sent">>},
|
'messages.sent' => #{
|
||||||
<<"messages.retained">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of messages sent to the client, equal to the sum of messages.qos0.sent,messages.qos1.sent and messages.qos2.sent">>},
|
||||||
description => <<"Number of retained messages stored by EMQ X Broker">>},
|
'messages.retained' => #{
|
||||||
<<"messages.acked">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of retained messages stored by EMQ X Broker">>},
|
||||||
description => <<"Number of received PUBACK and PUBREC packet">>},
|
'messages.acked' => #{
|
||||||
<<"packets.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBACK and PUBREC packet">>},
|
||||||
description => <<"Number of received packet">>},
|
'packets.received' => #{
|
||||||
<<"packets.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received packet">>},
|
||||||
description => <<"Number of sent packet">>},
|
'packets.sent' => #{
|
||||||
<<"packets.connect.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sent packet">>},
|
||||||
description => <<"Number of received CONNECT packet">>},
|
'packets.connect.received' => #{
|
||||||
<<"packets.connack.auth_error">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received CONNECT packet">>},
|
||||||
description => <<"Number of received CONNECT packet with failed authentication">>},
|
'packets.connack.auth_error' => #{
|
||||||
<<"packets.connack.error">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received CONNECT packet with failed authentication">>},
|
||||||
description => <<"Number of received CONNECT packet with unsuccessful connections">>},
|
'packets.connack.error' => #{
|
||||||
<<"packets.connack.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received CONNECT packet with unsuccessful connections">>},
|
||||||
description => <<"Number of sent CONNACK packet">>},
|
'packets.connack.sent' => #{
|
||||||
<<"packets.publish.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sent CONNACK packet">>},
|
||||||
description => <<"Number of received PUBLISH packet">>},
|
'packets.publish.received' => #{
|
||||||
<<"packets.publish.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBLISH packet">>},
|
||||||
description => <<"Number of sent PUBLISH packet">>},
|
'packets.publish.sent' => #{
|
||||||
<<"packets.publish.inuse">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sent PUBLISH packet">>},
|
||||||
description => <<"Number of received PUBLISH packet with occupied identifiers">>},
|
'packets.publish.inuse' => #{
|
||||||
<<"packets.publish.auth_error">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBLISH packet with occupied identifiers">>},
|
||||||
description => <<"Number of received PUBLISH packets with failed the ACL check">>},
|
'packets.publish.auth_error' => #{
|
||||||
<<"packets.publish.error">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBLISH packets with failed the ACL check">>},
|
||||||
description => <<"Number of received PUBLISH packet that cannot be published">>},
|
'packets.publish.error' => #{
|
||||||
<<"packets.publish.dropped">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBLISH packet that cannot be published">>},
|
||||||
description => <<"Number of messages discarded due to the receiving limit">>},
|
'packets.publish.dropped' => #{
|
||||||
<<"packets.puback.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of messages discarded due to the receiving limit">>},
|
||||||
description => <<"Number of received PUBACK packet">>},
|
'packets.puback.received' => #{
|
||||||
<<"packets.puback.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBACK packet">>},
|
||||||
description => <<"Number of sent PUBACK packet">>},
|
'packets.puback.sent' => #{
|
||||||
<<"packets.puback.inuse">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sent PUBACK packet">>},
|
||||||
description => <<"Number of received PUBACK packet with occupied identifiers">>},
|
'packets.puback.inuse' => #{
|
||||||
<<"packets.puback.missed">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBACK packet with occupied identifiers">>},
|
||||||
description => <<"Number of received packet with identifiers.">>},
|
'packets.puback.missed' => #{
|
||||||
<<"packets.pubrec.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received packet with identifiers.">>},
|
||||||
description => <<"Number of received PUBREC packet">>},
|
'packets.pubrec.received' => #{
|
||||||
<<"packets.pubrec.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBREC packet">>},
|
||||||
description => <<"Number of sent PUBREC packet">>},
|
'packets.pubrec.sent' => #{
|
||||||
<<"packets.pubrec.inuse">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sent PUBREC packet">>},
|
||||||
description => <<"Number of received PUBREC packet with occupied identifiers">>},
|
'packets.pubrec.inuse' => #{
|
||||||
<<"packets.pubrec.missed">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBREC packet with occupied identifiers">>},
|
||||||
description => <<"Number of received PUBREC packet with unknown identifiers">>},
|
'packets.pubrec.missed' => #{
|
||||||
<<"packets.pubrel.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBREC packet with unknown identifiers">>},
|
||||||
description => <<"Number of received PUBREL packet">>},
|
'packets.pubrel.received' => #{
|
||||||
<<"packets.pubrel.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBREL packet">>},
|
||||||
description => <<"Number of sent PUBREL packet">>},
|
'packets.pubrel.sent' => #{
|
||||||
<<"packets.pubrel.missed">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sent PUBREL packet">>},
|
||||||
description => <<"Number of received PUBREC packet with unknown identifiers">>},
|
'packets.pubrel.missed' => #{
|
||||||
<<"packets.pubcomp.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBREC packet with unknown identifiers">>},
|
||||||
description => <<"Number of received PUBCOMP packet">>},
|
'packets.pubcomp.received' => #{
|
||||||
<<"packets.pubcomp.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBCOMP packet">>},
|
||||||
description => <<"Number of sent PUBCOMP packet">>},
|
'packets.pubcomp.sent' => #{
|
||||||
<<"packets.pubcomp.inuse">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sent PUBCOMP packet">>},
|
||||||
description => <<"Number of received PUBCOMP packet with occupied identifiers">>},
|
'packets.pubcomp.inuse' => #{
|
||||||
<<"packets.pubcomp.missed">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PUBCOMP packet with occupied identifiers">>},
|
||||||
description => <<"Number of missed PUBCOMP packet">>},
|
'packets.pubcomp.missed' => #{
|
||||||
<<"packets.subscribe.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of missed PUBCOMP packet">>},
|
||||||
description => <<"Number of received SUBSCRIBE packet">>},
|
'packets.subscribe.received' => #{
|
||||||
<<"packets.subscribe.error">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received SUBSCRIBE packet">>},
|
||||||
description => <<"Number of received SUBSCRIBE packet with failed subscriptions">>},
|
'packets.subscribe.error' => #{
|
||||||
<<"packets.subscribe.auth_error">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received SUBSCRIBE packet with failed subscriptions">>},
|
||||||
description => <<"Number of received SUBACK packet with failed ACL check">>},
|
'packets.subscribe.auth_error' => #{
|
||||||
<<"packets.suback.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received SUBACK packet with failed ACL check">>},
|
||||||
description => <<"Number of sent SUBACK packet">>},
|
'packets.suback.sent' => #{
|
||||||
<<"packets.unsubscribe.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sent SUBACK packet">>},
|
||||||
description => <<"Number of received UNSUBSCRIBE packet">>},
|
'packets.unsubscribe.received' => #{
|
||||||
<<"packets.unsubscribe.error">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received UNSUBSCRIBE packet">>},
|
||||||
description => <<"Number of received UNSUBSCRIBE packet with failed unsubscriptions">>},
|
'packets.unsubscribe.error' => #{
|
||||||
<<"packets.unsuback.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received UNSUBSCRIBE packet with failed unsubscriptions">>},
|
||||||
description => <<"Number of sent UNSUBACK packet">>},
|
'packets.unsuback.sent' => #{
|
||||||
<<"packets.pingreq.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sent UNSUBACK packet">>},
|
||||||
description => <<"Number of received PINGREQ packet">>},
|
'packets.pingreq.received' => #{
|
||||||
<<"packets.pingresp.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received PINGREQ packet">>},
|
||||||
description => <<"Number of sent PUBRESP packet">>},
|
'packets.pingresp.sent' => #{
|
||||||
<<"packets.disconnect.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sent PUBRESP packet">>},
|
||||||
description => <<"Number of received DISCONNECT packet">>},
|
'packets.disconnect.received' => #{
|
||||||
<<"packets.disconnect.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received DISCONNECT packet">>},
|
||||||
description => <<"Number of sent DISCONNECT packet">>},
|
'packets.disconnect.sent' => #{
|
||||||
<<"packets.auth.received">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sent DISCONNECT packet">>},
|
||||||
description => <<"Number of received AUTH packet">>},
|
'packets.auth.received' => #{
|
||||||
<<"packets.auth.sent">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of received AUTH packet">>},
|
||||||
description => <<"Number of sent AUTH packet">>},
|
'packets.auth.sent' => #{
|
||||||
<<"rules.matched">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sent AUTH packet">>},
|
||||||
description => <<"Number of rule matched">>},
|
'rules.matched' => #{
|
||||||
<<"session.created">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of rule matched">>},
|
||||||
description => <<"Number of sessions created">>},
|
'session.created' => #{
|
||||||
<<"session.discarded">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sessions created">>},
|
||||||
description => <<"Number of sessions dropped because Clean Session or Clean Start is true">>},
|
'session.discarded' => #{
|
||||||
<<"session.resumed">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sessions dropped because Clean Session or Clean Start is true">>},
|
||||||
description => <<"Number of sessions resumed because Clean Session or Clean Start is false">>},
|
'session.resumed' => #{
|
||||||
<<"session.takeovered">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sessions resumed because Clean Session or Clean Start is false">>},
|
||||||
description => <<"Number of sessions takeovered because Clean Session or Clean Start is false">>},
|
'session.takeovered' => #{
|
||||||
<<"session.terminated">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of sessions takeovered because Clean Session or Clean Start is false">>},
|
||||||
description => <<"Number of terminated sessions">>}},
|
'session.terminated' => #{
|
||||||
{DefinitionName, DefinitionProperties}.
|
type => integer,
|
||||||
|
description => <<"Number of terminated sessions">>}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.
|
||||||
|
|
||||||
metrics_api() ->
|
metrics_api() ->
|
||||||
Metadata = #{
|
Metadata = #{
|
||||||
get => #{
|
get => #{
|
||||||
description => "EMQ X metrics",
|
description => "EMQ X metrics",
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"200">> => #{
|
<<"200">> => emqx_mgmt_util:response_schema(<<"List all metrics">>, <<"metrics">>)}}},
|
||||||
schema => cowboy_swagger:schema(<<"metrics">>)}}}},
|
|
||||||
{"/metrics", Metadata, list}.
|
{"/metrics", Metadata, list}.
|
||||||
|
|
||||||
%%%==============================================================================================
|
%%%==============================================================================================
|
||||||
|
|
|
@ -41,71 +41,71 @@ schemas() ->
|
||||||
[node_schema()].
|
[node_schema()].
|
||||||
|
|
||||||
node_schema() ->
|
node_schema() ->
|
||||||
DefinitionName = <<"node">>,
|
#{
|
||||||
DefinitionProperties = #{
|
node => #{
|
||||||
<<"node">> => #{
|
type => object,
|
||||||
type => <<"string">>,
|
properties => #{
|
||||||
description => <<"Node name">>},
|
node => #{
|
||||||
<<"connections">> => #{
|
type => string,
|
||||||
type => <<"integer">>,
|
description => <<"Node name">>},
|
||||||
description => <<"Number of clients currently connected to this node">>},
|
connections => #{
|
||||||
<<"load1">> => #{
|
type => integer,
|
||||||
type => <<"string">>,
|
description => <<"Number of clients currently connected to this node">>},
|
||||||
description => <<"CPU average load in 1 minute">>},
|
load1 => #{
|
||||||
<<"load5">> => #{
|
type => string,
|
||||||
type => <<"string">>,
|
description => <<"CPU average load in 1 minute">>},
|
||||||
description => <<"CPU average load in 5 minute">>},
|
load5 => #{
|
||||||
<<"load15">> => #{
|
type => string,
|
||||||
type => <<"string">>,
|
description => <<"CPU average load in 5 minute">>},
|
||||||
description => <<"CPU average load in 15 minute">>},
|
load15 => #{
|
||||||
<<"max_fds">> => #{
|
type => string,
|
||||||
type => <<"integer">>,
|
description => <<"CPU average load in 15 minute">>},
|
||||||
description => <<"Maximum file descriptor limit for the operating system">>},
|
max_fds => #{
|
||||||
<<"memory_total">> => #{
|
type => integer,
|
||||||
type => <<"string">>,
|
description => <<"Maximum file descriptor limit for the operating system">>},
|
||||||
description => <<"VM allocated system memory">>},
|
memory_total => #{
|
||||||
<<"memory_used">> => #{
|
type => string,
|
||||||
type => <<"string">>,
|
description => <<"VM allocated system memory">>},
|
||||||
description => <<"VM occupied system memory">>},
|
memory_used => #{
|
||||||
<<"node_status">> => #{
|
type => string,
|
||||||
type => <<"string">>,
|
description => <<"VM occupied system memory">>},
|
||||||
description => <<"Node status">>},
|
node_status => #{
|
||||||
<<"otp_release">> => #{
|
type => string,
|
||||||
type => <<"string">>,
|
description => <<"Node status">>},
|
||||||
description => <<"Erlang/OTP version used by EMQ X Broker">>},
|
otp_release => #{
|
||||||
<<"process_available">> => #{
|
type => string,
|
||||||
type => <<"integer">>,
|
description => <<"Erlang/OTP version used by EMQ X Broker">>},
|
||||||
description => <<"Number of available processes">>},
|
process_available => #{
|
||||||
<<"process_used">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of available processes">>},
|
||||||
description => <<"Number of used processes">>},
|
process_used => #{
|
||||||
<<"uptime">> => #{
|
type => integer,
|
||||||
type => <<"string">>,
|
description => <<"Number of used processes">>},
|
||||||
description => <<"EMQ X Broker runtime">>},
|
uptime => #{
|
||||||
<<"version">> => #{
|
type => string,
|
||||||
type => <<"string">>,
|
description => <<"EMQ X Broker runtime">>},
|
||||||
description => <<"EMQ X Broker version">>},
|
version => #{
|
||||||
<<"sys_path">> => #{
|
type => string,
|
||||||
type => <<"string">>,
|
description => <<"EMQ X Broker version">>},
|
||||||
description => <<"EMQ X system file location">>},
|
sys_path => #{
|
||||||
<<"log_path">> => #{
|
type => string,
|
||||||
type => <<"string">>,
|
description => <<"EMQ X system file location">>},
|
||||||
description => <<"EMQ X log file location">>},
|
log_path => #{
|
||||||
<<"config_path">> => #{
|
type => string,
|
||||||
type => <<"string">>,
|
description => <<"EMQ X log file location">>},
|
||||||
description => <<"EMQ X config file location">>}
|
config_path => #{
|
||||||
},
|
type => string,
|
||||||
{DefinitionName, DefinitionProperties}.
|
description => <<"EMQ X config file location">>}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.
|
||||||
|
|
||||||
nodes_api() ->
|
nodes_api() ->
|
||||||
Metadata = #{
|
Metadata = #{
|
||||||
get => #{
|
get => #{
|
||||||
description => "List EMQ X nodes",
|
description => "List EMQ X nodes",
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"200">> => #{description => <<"List EMQ X Nodes">>,
|
<<"200">> => emqx_mgmt_util:response_array_schema(<<"List EMQ X Nodes">>, <<"node">>)}}},
|
||||||
schema => #{
|
|
||||||
type => array,
|
|
||||||
items => cowboy_swagger:schema(<<"node">>)}}}}},
|
|
||||||
{"/nodes", Metadata, nodes}.
|
{"/nodes", Metadata, nodes}.
|
||||||
|
|
||||||
node_api() ->
|
node_api() ->
|
||||||
|
@ -116,15 +116,12 @@ node_api() ->
|
||||||
name => node_name,
|
name => node_name,
|
||||||
in => path,
|
in => path,
|
||||||
description => "node name",
|
description => "node name",
|
||||||
type => string,
|
schema => #{type => string},
|
||||||
required => true,
|
required => true,
|
||||||
default => node()}],
|
example => node()}],
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"400">> =>
|
<<"400">> => emqx_mgmt_util:not_found_schema(<<"Node error">>, [<<"SOURCE_ERROR">>]),
|
||||||
emqx_mgmt_util:not_found_schema(<<"Node error">>, [<<"SOURCE_ERROR">>]),
|
<<"200">> => emqx_mgmt_util:response_schema(<<"Get EMQ X Nodes info by name">>, <<"node">>)}}},
|
||||||
<<"200">> => #{
|
|
||||||
description => <<"Get EMQ X Nodes info by name">>,
|
|
||||||
schema => cowboy_swagger:schema(<<"node">>)}}}},
|
|
||||||
{"/nodes/:node_name", Metadata, node}.
|
{"/nodes/:node_name", Metadata, node}.
|
||||||
|
|
||||||
node_metrics_api() ->
|
node_metrics_api() ->
|
||||||
|
@ -135,15 +132,12 @@ node_metrics_api() ->
|
||||||
name => node_name,
|
name => node_name,
|
||||||
in => path,
|
in => path,
|
||||||
description => "node name",
|
description => "node name",
|
||||||
type => string,
|
schema => #{type => string},
|
||||||
required => true,
|
required => true,
|
||||||
default => node()}],
|
example => node()}],
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"400">> =>
|
<<"400">> => emqx_mgmt_util:not_found_schema(<<"Node error">>, [<<"SOURCE_ERROR">>]),
|
||||||
emqx_mgmt_util:not_found_schema(<<"Node error">>, [<<"SOURCE_ERROR">>]),
|
<<"200">> => emqx_mgmt_util:response_schema(<<"Get EMQ X Node Metrics">>, <<"metrics">>)}}},
|
||||||
<<"200">> => #{
|
|
||||||
description => <<"Get EMQ X Node Metrics">>,
|
|
||||||
schema => cowboy_swagger:schema(<<"metrics">>)}}}},
|
|
||||||
{"/nodes/:node_name/metrics", Metadata, node_metrics}.
|
{"/nodes/:node_name/metrics", Metadata, node_metrics}.
|
||||||
|
|
||||||
node_stats_api() ->
|
node_stats_api() ->
|
||||||
|
@ -154,15 +148,12 @@ node_stats_api() ->
|
||||||
name => node_name,
|
name => node_name,
|
||||||
in => path,
|
in => path,
|
||||||
description => "node name",
|
description => "node name",
|
||||||
type => string,
|
schema => #{type => string},
|
||||||
required => true,
|
required => true,
|
||||||
default => node()}],
|
example => node()}],
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"400">> =>
|
<<"400">> => emqx_mgmt_util:not_found_schema(<<"Node error">>, [<<"SOURCE_ERROR">>]),
|
||||||
emqx_mgmt_util:not_found_schema(<<"Node error">>, [<<"SOURCE_ERROR">>]),
|
<<"200">> => emqx_mgmt_util:response_schema(<<"Get EMQ X Node Stats">>, <<"stats">>)}}},
|
||||||
<<"200">> => #{
|
|
||||||
description => <<"Get EMQ X Node Stats">>,
|
|
||||||
schema => cowboy_swagger:schema(<<"stats">>)}}}},
|
|
||||||
{"/nodes/:node_name/stats", Metadata, node_metrics}.
|
{"/nodes/:node_name/stats", Metadata, node_metrics}.
|
||||||
|
|
||||||
%%%==============================================================================================
|
%%%==============================================================================================
|
||||||
|
|
|
@ -27,85 +27,83 @@
|
||||||
api_spec() ->
|
api_spec() ->
|
||||||
{
|
{
|
||||||
[publish_api(), publish_batch_api()],
|
[publish_api(), publish_batch_api()],
|
||||||
[request_message_schema(), mqtt_message_schema()]
|
[message_schema()]
|
||||||
}.
|
}.
|
||||||
|
|
||||||
publish_api() ->
|
publish_api() ->
|
||||||
MeteData = #{
|
MeteData = #{
|
||||||
post => #{
|
post => #{
|
||||||
description => "publish",
|
description => "publish",
|
||||||
parameters => [#{
|
'requestBody' => #{
|
||||||
name => message,
|
content => #{
|
||||||
in => body,
|
'application/json' => #{
|
||||||
required => true,
|
schema => #{
|
||||||
schema => minirest:ref(<<"request_message">>)
|
type => object,
|
||||||
}],
|
properties => maps:with([id], message_properties())}}}},
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"200">> => #{
|
<<"200">> => emqx_mgmt_util:response_schema(<<"publish ok">>, <<"message">>)}}},
|
||||||
description => <<"publish ok">>,
|
|
||||||
schema => minirest:ref(<<"message">>)}}}},
|
|
||||||
{"/publish", MeteData, publish}.
|
{"/publish", MeteData, publish}.
|
||||||
|
|
||||||
publish_batch_api() ->
|
publish_batch_api() ->
|
||||||
MeteData = #{
|
MeteData = #{
|
||||||
post => #{
|
post => #{
|
||||||
description => "publish",
|
description => "publish",
|
||||||
parameters => [#{
|
'requestBody' => #{
|
||||||
name => message,
|
content => #{
|
||||||
in => body,
|
'application/json' => #{
|
||||||
required => true,
|
schema => #{
|
||||||
schema =>#{
|
type => array,
|
||||||
type => array,
|
items => #{
|
||||||
items => minirest:ref(<<"request_message">>)}
|
type => object,
|
||||||
}],
|
properties => maps:with([id], message_properties())}}}}},
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"200">> => #{
|
<<"200">> => emqx_mgmt_util:response_array_schema(<<"publish ok">>, <<"message">>)}}},
|
||||||
description => <<"publish result">>,
|
|
||||||
schema => #{
|
|
||||||
type => array,
|
|
||||||
items => minirest:ref(<<"message">>)}}}}},
|
|
||||||
{"/publish_batch", MeteData, publish_batch}.
|
{"/publish_batch", MeteData, publish_batch}.
|
||||||
|
|
||||||
request_message_schema() ->
|
message_schema() ->
|
||||||
{<<"request_message">>, maps:without([<<"id">>], message_def())}.
|
|
||||||
|
|
||||||
mqtt_message_schema() ->
|
|
||||||
{<<"message">>, message_def()}.
|
|
||||||
|
|
||||||
message_def() ->
|
|
||||||
#{
|
#{
|
||||||
<<"id">> => #{
|
message => #{
|
||||||
type => <<"string">>,
|
type => object,
|
||||||
|
properties => message_properties()
|
||||||
|
}
|
||||||
|
}.
|
||||||
|
|
||||||
|
message_properties() ->
|
||||||
|
#{
|
||||||
|
id => #{
|
||||||
|
type => string,
|
||||||
description => <<"Message ID">>},
|
description => <<"Message ID">>},
|
||||||
<<"topic">> => #{
|
topic => #{
|
||||||
type => <<"string">>,
|
type => string,
|
||||||
description => <<"Topic">>},
|
description => <<"Topic">>},
|
||||||
<<"qos">> => #{
|
qos => #{
|
||||||
type => <<"integer">>,
|
type => integer,
|
||||||
enum => [0, 1, 2],
|
enum => [0, 1, 2],
|
||||||
description => <<"Qos">>},
|
description => <<"Qos">>},
|
||||||
<<"payload">> => #{
|
payload => #{
|
||||||
type => <<"string">>,
|
type => string,
|
||||||
description => <<"Topic">>},
|
description => <<"Topic">>},
|
||||||
<<"from">> => #{
|
from => #{
|
||||||
type => <<"string">>,
|
type => string,
|
||||||
description => <<"Message from">>},
|
description => <<"Message from">>},
|
||||||
<<"flag">> => #{
|
flag => #{
|
||||||
type => <<"object">>,
|
type => <<"object">>,
|
||||||
description => <<"Message flag">>,
|
description => <<"Message flag">>,
|
||||||
properties => #{
|
properties => #{
|
||||||
<<"sys">> => #{
|
sys => #{
|
||||||
type => <<"boolean">>,
|
type => boolean,
|
||||||
default => false,
|
default => false,
|
||||||
description => <<"System message flag, nullable, default false">>},
|
description => <<"System message flag, nullable, default false">>},
|
||||||
<<"dup">> => #{
|
dup => #{
|
||||||
type => <<"boolean">>,
|
type => boolean,
|
||||||
default => false,
|
default => false,
|
||||||
description => <<"Dup message flag, nullable, default false">>},
|
description => <<"Dup message flag, nullable, default false">>},
|
||||||
<<"retain">> => #{
|
retain => #{
|
||||||
type => <<"boolean">>,
|
type => boolean,
|
||||||
default => false,
|
default => false,
|
||||||
description => <<"Retain message flag, nullable, default false">>}}}
|
description => <<"Retain message flag, nullable, default false">>}
|
||||||
|
}
|
||||||
|
}
|
||||||
}.
|
}.
|
||||||
|
|
||||||
publish(post, Request) ->
|
publish(post, Request) ->
|
||||||
|
|
|
@ -22,81 +22,84 @@
|
||||||
-export([list/2]).
|
-export([list/2]).
|
||||||
|
|
||||||
api_spec() ->
|
api_spec() ->
|
||||||
{stats_api(), stats_schema()}.
|
{[stats_api()], [stats_schema()]}.
|
||||||
|
|
||||||
stats_schema() ->
|
stats_schema() ->
|
||||||
DefinitionName = <<"stats">>,
|
#{
|
||||||
DefinitionProperties = #{
|
stats => #{
|
||||||
<<"connections.count">> => #{
|
type => object,
|
||||||
type => <<"integer">>,
|
properties => #{
|
||||||
description => <<"Number of current connections">>},
|
'connections.count' => #{
|
||||||
<<"connections.max">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of current connections">>},
|
||||||
description => <<"Historical maximum number of connections">>},
|
'connections.max' => #{
|
||||||
<<"channels.count">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Historical maximum number of connections">>},
|
||||||
description => <<"sessions.count">>},
|
'channels.count' => #{
|
||||||
<<"channels.max">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"sessions.count">>},
|
||||||
description => <<"session.max">>},
|
'channels.max' => #{
|
||||||
<<"sessions.count">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"session.max">>},
|
||||||
description => <<"Number of current sessions">>},
|
'sessions.count' => #{
|
||||||
<<"sessions.max">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of current sessions">>},
|
||||||
description => <<"Historical maximum number of sessions">>},
|
'sessions.max' => #{
|
||||||
<<"topics.count">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Historical maximum number of sessions">>},
|
||||||
description => <<"Number of current topics">>},
|
'topics.count' => #{
|
||||||
<<"topics.max">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of current topics">>},
|
||||||
description => <<"Historical maximum number of topics">>},
|
'topics.max' => #{
|
||||||
<<"suboptions.count">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Historical maximum number of topics">>},
|
||||||
description => <<"subscriptions.count">>},
|
'suboptions.count' => #{
|
||||||
<<"suboptions.max">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"subscriptions.count">>},
|
||||||
description => <<"subscriptions.max">>},
|
'suboptions.max' => #{
|
||||||
<<"subscribers.count">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"subscriptions.max">>},
|
||||||
description => <<"Number of current subscribers">>},
|
'subscribers.count' => #{
|
||||||
<<"subscribers.max">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of current subscribers">>},
|
||||||
description => <<"Historical maximum number of subscribers">>},
|
'subscribers.max' => #{
|
||||||
<<"subscriptions.count">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Historical maximum number of subscribers">>},
|
||||||
description => <<"Number of current subscriptions, including shared subscriptions">>},
|
'subscriptions.count' => #{
|
||||||
<<"subscriptions.max">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of current subscriptions, including shared subscriptions">>},
|
||||||
description => <<"Historical maximum number of subscriptions">>},
|
'subscriptions.max' => #{
|
||||||
<<"subscriptions.shared.count">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Historical maximum number of subscriptions">>},
|
||||||
description => <<"Number of current shared subscriptions">>},
|
'subscriptions.shared.count' => #{
|
||||||
<<"subscriptions.shared.max">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of current shared subscriptions">>},
|
||||||
description => <<"Historical maximum number of shared subscriptions">>},
|
'subscriptions.shared.max' => #{
|
||||||
<<"routes.count">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Historical maximum number of shared subscriptions">>},
|
||||||
description => <<"Number of current routes">>},
|
'routes.count' => #{
|
||||||
<<"routes.max">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of current routes">>},
|
||||||
description => <<"Historical maximum number of routes">>},
|
'routes.max' => #{
|
||||||
<<"retained.count">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Historical maximum number of routes">>},
|
||||||
description => <<"Number of currently retained messages">>},
|
'retained.count' => #{
|
||||||
<<"retained.max">> => #{
|
type => integer,
|
||||||
type => <<"integer">>,
|
description => <<"Number of currently retained messages">>},
|
||||||
description => <<"Historical maximum number of retained messages">>}},
|
'retained.max' => #{
|
||||||
[{DefinitionName, DefinitionProperties}].
|
type => integer,
|
||||||
|
description => <<"Historical maximum number of retained messages">>}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.
|
||||||
|
|
||||||
stats_api() ->
|
stats_api() ->
|
||||||
Metadata = #{
|
Metadata = #{
|
||||||
get => #{
|
get => #{
|
||||||
description => "EMQ X stats",
|
description => "EMQ X stats",
|
||||||
responses => #{
|
responses => #{
|
||||||
<<"200">> => #{
|
<<"200">> => emqx_mgmt_util:response_schema(<<"List stats ok">>, <<"stats">>)}}},
|
||||||
schema => cowboy_swagger:schema(<<"stats">>)}}}},
|
{"/stats", Metadata, list}.
|
||||||
[{"/stats", Metadata, list}].
|
|
||||||
|
|
||||||
%%%==============================================================================================
|
%%%==============================================================================================
|
||||||
%% api apply
|
%% api apply
|
||||||
|
|
|
@ -44,14 +44,16 @@ start_listener({Proto, Port, Options}) ->
|
||||||
Authorization = {?MODULE, authorize_appid},
|
Authorization = {?MODULE, authorize_appid},
|
||||||
RanchOptions = ranch_opts(Port, Options),
|
RanchOptions = ranch_opts(Port, Options),
|
||||||
GlobalSpec = #{
|
GlobalSpec = #{
|
||||||
swagger => "2.0",
|
openapi => "3.0.0",
|
||||||
info => #{title => "EMQ X API", version => "5.0.0"},
|
info => #{title => "EMQ X API", version => "5.0.0"},
|
||||||
basePath => ?BASE_PATH,
|
servers => [#{url => ?BASE_PATH}],
|
||||||
securityDefinitions => #{
|
components => #{
|
||||||
application => #{
|
schemas => #{},
|
||||||
type => apiKey,
|
securitySchemes => #{
|
||||||
name => "authorization",
|
application => #{
|
||||||
in => header}}},
|
type => apiKey,
|
||||||
|
name => "authorization",
|
||||||
|
in => header}}}},
|
||||||
Minirest = #{
|
Minirest = #{
|
||||||
protocol => Proto,
|
protocol => Proto,
|
||||||
base_path => ?BASE_PATH,
|
base_path => ?BASE_PATH,
|
||||||
|
|
|
@ -24,7 +24,12 @@
|
||||||
, batch_operation/3
|
, batch_operation/3
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([ not_found_schema/1
|
-export([ request_body_schema/1
|
||||||
|
, request_body_array_schema/1
|
||||||
|
, response_schema/1
|
||||||
|
, response_schema/2
|
||||||
|
, response_array_schema/2
|
||||||
|
, not_found_schema/1
|
||||||
, not_found_schema/2
|
, not_found_schema/2
|
||||||
, batch_response_schema/1]).
|
, batch_response_schema/1]).
|
||||||
|
|
||||||
|
@ -84,24 +89,46 @@ urldecode(S) ->
|
||||||
|
|
||||||
%%%==============================================================================================
|
%%%==============================================================================================
|
||||||
%% schema util
|
%% schema util
|
||||||
|
|
||||||
|
request_body_array_schema(Schema) when is_map(Schema) ->
|
||||||
|
json_content_schema("", #{type => array, items => Schema});
|
||||||
|
request_body_array_schema(Ref) when is_binary(Ref) ->
|
||||||
|
json_content_schema("", #{type => array, items => minirest:ref(Ref)}).
|
||||||
|
|
||||||
|
request_body_schema(Schema) when is_map(Schema) ->
|
||||||
|
json_content_schema("", Schema);
|
||||||
|
request_body_schema(Ref) when is_binary(Ref) ->
|
||||||
|
json_content_schema("", minirest:ref(Ref)).
|
||||||
|
|
||||||
|
response_array_schema(Description, Schema) when is_map(Schema) ->
|
||||||
|
json_content_schema(Description, #{type => array, items => Schema});
|
||||||
|
response_array_schema(Description, Ref) when is_binary(Ref) ->
|
||||||
|
json_content_schema(Description, #{type => array, items => minirest:ref(Ref)}).
|
||||||
|
|
||||||
|
response_schema(Description) ->
|
||||||
|
json_content_schema(Description).
|
||||||
|
|
||||||
|
response_schema(Description, Schema) when is_map(Schema) ->
|
||||||
|
json_content_schema(Description, Schema);
|
||||||
|
response_schema(Description, Ref) when is_binary(Ref) ->
|
||||||
|
json_content_schema(Description, minirest:ref(Ref)).
|
||||||
|
|
||||||
not_found_schema(Description) ->
|
not_found_schema(Description) ->
|
||||||
not_found_schema(Description, ["RESOURCE_NOT_FOUND"]).
|
not_found_schema(Description, ["RESOURCE_NOT_FOUND"]).
|
||||||
|
|
||||||
not_found_schema(Description, Enum) ->
|
not_found_schema(Description, Enum) ->
|
||||||
#{
|
Schema = #{
|
||||||
description => Description,
|
type => object,
|
||||||
schema => #{
|
properties => #{
|
||||||
type => object,
|
code => #{
|
||||||
properties => #{
|
type => string,
|
||||||
code => #{
|
enum => Enum},
|
||||||
type => string,
|
reason => #{
|
||||||
enum => Enum},
|
type => string}}},
|
||||||
reason => #{
|
json_content_schema(Description, Schema).
|
||||||
type => string}}}
|
|
||||||
}.
|
|
||||||
|
|
||||||
batch_response_schema(DefName) ->
|
batch_response_schema(DefName) when is_binary(DefName) ->
|
||||||
#{
|
Schema = #{
|
||||||
type => object,
|
type => object,
|
||||||
properties => #{
|
properties => #{
|
||||||
success => #{
|
success => #{
|
||||||
|
@ -119,13 +146,23 @@ batch_response_schema(DefName) ->
|
||||||
#{
|
#{
|
||||||
data => minirest:ref(DefName),
|
data => minirest:ref(DefName),
|
||||||
reason => #{
|
reason => #{
|
||||||
type => <<"string">>
|
type => <<"string">>}}}}}},
|
||||||
}
|
json_content_schema("", Schema).
|
||||||
}
|
|
||||||
}
|
json_content_schema(Description, Schema) ->
|
||||||
}
|
Content =
|
||||||
}
|
#{content => #{
|
||||||
}.
|
'application/json' => #{
|
||||||
|
schema => Schema}}},
|
||||||
|
case Description of
|
||||||
|
"" ->
|
||||||
|
Content;
|
||||||
|
_ ->
|
||||||
|
maps:merge(#{description => Description}, Content)
|
||||||
|
end.
|
||||||
|
|
||||||
|
json_content_schema(Description) ->
|
||||||
|
#{description => Description}.
|
||||||
|
|
||||||
%%%==============================================================================================
|
%%%==============================================================================================
|
||||||
batch_operation(Module, Function, ArgsList) ->
|
batch_operation(Module, Function, ArgsList) ->
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
, {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.10.3"}}}
|
, {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.10.3"}}}
|
||||||
, {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.5.1"}}}
|
, {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.5.1"}}}
|
||||||
, {cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v4.0.1"}}} % TODO: delete when all apps moved to hocon
|
, {cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v4.0.1"}}} % TODO: delete when all apps moved to hocon
|
||||||
, {minirest, {git, "https://github.com/emqx/minirest", {tag, "1.1.1"}}}
|
, {minirest, {git, "https://github.com/emqx/minirest", {tag, "1.1.2"}}}
|
||||||
, {ecpool, {git, "https://github.com/emqx/ecpool", {tag, "0.5.1"}}}
|
, {ecpool, {git, "https://github.com/emqx/ecpool", {tag, "0.5.1"}}}
|
||||||
, {replayq, {git, "https://github.com/emqx/replayq", {tag, "0.3.2"}}}
|
, {replayq, {git, "https://github.com/emqx/replayq", {tag, "0.3.2"}}}
|
||||||
, {pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {tag, "2.0.4"}}}
|
, {pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {tag, "2.0.4"}}}
|
||||||
|
|
Loading…
Reference in New Issue