Prechádzať zdrojové kódy

feat: update to openapi 3.0.0

DDDHuang 4 rokov pred
rodič
commit
92ae5663a6

+ 173 - 174
apps/emqx_management/src/emqx_mgmt_api_clients.erl

@@ -71,144 +71,152 @@ apis() ->
     , subscribe_api()].
 
 schemas() ->
-    ClientDef = #{
-        <<"node">> => #{
-            type => <<"string">>,
-            description => <<"Name of the node to which the client is connected">>},
-        <<"clientid">> => #{
-            type => <<"string">>,
-            description => <<"Client identifier">>},
-        <<"username">> => #{
-            type => <<"string">>,
-            description => <<"User name of client when connecting">>},
-        <<"proto_name">> => #{
-            type => <<"string">>,
-            description => <<"Client protocol name">>},
-        <<"proto_ver">> => #{
-            type => <<"integer">>,
-            description => <<"Protocol version used by the client">>},
-        <<"ip_address">> => #{
-            type => <<"string">>,
-            description => <<"Client's IP address">>},
-        <<"is_bridge">> => #{
-            type => <<"boolean">>,
-            description => <<"Indicates whether the client is connectedvia bridge">>},
-        <<"connected_at">> => #{
-            type => <<"string">>,
-            description => <<"Client connection time">>},
-        <<"disconnected_at">> => #{
-            type => <<"string">>,
-            description => <<"Client offline time, This field is only valid and returned when connected is false">>},
-        <<"connected">> => #{
-            type => <<"boolean">>,
-            description => <<"Whether the client is connected">>},
-        <<"will_msg">> => #{
-            type => <<"string">>,
-            description => <<"Client will message">>},
-        <<"zone">> => #{
-            type => <<"string">>,
-            description => <<"Indicate the configuration group used by the client">>},
-        <<"keepalive">> => #{
-            type => <<"integer">>,
-            description => <<"keepalive time, with the unit of second">>},
-        <<"clean_start">> => #{
-            type => <<"boolean">>,
-            description => <<"Indicate whether the client is using a brand new session">>},
-        <<"expiry_interval">> => #{
-            type => <<"integer">>,
-            description => <<"Session expiration interval, with the unit of second">>},
-        <<"created_at">> => #{
-            type => <<"string">>,
-            description => <<"Session creation time">>},
-        <<"subscriptions_cnt">> => #{
-            type => <<"integer">>,
-            description => <<"Number of subscriptions established by this client.">>},
-        <<"subscriptions_max">> => #{
-            type => <<"integer">>,
-            description => <<"v4 api name [max_subscriptions] Maximum number of subscriptions allowed by this client">>},
-        <<"inflight_cnt">> => #{
-            type => <<"integer">>,
-            description => <<"Current length of inflight">>},
-        <<"inflight_max">> => #{
-            type => <<"integer">>,
-            description => <<"v4 api name [max_inflight]. Maximum length of inflight">>},
-        <<"mqueue_len">> => #{
-            type => <<"integer">>,
-            description => <<"Current length of message queue">>},
-        <<"mqueue_max">> => #{
-            type => <<"integer">>,
-            description => <<"v4 api name [max_mqueue]. Maximum length of message queue">>},
-        <<"mqueue_dropped">> => #{
-            type => <<"integer">>,
-            description => <<"Number of messages dropped by the message queue due to exceeding the length">>},
-        <<"awaiting_rel_cnt">> => #{
-            type => <<"integer">>,
-            description => <<"v4 api name [awaiting_rel] Number of awaiting PUBREC packet">>},
-        <<"awaiting_rel_max">> => #{
-            type => <<"integer">>,
-            description => <<"v4 api name [max_awaiting_rel]. Maximum allowed number of awaiting PUBREC packet">>},
-        <<"recv_oct">> => #{
-            type => <<"integer">>,
-            description => <<"Number of bytes received by EMQ X Broker (the same below)">>},
-        <<"recv_cnt">> => #{
-            type => <<"integer">>,
-            description => <<"Number of TCP packets received">>},
-        <<"recv_pkt">> => #{
-            type => <<"integer">>,
-            description => <<"Number of MQTT packets received">>},
-        <<"recv_msg">> => #{
-            type => <<"integer">>,
-            description => <<"Number of PUBLISH packets received">>},
-        <<"send_oct">> => #{
-            type => <<"integer">>,
-            description => <<"Number of bytes sent">>},
-        <<"send_cnt">> => #{
-            type => <<"integer">>,
-            description => <<"Number of TCP packets sent">>},
-        <<"send_pkt">> => #{
-            type => <<"integer">>,
-            description => <<"Number of MQTT packets sent">>},
-        <<"send_msg">> => #{
-            type => <<"integer">>,
-            description => <<"Number of PUBLISH packets sent">>},
-        <<"mailbox_len">> => #{
-            type => <<"integer">>,
-            description => <<"Process mailbox size">>},
-        <<"heap_size">> => #{
-            type => <<"integer">>,
-            description => <<"Process heap size with the unit of byte">>
-        },
-        <<"reductions">> => #{
-            type => <<"integer">>,
-            description => <<"Erlang reduction">>}},
-    ACLCacheDefinitionProperties = #{
-        <<"topic">> => #{
-            type => <<"string">>,
-            description => <<"Topic name">>},
-        <<"access">> => #{
-            type => <<"string">>,
-            enum => [<<"subscribe">>, <<"publish">>],
-            description => <<"Access type">>},
-        <<"result">> => #{
-            type => <<"string">>,
-            enum => [<<"allow">>, <<"deny">>],
-            default => <<"allow">>,
-            description => <<"Allow or deny">>},
-        <<"updated_time">> => #{
-            type => <<"integer">>,
-            description => <<"Update time">>}},
-    [{<<"client">>, ClientDef}, {<<"acl_cache">>, ACLCacheDefinitionProperties}].
+    Client = #{
+        client => #{
+            type => object,
+            properties => #{
+                node => #{
+                    type => string,
+                    description => <<"Name of the node to which the client is connected">>},
+                clientid => #{
+                    type => string,
+                    description => <<"Client identifier">>},
+                username => #{
+                    type => string,
+                    description => <<"User name of client when connecting">>},
+                proto_name => #{
+                    type => string,
+                    description => <<"Client protocol name">>},
+                proto_ver => #{
+                    type => integer,
+                    description => <<"Protocol version used by the client">>},
+                ip_address => #{
+                    type => string,
+                    description => <<"Client's IP address">>},
+                is_bridge => #{
+                    type => boolean,
+                    description => <<"Indicates whether the client is connectedvia bridge">>},
+                connected_at => #{
+                    type => string,
+                    description => <<"Client connection time">>},
+                disconnected_at => #{
+                    type => string,
+                    description => <<"Client offline time, This field is only valid and returned when connected is false">>},
+                connected => #{
+                    type => boolean,
+                    description => <<"Whether the client is connected">>},
+                will_msg => #{
+                    type => string,
+                    description => <<"Client will message">>},
+                zone => #{
+                    type => string,
+                    description => <<"Indicate the configuration group used by the client">>},
+                keepalive => #{
+                    type => integer,
+                    description => <<"keepalive time, with the unit of second">>},
+                clean_start => #{
+                    type => boolean,
+                    description => <<"Indicate whether the client is using a brand new session">>},
+                expiry_interval => #{
+                    type => integer,
+                    description => <<"Session expiration interval, with the unit of second">>},
+                created_at => #{
+                    type => string,
+                    description => <<"Session creation time">>},
+                subscriptions_cnt => #{
+                    type => integer,
+                    description => <<"Number of subscriptions established by this client.">>},
+                subscriptions_max => #{
+                    type => integer,
+                    description => <<"v4 api name [max_subscriptions] Maximum number of subscriptions allowed by this client">>},
+                inflight_cnt => #{
+                    type => integer,
+                    description => <<"Current length of inflight">>},
+                inflight_max => #{
+                    type => integer,
+                    description => <<"v4 api name [max_inflight]. Maximum length of inflight">>},
+                mqueue_len => #{
+                    type => integer,
+                    description => <<"Current length of message queue">>},
+                mqueue_max => #{
+                    type => integer,
+                    description => <<"v4 api name [max_mqueue]. Maximum length of message queue">>},
+                mqueue_dropped => #{
+                    type => integer,
+                    description => <<"Number of messages dropped by the message queue due to exceeding the length">>},
+                awaiting_rel_cnt => #{
+                    type => integer,
+                    description => <<"v4 api name [awaiting_rel] Number of awaiting PUBREC packet">>},
+                awaiting_rel_max => #{
+                    type => integer,
+                    description => <<"v4 api name [max_awaiting_rel]. Maximum allowed number of awaiting PUBREC packet">>},
+                recv_oct => #{
+                    type => integer,
+                    description => <<"Number of bytes received by EMQ X Broker (the same below)">>},
+                recv_cnt => #{
+                    type => integer,
+                    description => <<"Number of TCP packets received">>},
+                recv_pkt => #{
+                    type => integer,
+                    description => <<"Number of MQTT packets received">>},
+                recv_msg => #{
+                    type => integer,
+                    description => <<"Number of PUBLISH packets received">>},
+                send_oct => #{
+                    type => integer,
+                    description => <<"Number of bytes sent">>},
+                send_cnt => #{
+                    type => integer,
+                    description => <<"Number of TCP packets sent">>},
+                send_pkt => #{
+                    type => integer,
+                    description => <<"Number of MQTT packets sent">>},
+                send_msg => #{
+                    type => integer,
+                    description => <<"Number of PUBLISH packets sent">>},
+                mailbox_len => #{
+                    type => integer,
+                    description => <<"Process mailbox size">>},
+                heap_size => #{
+                    type => integer,
+                    description => <<"Process heap size with the unit of byte">>
+                },
+                reductions => #{
+                    type => integer,
+                    description => <<"Erlang reduction">>}
+            }
+        }
+       },
+    AclCache = #{
+        acl_cache => #{
+            type => object,
+            properties => #{
+                topic => #{
+                    type => string,
+                    description => <<"Topic name">>},
+                access => #{
+                    type => string,
+                    enum => [<<"subscribe">>, <<"publish">>],
+                    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() ->
     Metadata = #{
         get => #{
             description => "List clients",
             responses => #{
-                <<"200">> => #{
-                    description => <<"List clients 200 OK">>,
-                    schema => #{
-                        type => array,
-                        items => minirest:ref(<<"client">>)}}}}},
+                <<"200">> => emqx_mgmt_util:response_array_schema(<<"List clients 200 OK">>, <<"client">>)}}},
     {"/clients", Metadata, clients}.
 
 client_api() ->
@@ -218,25 +226,23 @@ client_api() ->
             parameters => [#{
                 name => clientid,
                 in => path,
-                type => string,
+                schema => #{type => string},
                 required => true,
-                default => 123456}],
+                example => 123456}],
             responses => #{
                 <<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
-                <<"200">> => #{
-                    description => <<"Get clients 200 OK">>,
-                    schema => minirest:ref(<<"client">>)}}},
+                <<"200">> => emqx_mgmt_util:response_schema(<<"List clients 200 OK">>, <<"client">>)}},
         delete => #{
             description => "Kick out client by client ID",
             parameters => [#{
                 name => clientid,
                 in => path,
-                type => string,
+                schema => #{type => string},
                 required => true,
-                default => 123456}],
+                example => 123456}],
             responses => #{
                 <<"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_acl_cache_api() ->
@@ -246,26 +252,23 @@ clients_acl_cache_api() ->
             parameters => [#{
                 name => clientid,
                 in => path,
-                type => string,
+                schema => #{type => string},
                 required => true,
-                default => 123456}],
+                example => 123456}],
             responses => #{
                 <<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
-                <<"200">> => #{
-                    description => <<"List 200 OK">>,
-                    schema => minirest:ref(<<"acl_cache">>)}}},
+                <<"200">> => emqx_mgmt_util:response_schema(<<"List clients 200 OK">>, <<"acl_cache">>)}},
         delete => #{
             description => "Clean client acl cache",
             parameters => [#{
                 name => clientid,
                 in => path,
-                type => string,
+                schema => #{type => string},
                 required => true,
-                default => 123456}],
+                example => 123456}],
             responses => #{
-                <<"404">> => emqx_mgmt_util:not_found_schema(<<"client id not found">>),
-                <<"200">> => #{
-                    description => <<"Clean acl cache 200 OK">>}}}},
+                <<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
+                <<"200">> => emqx_mgmt_util:response_schema(<<"Delete clients 200 OK">>)}}},
     {"/clients/:clientid/acl_cache", Metadata, acl_cache}.
 
 subscribe_api() ->
@@ -276,51 +279,47 @@ subscribe_api() ->
                 #{
                     name => clientid,
                     in => path,
-                    type => string,
+                    schema => #{type => string},
                     required => true,
-                    default => 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">>}}}
+                    example => 123456
                 }
             ],
+            '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 => #{
                 <<"404">> => emqx_mgmt_util:not_found_schema(<<"Client id not found">>),
-                <<"200">> => #{description => <<"subscribe ok">>}}},
+                <<"200">> => emqx_mgmt_util:response_schema(<<"subscribe ok">>)}},
         delete => #{
             description => "unsubscribe",
             parameters => [
                 #{
                     name => clientid,
                     in => path,
-                    type => string,
+                    schema => #{type => string},
                     required => true,
-                    default => 123456
+                    example => 123456
                 },
                 #{
                     name => topic,
                     in => query,
-                    type => string,
+                    schema => #{type => string},
                     required => true,
-                    default => <<"topic_1">>
+                    example => <<"topic_1">>
                 }
             ],
             responses => #{
                 <<"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}.
 
 %%%==============================================================================================

+ 254 - 251
apps/emqx_management/src/emqx_mgmt_api_metrics.erl

@@ -26,263 +26,266 @@ api_spec() ->
     {[metrics_api()], [metrics_schema()]}.
 
 metrics_schema() ->
-    DefinitionName = <<"metrics">>,
-    DefinitionProperties = #{
-        <<"actions.failure">> => #{
-            type => <<"integer">>,
-            description => <<"Number of failure executions of the rule engine action">>},
-        <<"actions.success">> => #{
-            type => <<"integer">>,
-            description => <<"Number of successful executions of the rule engine action">>},
-        <<"bytes.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of bytes received by EMQ X Broker">>},
-        <<"bytes.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of bytes sent by EMQ X Broker on this connection">>},
-        <<"client.authenticate">> => #{
-            type => <<"integer">>,
-            description => <<"Number of client authentications">>},
-        <<"client.auth.anonymous">> => #{
-            type => <<"integer">>,
-            description => <<"Number of clients who log in anonymously">>},
-        <<"client.connect">> => #{
-            type => <<"integer">>,
-            description => <<"Number of client connections">>},
-        <<"client.connack">> => #{
-            type => <<"integer">>,
-            description => <<"Number of CONNACK packet sent">>},
-        <<"client.connected">> => #{
-            type => <<"integer">>,
-            description => <<"Number of successful client connections">>},
-        <<"client.disconnected">> => #{
-            type => <<"integer">>,
-            description => <<"Number of client disconnects">>},
-        <<"client.check_acl">> => #{
-            type => <<"integer">>,
-            description => <<"Number of ACL rule checks">>},
-        <<"client.subscribe">> => #{
-            type => <<"integer">>,
-            description => <<"Number of client subscriptions">>},
-        <<"client.unsubscribe">> => #{
-            type => <<"integer">>,
-            description => <<"Number of client unsubscriptions">>},
-        <<"delivery.dropped.too_large">> => #{
-            type => <<"integer">>,
-            description => <<"The number of messages that were dropped because the length exceeded the limit when sending">>},
-        <<"delivery.dropped.queue_full">> => #{
-            type => <<"integer">>,
-            description => <<"Number of messages with a non-zero QoS that were dropped because the message queue was full when sending">>},
-        <<"delivery.dropped.qos0_msg">> => #{
-            type => <<"integer">>,
-            description => <<"Number of messages with QoS 0 that were dropped because the message queue was full when sending">>},
-        <<"delivery.dropped.expired">> => #{
-            type => <<"integer">>,
-            description => <<"Number of messages dropped due to message expiration on sending">>},
-        <<"delivery.dropped.no_local">> => #{
-            type => <<"integer">>,
-            description => <<"Number of messages that were dropped due to the No Local subscription option when sending">>},
-        <<"delivery.dropped">> => #{
-            type => <<"integer">>,
-            description => <<"Total number of discarded messages when sending">>},
-        <<"messages.delayed">> => #{
-            type => <<"integer">>,
-            description => <<"Number of delay- published messages stored by EMQ X Broker">>},
-        <<"messages.delivered">> => #{
-            type => <<"integer">>,
-            description => <<"Number of messages forwarded to the subscription process internally by EMQ X Broker">>},
-        <<"messages.dropped">> => #{
-            type => <<"integer">>,
-            description => <<"Total number of messages dropped by EMQ X Broker before forwarding to the subscription process">>},
-        <<"messages.dropped.expired">> => #{
-            type => <<"integer">>,
-            description => <<"Number of messages dropped due to message expiration when receiving">>},
-        <<"messages.dropped.no_subscribers">> => #{
-            type => <<"integer">>,
-            description => <<"Number of messages dropped due to no subscribers">>},
-        <<"messages.forward">> => #{
-            type => <<"integer">>,
-            description => <<"Number of messages forwarded to other nodes">>},
-        <<"messages.publish">> => #{
-            type => <<"integer">>,
-            description => <<"Number of messages published in addition to system messages">>},
-        <<"messages.qos0.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of QoS 0 messages received from clients">>},
-        <<"messages.qos1.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of QoS 1 messages received from clients">>},
-        <<"messages.qos2.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of QoS 2 messages received from clients">>},
-        <<"messages.qos0.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of QoS 0 messages sent to clients">>},
-        <<"messages.qos1.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of QoS 1 messages sent to clients">>},
-        <<"messages.qos2.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of QoS 2 messages sent to clients">>},
-        <<"messages.received">> => #{
-            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">>},
-        <<"messages.sent">> => #{
-            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">>},
-        <<"messages.retained">> => #{
-            type => <<"integer">>,
-            description => <<"Number of retained messages stored by EMQ X Broker">>},
-        <<"messages.acked">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBACK and PUBREC packet">>},
-        <<"packets.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received packet">>},
-        <<"packets.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sent packet">>},
-        <<"packets.connect.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received CONNECT packet">>},
-        <<"packets.connack.auth_error">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received CONNECT packet with failed authentication">>},
-        <<"packets.connack.error">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received CONNECT packet with unsuccessful connections">>},
-        <<"packets.connack.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sent CONNACK packet">>},
-        <<"packets.publish.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBLISH packet">>},
-        <<"packets.publish.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sent PUBLISH packet">>},
-        <<"packets.publish.inuse">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBLISH packet with occupied identifiers">>},
-        <<"packets.publish.auth_error">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBLISH packets with failed the ACL check">>},
-        <<"packets.publish.error">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBLISH packet that cannot be published">>},
-        <<"packets.publish.dropped">> => #{
-            type => <<"integer">>,
-            description => <<"Number of messages discarded due to the receiving limit">>},
-        <<"packets.puback.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBACK packet">>},
-        <<"packets.puback.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sent PUBACK packet">>},
-        <<"packets.puback.inuse">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBACK packet with occupied identifiers">>},
-        <<"packets.puback.missed">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received packet with identifiers.">>},
-        <<"packets.pubrec.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBREC packet">>},
-        <<"packets.pubrec.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sent PUBREC packet">>},
-        <<"packets.pubrec.inuse">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBREC packet with occupied identifiers">>},
-        <<"packets.pubrec.missed">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBREC packet with unknown identifiers">>},
-        <<"packets.pubrel.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBREL packet">>},
-        <<"packets.pubrel.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sent PUBREL packet">>},
-        <<"packets.pubrel.missed">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBREC packet with unknown identifiers">>},
-        <<"packets.pubcomp.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBCOMP packet">>},
-        <<"packets.pubcomp.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sent PUBCOMP packet">>},
-        <<"packets.pubcomp.inuse">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PUBCOMP packet with occupied identifiers">>},
-        <<"packets.pubcomp.missed">> => #{
-            type => <<"integer">>,
-            description => <<"Number of missed PUBCOMP packet">>},
-        <<"packets.subscribe.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received SUBSCRIBE packet">>},
-        <<"packets.subscribe.error">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received SUBSCRIBE packet with failed subscriptions">>},
-        <<"packets.subscribe.auth_error">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received SUBACK packet with failed ACL check">>},
-        <<"packets.suback.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sent SUBACK packet">>},
-        <<"packets.unsubscribe.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received UNSUBSCRIBE packet">>},
-        <<"packets.unsubscribe.error">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received UNSUBSCRIBE packet with failed unsubscriptions">>},
-        <<"packets.unsuback.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sent UNSUBACK packet">>},
-        <<"packets.pingreq.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received PINGREQ packet">>},
-        <<"packets.pingresp.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sent PUBRESP packet">>},
-        <<"packets.disconnect.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received DISCONNECT packet">>},
-        <<"packets.disconnect.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sent DISCONNECT packet">>},
-        <<"packets.auth.received">> => #{
-            type => <<"integer">>,
-            description => <<"Number of received AUTH packet">>},
-        <<"packets.auth.sent">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sent AUTH packet">>},
-        <<"rules.matched">> => #{
-            type => <<"integer">>,
-            description => <<"Number of rule matched">>},
-        <<"session.created">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sessions created">>},
-        <<"session.discarded">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sessions dropped because Clean Session or Clean Start is true">>},
-        <<"session.resumed">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sessions resumed because Clean Session or Clean Start is false">>},
-        <<"session.takeovered">> => #{
-            type => <<"integer">>,
-            description => <<"Number of sessions takeovered because Clean Session or Clean Start is false">>},
-        <<"session.terminated">> => #{
-            type => <<"integer">>,
-            description => <<"Number of terminated sessions">>}},
-    {DefinitionName, DefinitionProperties}.
+    #{
+        metrics => #{
+            type => object,
+            properties => #{
+                'actions.failure' => #{
+                    type => integer,
+                    description => <<"Number of failure executions of the rule engine action">>},
+                'actions.success' => #{
+                    type => integer,
+                    description => <<"Number of successful executions of the rule engine action">>},
+                'bytes.received' => #{
+                    type => integer,
+                    description => <<"Number of bytes received by EMQ X Broker">>},
+                'bytes.sent' => #{
+                    type => integer,
+                    description => <<"Number of bytes sent by EMQ X Broker on this connection">>},
+                'client.authenticate' => #{
+                    type => integer,
+                    description => <<"Number of client authentications">>},
+                'client.auth.anonymous' => #{
+                    type => integer,
+                    description => <<"Number of clients who log in anonymously">>},
+                'client.connect' => #{
+                    type => integer,
+                    description => <<"Number of client connections">>},
+                'client.connack' => #{
+                    type => integer,
+                    description => <<"Number of CONNACK packet sent">>},
+                'client.connected' => #{
+                    type => integer,
+                    description => <<"Number of successful client connections">>},
+                'client.disconnected' => #{
+                    type => integer,
+                    description => <<"Number of client disconnects">>},
+                'client.check_acl' => #{
+                    type => integer,
+                    description => <<"Number of ACL rule checks">>},
+                'client.subscribe' => #{
+                    type => integer,
+                    description => <<"Number of client subscriptions">>},
+                'client.unsubscribe' => #{
+                    type => integer,
+                    description => <<"Number of client unsubscriptions">>},
+                'delivery.dropped.too_large' => #{
+                    type => integer,
+                    description => <<"The number of messages that were dropped because the length exceeded the limit when sending">>},
+                'delivery.dropped.queue_full' => #{
+                    type => integer,
+                    description => <<"Number of messages with a non-zero QoS that were dropped because the message queue was full when sending">>},
+                'delivery.dropped.qos0_msg' => #{
+                    type => integer,
+                    description => <<"Number of messages with QoS 0 that were dropped because the message queue was full when sending">>},
+                'delivery.dropped.expired' => #{
+                    type => integer,
+                    description => <<"Number of messages dropped due to message expiration on sending">>},
+                'delivery.dropped.no_local' => #{
+                    type => integer,
+                    description => <<"Number of messages that were dropped due to the No Local subscription option when sending">>},
+                'delivery.dropped' => #{
+                    type => integer,
+                    description => <<"Total number of discarded messages when sending">>},
+                'messages.delayed' => #{
+                    type => integer,
+                    description => <<"Number of delay- published messages stored by EMQ X Broker">>},
+                'messages.delivered' => #{
+                    type => integer,
+                    description => <<"Number of messages forwarded to the subscription process internally by EMQ X Broker">>},
+                'messages.dropped' => #{
+                    type => integer,
+                    description => <<"Total number of messages dropped by EMQ X Broker before forwarding to the subscription process">>},
+                'messages.dropped.expired' => #{
+                    type => integer,
+                    description => <<"Number of messages dropped due to message expiration when receiving">>},
+                'messages.dropped.no_subscribers' => #{
+                    type => integer,
+                    description => <<"Number of messages dropped due to no subscribers">>},
+                'messages.forward' => #{
+                    type => integer,
+                    description => <<"Number of messages forwarded to other nodes">>},
+                'messages.publish' => #{
+                    type => integer,
+                    description => <<"Number of messages published in addition to system messages">>},
+                'messages.qos0.received' => #{
+                    type => integer,
+                    description => <<"Number of QoS 0 messages received from clients">>},
+                'messages.qos1.received' => #{
+                    type => integer,
+                    description => <<"Number of QoS 1 messages received from clients">>},
+                'messages.qos2.received' => #{
+                    type => integer,
+                    description => <<"Number of QoS 2 messages received from clients">>},
+                'messages.qos0.sent' => #{
+                    type => integer,
+                    description => <<"Number of QoS 0 messages sent to clients">>},
+                'messages.qos1.sent' => #{
+                    type => integer,
+                    description => <<"Number of QoS 1 messages sent to clients">>},
+                'messages.qos2.sent' => #{
+                    type => integer,
+                    description => <<"Number of QoS 2 messages sent to clients">>},
+                'messages.received' => #{
+                    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">>},
+                'messages.sent' => #{
+                    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">>},
+                'messages.retained' => #{
+                    type => integer,
+                    description => <<"Number of retained messages stored by EMQ X Broker">>},
+                'messages.acked' => #{
+                    type => integer,
+                    description => <<"Number of received PUBACK and PUBREC packet">>},
+                'packets.received' => #{
+                    type => integer,
+                    description => <<"Number of received packet">>},
+                'packets.sent' => #{
+                    type => integer,
+                    description => <<"Number of sent packet">>},
+                'packets.connect.received' => #{
+                    type => integer,
+                    description => <<"Number of received CONNECT packet">>},
+                'packets.connack.auth_error' => #{
+                    type => integer,
+                    description => <<"Number of received CONNECT packet with failed authentication">>},
+                'packets.connack.error' => #{
+                    type => integer,
+                    description => <<"Number of received CONNECT packet with unsuccessful connections">>},
+                'packets.connack.sent' => #{
+                    type => integer,
+                    description => <<"Number of sent CONNACK packet">>},
+                'packets.publish.received' => #{
+                    type => integer,
+                    description => <<"Number of received PUBLISH packet">>},
+                'packets.publish.sent' => #{
+                    type => integer,
+                    description => <<"Number of sent PUBLISH packet">>},
+                'packets.publish.inuse' => #{
+                    type => integer,
+                    description => <<"Number of received PUBLISH packet with occupied identifiers">>},
+                'packets.publish.auth_error' => #{
+                    type => integer,
+                    description => <<"Number of received PUBLISH packets with failed the ACL check">>},
+                'packets.publish.error' => #{
+                    type => integer,
+                    description => <<"Number of received PUBLISH packet that cannot be published">>},
+                'packets.publish.dropped' => #{
+                    type => integer,
+                    description => <<"Number of messages discarded due to the receiving limit">>},
+                'packets.puback.received' => #{
+                    type => integer,
+                    description => <<"Number of received PUBACK packet">>},
+                'packets.puback.sent' => #{
+                    type => integer,
+                    description => <<"Number of sent PUBACK packet">>},
+                'packets.puback.inuse' => #{
+                    type => integer,
+                    description => <<"Number of received PUBACK packet with occupied identifiers">>},
+                'packets.puback.missed' => #{
+                    type => integer,
+                    description => <<"Number of received packet with identifiers.">>},
+                'packets.pubrec.received' => #{
+                    type => integer,
+                    description => <<"Number of received PUBREC packet">>},
+                'packets.pubrec.sent' => #{
+                    type => integer,
+                    description => <<"Number of sent PUBREC packet">>},
+                'packets.pubrec.inuse' => #{
+                    type => integer,
+                    description => <<"Number of received PUBREC packet with occupied identifiers">>},
+                'packets.pubrec.missed' => #{
+                    type => integer,
+                    description => <<"Number of received PUBREC packet with unknown identifiers">>},
+                'packets.pubrel.received' => #{
+                    type => integer,
+                    description => <<"Number of received PUBREL packet">>},
+                'packets.pubrel.sent' => #{
+                    type => integer,
+                    description => <<"Number of sent PUBREL packet">>},
+                'packets.pubrel.missed' => #{
+                    type => integer,
+                    description => <<"Number of received PUBREC packet with unknown identifiers">>},
+                'packets.pubcomp.received' => #{
+                    type => integer,
+                    description => <<"Number of received PUBCOMP packet">>},
+                'packets.pubcomp.sent' => #{
+                    type => integer,
+                    description => <<"Number of sent PUBCOMP packet">>},
+                'packets.pubcomp.inuse' => #{
+                    type => integer,
+                    description => <<"Number of received PUBCOMP packet with occupied identifiers">>},
+                'packets.pubcomp.missed' => #{
+                    type => integer,
+                    description => <<"Number of missed PUBCOMP packet">>},
+                'packets.subscribe.received' => #{
+                    type => integer,
+                    description => <<"Number of received SUBSCRIBE packet">>},
+                'packets.subscribe.error' => #{
+                    type => integer,
+                    description => <<"Number of received SUBSCRIBE packet with failed subscriptions">>},
+                'packets.subscribe.auth_error' => #{
+                    type => integer,
+                    description => <<"Number of received SUBACK packet with failed ACL check">>},
+                'packets.suback.sent' => #{
+                    type => integer,
+                    description => <<"Number of sent SUBACK packet">>},
+                'packets.unsubscribe.received' => #{
+                    type => integer,
+                    description => <<"Number of received UNSUBSCRIBE packet">>},
+                'packets.unsubscribe.error' => #{
+                    type => integer,
+                    description => <<"Number of received UNSUBSCRIBE packet with failed unsubscriptions">>},
+                'packets.unsuback.sent' => #{
+                    type => integer,
+                    description => <<"Number of sent UNSUBACK packet">>},
+                'packets.pingreq.received' => #{
+                    type => integer,
+                    description => <<"Number of received PINGREQ packet">>},
+                'packets.pingresp.sent' => #{
+                    type => integer,
+                    description => <<"Number of sent PUBRESP packet">>},
+                'packets.disconnect.received' => #{
+                    type => integer,
+                    description => <<"Number of received DISCONNECT packet">>},
+                'packets.disconnect.sent' => #{
+                    type => integer,
+                    description => <<"Number of sent DISCONNECT packet">>},
+                'packets.auth.received' => #{
+                    type => integer,
+                    description => <<"Number of received AUTH packet">>},
+                'packets.auth.sent' => #{
+                    type => integer,
+                    description => <<"Number of sent AUTH packet">>},
+                'rules.matched' => #{
+                    type => integer,
+                    description => <<"Number of rule matched">>},
+                'session.created' => #{
+                    type => integer,
+                    description => <<"Number of sessions created">>},
+                'session.discarded' => #{
+                    type => integer,
+                    description => <<"Number of sessions dropped because Clean Session or Clean Start is true">>},
+                'session.resumed' => #{
+                    type => integer,
+                    description => <<"Number of sessions resumed because Clean Session or Clean Start is false">>},
+                'session.takeovered' => #{
+                    type => integer,
+                    description => <<"Number of sessions takeovered because Clean Session or Clean Start is false">>},
+                'session.terminated' => #{
+                    type => integer,
+                    description => <<"Number of terminated sessions">>}
+            }
+        }
+    }.
 
 metrics_api() ->
     Metadata = #{
         get => #{
             description => "EMQ X metrics",
             responses => #{
-                <<"200">> => #{
-                    schema => cowboy_swagger:schema(<<"metrics">>)}}}},
+                <<"200">> => emqx_mgmt_util:response_schema(<<"List all metrics">>, <<"metrics">>)}}},
     {"/metrics", Metadata, list}.
 
 %%%==============================================================================================

+ 71 - 80
apps/emqx_management/src/emqx_mgmt_api_nodes.erl

@@ -41,71 +41,71 @@ schemas() ->
     [node_schema()].
 
 node_schema() ->
-    DefinitionName = <<"node">>,
-    DefinitionProperties = #{
-        <<"node">> => #{
-            type => <<"string">>,
-            description => <<"Node name">>},
-        <<"connections">> => #{
-            type => <<"integer">>,
-            description => <<"Number of clients currently connected to this node">>},
-        <<"load1">> => #{
-            type => <<"string">>,
-            description => <<"CPU average load in 1 minute">>},
-        <<"load5">> => #{
-            type => <<"string">>,
-            description => <<"CPU average load in 5 minute">>},
-        <<"load15">> => #{
-            type => <<"string">>,
-            description => <<"CPU average load in 15 minute">>},
-        <<"max_fds">> => #{
-            type => <<"integer">>,
-            description => <<"Maximum file descriptor limit for the operating system">>},
-        <<"memory_total">> => #{
-            type => <<"string">>,
-            description => <<"VM allocated system memory">>},
-        <<"memory_used">> => #{
-            type => <<"string">>,
-            description => <<"VM occupied system memory">>},
-        <<"node_status">> => #{
-            type => <<"string">>,
-            description => <<"Node status">>},
-        <<"otp_release">> => #{
-            type => <<"string">>,
-            description => <<"Erlang/OTP version used by EMQ X Broker">>},
-        <<"process_available">> => #{
-            type => <<"integer">>,
-            description => <<"Number of available processes">>},
-        <<"process_used">> => #{
-            type => <<"integer">>,
-            description => <<"Number of used processes">>},
-        <<"uptime">> => #{
-            type => <<"string">>,
-            description => <<"EMQ X Broker runtime">>},
-        <<"version">> => #{
-            type => <<"string">>,
-            description => <<"EMQ X Broker version">>},
-        <<"sys_path">> => #{
-            type => <<"string">>,
-            description => <<"EMQ X system file location">>},
-        <<"log_path">> => #{
-            type => <<"string">>,
-            description => <<"EMQ X log file location">>},
-        <<"config_path">> => #{
-            type => <<"string">>,
-            description => <<"EMQ X config file location">>}
-    },
-    {DefinitionName, DefinitionProperties}.
+     #{
+        node => #{
+            type => object,
+            properties => #{
+                node => #{
+                    type => string,
+                    description => <<"Node name">>},
+                connections => #{
+                    type => integer,
+                    description => <<"Number of clients currently connected to this node">>},
+                load1 => #{
+                    type => string,
+                    description => <<"CPU average load in 1 minute">>},
+                load5 => #{
+                    type => string,
+                    description => <<"CPU average load in 5 minute">>},
+                load15 => #{
+                    type => string,
+                    description => <<"CPU average load in 15 minute">>},
+                max_fds => #{
+                    type => integer,
+                    description => <<"Maximum file descriptor limit for the operating system">>},
+                memory_total => #{
+                    type => string,
+                    description => <<"VM allocated system memory">>},
+                memory_used => #{
+                    type => string,
+                    description => <<"VM occupied system memory">>},
+                node_status => #{
+                    type => string,
+                    description => <<"Node status">>},
+                otp_release => #{
+                    type => string,
+                    description => <<"Erlang/OTP version used by EMQ X Broker">>},
+                process_available => #{
+                    type => integer,
+                    description => <<"Number of available processes">>},
+                process_used => #{
+                    type => integer,
+                    description => <<"Number of used processes">>},
+                uptime => #{
+                    type => string,
+                    description => <<"EMQ X Broker runtime">>},
+                version => #{
+                    type => string,
+                    description => <<"EMQ X Broker version">>},
+                sys_path => #{
+                    type => string,
+                    description => <<"EMQ X system file location">>},
+                log_path => #{
+                    type => string,
+                    description => <<"EMQ X log file location">>},
+                config_path => #{
+                    type => string,
+                    description => <<"EMQ X config file location">>}
+            }
+        }
+    }.
 
 nodes_api() ->
     Metadata = #{
         get => #{
             description => "List EMQ X nodes",
             responses => #{
-                <<"200">> => #{description => <<"List EMQ X Nodes">>,
-                    schema => #{
-                        type => array,
-                        items => cowboy_swagger:schema(<<"node">>)}}}}},
+                <<"200">> => emqx_mgmt_util:response_array_schema(<<"List EMQ X Nodes">>, <<"node">>)}}},
     {"/nodes", Metadata, nodes}.
 
 node_api() ->
@@ -116,15 +116,12 @@ node_api() ->
                 name => node_name,
                 in => path,
                 description => "node name",
-                type => string,
+                schema => #{type => string},
                 required => true,
-                default => node()}],
+                example => node()}],
             responses => #{
-                <<"400">> =>
-                emqx_mgmt_util:not_found_schema(<<"Node error">>, [<<"SOURCE_ERROR">>]),
-                <<"200">> => #{
-                    description => <<"Get EMQ X Nodes info by name">>,
-                    schema => cowboy_swagger:schema(<<"node">>)}}}},
+                <<"400">> => emqx_mgmt_util:not_found_schema(<<"Node error">>, [<<"SOURCE_ERROR">>]),
+                <<"200">> => emqx_mgmt_util:response_schema(<<"Get EMQ X Nodes info by name">>, <<"node">>)}}},
     {"/nodes/:node_name", Metadata, node}.
 
 node_metrics_api() ->
@@ -135,15 +132,12 @@ node_metrics_api() ->
                 name => node_name,
                 in => path,
                 description => "node name",
-                type => string,
+                schema => #{type => string},
                 required => true,
-                default => node()}],
+                example => node()}],
             responses => #{
-                <<"400">> =>
-                emqx_mgmt_util:not_found_schema(<<"Node error">>, [<<"SOURCE_ERROR">>]),
-                <<"200">> => #{
-                    description => <<"Get EMQ X Node Metrics">>,
-                    schema => cowboy_swagger:schema(<<"metrics">>)}}}},
+                <<"400">> => emqx_mgmt_util:not_found_schema(<<"Node error">>, [<<"SOURCE_ERROR">>]),
+                <<"200">> => emqx_mgmt_util:response_schema(<<"Get EMQ X Node Metrics">>, <<"metrics">>)}}},
     {"/nodes/:node_name/metrics", Metadata, node_metrics}.
 
 node_stats_api() ->
@@ -154,15 +148,12 @@ node_stats_api() ->
                 name => node_name,
                 in => path,
                 description => "node name",
-                type => string,
+                schema => #{type => string},
                 required => true,
-                default => node()}],
+                example => node()}],
             responses => #{
-                <<"400">> =>
-                emqx_mgmt_util:not_found_schema(<<"Node error">>, [<<"SOURCE_ERROR">>]),
-                <<"200">> => #{
-                    description => <<"Get EMQ X Node Stats">>,
-                    schema => cowboy_swagger:schema(<<"stats">>)}}}},
+                <<"400">> => emqx_mgmt_util:not_found_schema(<<"Node error">>, [<<"SOURCE_ERROR">>]),
+                <<"200">> => emqx_mgmt_util:response_schema(<<"Get EMQ X Node Stats">>, <<"stats">>)}}},
     {"/nodes/:node_name/stats", Metadata, node_metrics}.
 
 %%%==============================================================================================

+ 45 - 47
apps/emqx_management/src/emqx_mgmt_api_publish.erl

@@ -27,85 +27,83 @@
 api_spec() ->
     {
         [publish_api(), publish_batch_api()],
-        [request_message_schema(), mqtt_message_schema()]
+        [message_schema()]
     }.
 
 publish_api() ->
     MeteData = #{
         post => #{
             description => "publish",
-            parameters => [#{
-                name => message,
-                in => body,
-                required => true,
-                schema => minirest:ref(<<"request_message">>)
-            }],
+            'requestBody' => #{
+                content => #{
+                    'application/json' => #{
+                        schema => #{
+                            type => object,
+                            properties => maps:with([id], message_properties())}}}},
             responses => #{
-                <<"200">> => #{
-                    description => <<"publish ok">>,
-                    schema => minirest:ref(<<"message">>)}}}},
+                <<"200">> => emqx_mgmt_util:response_schema(<<"publish ok">>, <<"message">>)}}},
     {"/publish", MeteData, publish}.
 
 publish_batch_api() ->
     MeteData = #{
         post => #{
             description => "publish",
-            parameters => [#{
-                name => message,
-                in => body,
-                required => true,
-                schema =>#{
-                    type => array,
-                    items => minirest:ref(<<"request_message">>)}
-            }],
+            'requestBody' => #{
+                content => #{
+                    'application/json' => #{
+                        schema => #{
+                            type => array,
+                            items => #{
+                                type => object,
+                                properties =>  maps:with([id], message_properties())}}}}},
             responses => #{
-                <<"200">> => #{
-                    description => <<"publish result">>,
-                    schema => #{
-                        type => array,
-                        items => minirest:ref(<<"message">>)}}}}},
+                <<"200">> => emqx_mgmt_util:response_array_schema(<<"publish ok">>, <<"message">>)}}},
     {"/publish_batch", MeteData, publish_batch}.
 
-request_message_schema() ->
-    {<<"request_message">>, maps:without([<<"id">>], message_def())}.
-
-mqtt_message_schema() ->
-    {<<"message">>, message_def()}.
+message_schema() ->
+    #{
+        message => #{
+            type => object,
+            properties => message_properties()
+        }
+    }.
 
-message_def() ->
+message_properties() ->
     #{
-        <<"id">> => #{
-            type => <<"string">>,
+        id => #{
+            type => string,
             description => <<"Message ID">>},
-        <<"topic">> => #{
-            type => <<"string">>,
+        topic => #{
+            type => string,
             description => <<"Topic">>},
-        <<"qos">> => #{
-            type => <<"integer">>,
+        qos => #{
+            type => integer,
             enum => [0, 1, 2],
             description => <<"Qos">>},
-        <<"payload">> => #{
-            type => <<"string">>,
+        payload => #{
+            type => string,
             description => <<"Topic">>},
-        <<"from">> => #{
-            type => <<"string">>,
+        from => #{
+            type => string,
             description => <<"Message from">>},
-        <<"flag">> => #{
+        flag => #{
             type => <<"object">>,
             description => <<"Message flag">>,
             properties => #{
-                <<"sys">> => #{
-                    type => <<"boolean">>,
+                sys => #{
+                    type => boolean,
                     default => false,
                     description => <<"System message flag, nullable, default false">>},
-                <<"dup">> => #{
-                    type => <<"boolean">>,
+                dup => #{
+                    type => boolean,
                     default => false,
                     description => <<"Dup message flag, nullable, default false">>},
-                <<"retain">> => #{
-                    type => <<"boolean">>,
+                retain => #{
+                    type => boolean,
                     default => false,
-                    description => <<"Retain message flag, nullable, default false">>}}}
+                    description => <<"Retain message flag, nullable, default false">>}
+            }
+        }
     }.
 
 publish(post, Request) ->

+ 70 - 67
apps/emqx_management/src/emqx_mgmt_api_stats.erl

@@ -22,81 +22,84 @@
 -export([list/2]).
 
 api_spec() ->
-    {stats_api(), stats_schema()}.
+    {[stats_api()], [stats_schema()]}.
 
 stats_schema() ->
-    DefinitionName = <<"stats">>,
-    DefinitionProperties = #{
-        <<"connections.count">> => #{
-            type => <<"integer">>,
-            description => <<"Number of current connections">>},
-        <<"connections.max">> => #{
-            type => <<"integer">>,
-            description => <<"Historical maximum number of connections">>},
-        <<"channels.count">> => #{
-            type => <<"integer">>,
-            description => <<"sessions.count">>},
-        <<"channels.max">> => #{
-            type => <<"integer">>,
-            description => <<"session.max">>},
-        <<"sessions.count">> => #{
-            type => <<"integer">>,
-            description => <<"Number of current sessions">>},
-        <<"sessions.max">> => #{
-            type => <<"integer">>,
-            description => <<"Historical maximum number of sessions">>},
-        <<"topics.count">> => #{
-            type => <<"integer">>,
-            description => <<"Number of current topics">>},
-        <<"topics.max">> => #{
-            type => <<"integer">>,
-            description => <<"Historical maximum number of topics">>},
-        <<"suboptions.count">> => #{
-            type => <<"integer">>,
-            description => <<"subscriptions.count">>},
-        <<"suboptions.max">> => #{
-            type => <<"integer">>,
-            description => <<"subscriptions.max">>},
-        <<"subscribers.count">> => #{
-            type => <<"integer">>,
-            description => <<"Number of current subscribers">>},
-        <<"subscribers.max">> => #{
-            type => <<"integer">>,
-            description => <<"Historical maximum number of subscribers">>},
-        <<"subscriptions.count">> => #{
-            type => <<"integer">>,
-            description => <<"Number of current subscriptions, including shared subscriptions">>},
-        <<"subscriptions.max">> => #{
-            type => <<"integer">>,
-            description => <<"Historical maximum number of subscriptions">>},
-        <<"subscriptions.shared.count">> => #{
-            type => <<"integer">>,
-            description => <<"Number of current shared subscriptions">>},
-        <<"subscriptions.shared.max">> => #{
-            type => <<"integer">>,
-            description => <<"Historical maximum number of shared subscriptions">>},
-        <<"routes.count">> => #{
-            type => <<"integer">>,
-            description => <<"Number of current routes">>},
-        <<"routes.max">> => #{
-            type => <<"integer">>,
-            description => <<"Historical maximum number of routes">>},
-        <<"retained.count">> => #{
-            type => <<"integer">>,
-            description => <<"Number of currently retained messages">>},
-        <<"retained.max">> => #{
-            type => <<"integer">>,
-            description => <<"Historical maximum number of retained messages">>}},
-    [{DefinitionName, DefinitionProperties}].
+    #{
+        stats => #{
+            type => object,
+            properties => #{
+                'connections.count' => #{
+                    type => integer,
+                    description => <<"Number of current connections">>},
+                'connections.max' => #{
+                    type => integer,
+                    description => <<"Historical maximum number of connections">>},
+                'channels.count' => #{
+                    type => integer,
+                    description => <<"sessions.count">>},
+                'channels.max' => #{
+                    type => integer,
+                    description => <<"session.max">>},
+                'sessions.count' => #{
+                    type => integer,
+                    description => <<"Number of current sessions">>},
+                'sessions.max' => #{
+                    type => integer,
+                    description => <<"Historical maximum number of sessions">>},
+                'topics.count' => #{
+                    type => integer,
+                    description => <<"Number of current topics">>},
+                'topics.max' => #{
+                    type => integer,
+                    description => <<"Historical maximum number of topics">>},
+                'suboptions.count' => #{
+                    type => integer,
+                    description => <<"subscriptions.count">>},
+                'suboptions.max' => #{
+                    type => integer,
+                    description => <<"subscriptions.max">>},
+                'subscribers.count' => #{
+                    type => integer,
+                    description => <<"Number of current subscribers">>},
+                'subscribers.max' => #{
+                    type => integer,
+                    description => <<"Historical maximum number of subscribers">>},
+                'subscriptions.count' => #{
+                    type => integer,
+                    description => <<"Number of current subscriptions, including shared subscriptions">>},
+                'subscriptions.max' => #{
+                    type => integer,
+                    description => <<"Historical maximum number of subscriptions">>},
+                'subscriptions.shared.count' => #{
+                    type => integer,
+                    description => <<"Number of current shared subscriptions">>},
+                'subscriptions.shared.max' => #{
+                    type => integer,
+                    description => <<"Historical maximum number of shared subscriptions">>},
+                'routes.count' => #{
+                    type => integer,
+                    description => <<"Number of current routes">>},
+                'routes.max' => #{
+                    type => integer,
+                    description => <<"Historical maximum number of routes">>},
+                'retained.count' => #{
+                    type => integer,
+                    description => <<"Number of currently retained messages">>},
+                'retained.max' => #{
+                    type => integer,
+                    description => <<"Historical maximum number of retained messages">>}
+            }
+        }
+    }.
 
 stats_api() ->
     Metadata = #{
         get => #{
             description => "EMQ X stats",
             responses => #{
-                <<"200">> => #{
-                    schema => cowboy_swagger:schema(<<"stats">>)}}}},
-    [{"/stats", Metadata, list}].
+                <<"200">> => emqx_mgmt_util:response_schema(<<"List stats ok">>, <<"stats">>)}}},
+    {"/stats", Metadata, list}.
 
 %%%==============================================================================================
 %% api apply

+ 9 - 7
apps/emqx_management/src/emqx_mgmt_http.erl

@@ -44,14 +44,16 @@ start_listener({Proto, Port, Options}) ->
     Authorization = {?MODULE, authorize_appid},
     RanchOptions = ranch_opts(Port, Options),
     GlobalSpec = #{
-        swagger => "2.0",
+        openapi => "3.0.0",
         info => #{title => "EMQ X API", version => "5.0.0"},
-        basePath => ?BASE_PATH,
-        securityDefinitions => #{
-            application => #{
-                type => apiKey,
-                name => "authorization",
-                in => header}}},
+        servers => [#{url => ?BASE_PATH}],
+        components => #{
+            schemas => #{},
+            securitySchemes => #{
+                application => #{
+                    type => apiKey,
+                    name => "authorization",
+                    in => header}}}},
     Minirest = #{
         protocol => Proto,
         base_path => ?BASE_PATH,

+ 59 - 22
apps/emqx_management/src/emqx_mgmt_util.erl

@@ -24,7 +24,12 @@
         , 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
         , batch_response_schema/1]).
 
@@ -84,24 +89,46 @@ urldecode(S) ->
 
 %%%==============================================================================================
 %% 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, ["RESOURCE_NOT_FOUND"]).
 
 not_found_schema(Description, Enum) ->
-    #{
-        description => Description,
-        schema => #{
-            type => object,
-            properties => #{
-                code => #{
-                    type => string,
-                    enum => Enum},
-                reason => #{
-                    type => string}}}
-    }.
-
-batch_response_schema(DefName) ->
-    #{
+    Schema = #{
+        type => object,
+        properties => #{
+            code => #{
+                type => string,
+                enum => Enum},
+            reason => #{
+                type => string}}},
+    json_content_schema(Description, Schema).
+
+batch_response_schema(DefName) when is_binary(DefName) ->
+    Schema = #{
         type => object,
         properties => #{
             success => #{
@@ -119,13 +146,23 @@ batch_response_schema(DefName) ->
                     #{
                         data => minirest:ref(DefName),
                         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) ->

+ 1 - 1
rebar.config

@@ -51,7 +51,7 @@
     , {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.10.3"}}}
     , {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
-    , {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"}}}
     , {replayq, {git, "https://github.com/emqx/replayq", {tag, "0.3.2"}}}
     , {pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {tag, "2.0.4"}}}