refactor(api): topic_metrics api swagger spec

This commit is contained in:
JimMoen 2022-01-16 22:35:57 +08:00
parent 5411f6ff80
commit f5cfefc0a5
2 changed files with 204 additions and 104 deletions

View File

@ -71,13 +71,13 @@ schema("/telemetry/data") ->
}.
status_schema(Desc) ->
mk(ref(?MODULE, status), #{desc => Desc}).
mk(ref(?MODULE, status), #{in => body, desc => Desc}).
fields(status) ->
[ { enable
, mk( boolean()
, #{ desc => <<"Telemetry status">>
, default => false
, default => true
, example => false
})
}

View File

@ -18,121 +18,221 @@
-behaviour(minirest_api).
-import(emqx_mgmt_util, [ properties/1
, schema/1
, object_schema/1
, object_schema/2
, object_array_schema/2
, error_schema/2
]).
-include_lib("typerefl/include/types.hrl").
-export([api_spec/0]).
-import( hoconsc
, [ mk/2
, ref/1
, ref/2
, array/1
, map/2]).
-export([ topic_metrics/2
, operate_topic_metrics/2
]).
-export([ api_spec/0
, paths/0
, schema/1
, fields/1
]).
-define(ERROR_TOPIC, 'ERROR_TOPIC').
-define(EXCEED_LIMIT, 'EXCEED_LIMIT').
-define(BAD_TOPIC, 'BAD_TOPIC').
-define(BAD_REQUEST, 'BAD_REQUEST').
-define(API_TAG_MQTT, [<<"mqtt">>]).
api_spec() ->
{[
topic_metrics_api(),
operation_topic_metrics_api()
],[]}.
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
properties() ->
properties([
{topic, string},
{create_time, string, <<"Date time, rfc3339">>},
{reset_time, string, <<"Nullable. Date time, rfc3339.">>},
{metrics, object, [{'messages.dropped.count', integer},
{'messages.dropped.rate', number},
{'messages.in.count', integer},
{'messages.in.rate', number},
{'messages.out.count', integer},
{'messages.out.rate', number},
{'messages.qos0.in.count', integer},
{'messages.qos0.in.rate', number},
{'messages.qos0.out.count', integer},
{'messages.qos0.out.rate', number},
{'messages.qos1.in.count', integer},
{'messages.qos1.in.rate', number},
{'messages.qos1.out.count', integer},
{'messages.qos1.out.rate', number},
{'messages.qos2.in.count', integer},
{'messages.qos2.in.rate', number},
{'messages.qos2.out.count', integer},
{'messages.qos2.out.rate', number}]}
]).
paths() ->
[ "/mqtt/topic_metrics"
, "/mqtt/topic_metrics/:topic"
].
topic_metrics_api() ->
MetaData = #{
%% Get all nodes metrics and accumulate all of these
get => #{
description => <<"List topic metrics">>,
responses => #{
<<"200">> => object_array_schema(properties(), <<"List topic metrics">>)
}
},
put => #{
description => <<"Reset topic metrics by topic name, or all">>,
'requestBody' => object_schema(properties([
{topic, string, <<"no topic will reset all">>},
{action, string, <<"Action, default reset">>, [reset]}
])),
responses => #{
<<"200">> => schema(<<"Reset topic metrics success">>),
<<"404">> => error_schema(<<"Topic not found">>, [?ERROR_TOPIC])
}
},
post => #{
description => <<"Create topic metrics">>,
'requestBody' => object_schema(properties([{topic, string}])),
responses => #{
<<"200">> => schema(<<"Create topic metrics success">>),
<<"409">> => error_schema(<<"Topic metrics max limit">>, [?EXCEED_LIMIT]),
<<"400">> => error_schema( <<"Topic metrics already exist or bad topic">>
, [?BAD_REQUEST, ?BAD_TOPIC])
}
}
},
{"/mqtt/topic_metrics", MetaData, topic_metrics}.
operation_topic_metrics_api() ->
MetaData = #{
get => #{
description => <<"Get topic metrics">>,
parameters => [topic_param()],
responses => #{
<<"200">> => object_schema(properties(), <<"Topic metrics">>),
<<"404">> => error_schema(<<"Topic not found">>, [?ERROR_TOPIC])
}},
delete => #{
description => <<"Deregister topic metrics">>,
parameters => [topic_param()],
responses => #{
<<"204">> => schema(<<"Deregister topic metrics">>),
<<"404">> => error_schema(<<"Topic not found">>, [?ERROR_TOPIC])
schema("/mqtt/topic_metrics") ->
#{ 'operationId' => topic_metrics
, get =>
#{ description => <<"List topic metrics">>
, tags => ?API_TAG_MQTT
, responses =>
#{200 => mk(array(hoconsc:ref(topic_metrics)), #{ desc => <<"List topic metrics">>})}
}
}
},
{"/mqtt/topic_metrics/:topic", MetaData, operate_topic_metrics}.
, put =>
#{ description => <<"Reset topic metrics by topic name, or all">>
, tags => ?API_TAG_MQTT
, 'requestBody' => emqx_dashboard_swagger:schema_with_examples(
ref(reset),
reset_examples())
, responses =>
#{ 204 => <<"Reset topic metrics success">>
, 404 => emqx_dashboard_swagger:error_codes([?ERROR_TOPIC], <<"Topic not found">>)
}
}
, post =>
#{ description => <<"Create topic metrics">>
, tags => ?API_TAG_MQTT
, 'requestBody' => [topic(body)]
, responses =>
#{ 204 => <<"Create topic metrics success">>
, 409 => emqx_dashboard_swagger:error_codes([?EXCEED_LIMIT], <<"Topic metrics max limit">>)
, 400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST, ?BAD_TOPIC], <<"Topic metrics already exist or bad topic">>)
}
}
};
schema("/mqtt/topic_metrics/:topic") ->
#{ 'operationId' => operate_topic_metrics
, get =>
#{ description => <<"Get topic metrics">>
, tags => ?API_TAG_MQTT
, parameters => [topic(path)]
, responses =>
#{ 200 => mk(ref(topic_metrics), #{ desc => <<"Topic metrics">> })
, 404 => emqx_dashboard_swagger:error_codes([?ERROR_TOPIC], <<"Topic not found">>)
}
}
, delete =>
#{ description => <<"Remove the topic metrics">>
, tags => ?API_TAG_MQTT
, parameters => [topic(path)]
, responses =>
#{ 204 => <<"Removed topic metrics successfully">>,
404 => emqx_dashboard_swagger:error_codes([?ERROR_TOPIC], <<"Topic not found">>)
}
}
}.
topic_param() ->
#{
name => topic,
in => path,
required => true,
description => <<"Notice: Topic string url must encode">>,
schema => #{type => string}
fields(reset) ->
[ {topic
, mk( binary()
, #{ desc => <<"Topic Name. If this paramter is not present, all created topic metrics will be reset.">>
, example => <<"testtopic/1">>
, nullable => true})}
, {action
, mk( string()
, #{ desc => <<"Action Name. Only as a \"reset\"">>
, enum => [reset]
, nullable => false
, example => <<"reset">>})}
];
fields(topic_metrics) ->
[ { topic
, mk( binary()
, #{ desc => <<"MQTT Topic">>
, example => <<"testtopic/1">>
, nullable => false})},
{ create_time
, mk( emqx_schema:rfc3339_system_time()
, #{ desc => <<"Date time, rfc3339">>
, nullable => false
, example => <<"2022-01-14T21:48:47+08:00">>})},
{ reset_time
, mk( emqx_schema:rfc3339_system_time()
, #{ desc => <<"Nullable. Date time, rfc3339.">>
, nullable => true
, example => <<"2022-01-14T21:48:47+08:00">>})},
{ metrics
, mk( ref(metrics)
, #{ desc => <<"MQTT Topic Metrics">>
, nullable => false})
}
];
fields(metrics) ->
[ { 'messages.dropped.count'
, mk( integer(), #{ desc => <<"Message dropped count.">>
, example => 0})},
{ 'messages.dropped.rate'
, mk( number(), #{ desc => <<"Message dropped rate in 5s.">>
, example => 0})},
{ 'messages.in.count'
, mk( integer(), #{ desc => <<"Message received count.">>
, example => 0})},
{ 'messages.in.rate'
, mk( number(), #{ desc => <<"Message received rate in 5s">>
, example => 0})},
{ 'messages.out.count'
, mk( integer(), #{ desc => <<"Message sent count.">>
, example => 0})},
{ 'messages.out.rate'
, mk( number(), #{ desc => <<"Message sent rate in 5s.">>
, example => 0})},
{ 'messages.qos0.in.count'
, mk( integer(), #{ desc => <<"Message with QoS 0 received count.">>
, example => 0})},
{ 'messages.qos0.in.rate'
, mk( number(), #{ desc => <<"Message with QoS 0 received rate in 5s.">>
, example => 0})},
{ 'messages.qos0.out.count'
, mk( integer(), #{ desc => <<"Message with QoS 0 sent count.">>
, example => 0})},
{ 'messages.qos0.out.rate'
, mk( number(), #{ desc => <<"Message with QoS 0 sent rate in 5s.">>
, example => 0})},
{ 'messages.qos1.in.count'
, mk( integer(), #{ desc => <<"Message with QoS 1 received count.">>
, example => 0})},
{ 'messages.qos1.in.rate'
, mk( number(), #{ desc => <<"Message with QoS 1 received rate in 5s.">>
, example => 0})},
{ 'messages.qos1.out.count'
, mk( integer(), #{ desc => <<"Message with QoS 1 sent count.">>
, example => 0})},
{ 'messages.qos1.out.rate'
, mk( number(), #{ desc => <<"Message with QoS 1 sent rate in 5s.">>
, example => 0})},
{ 'messages.qos2.in.count'
, mk( integer(), #{ desc => <<"Message with QoS 2 sent count.">>
, example => 0})},
{ 'messages.qos2.in.rate'
, mk( number(), #{ desc => <<"Message with QoS 2 received rate in 5s.">>
, example => 0})},
{ 'messages.qos2.out.count'
, mk( integer(), #{ desc => <<"Message with QoS 2 sent count.">>
, example => 0})},
{ 'messages.qos2.out.rate'
, mk( number(), #{ desc => <<"Message with QoS 2 sent rate in 5s.">>
, example => 0})}
].
topic(In) ->
case In of
body ->
Desc = <<"Raw topic string">>,
Example = "testtopic/1";
path ->
Desc = <<"Notice Topic string in url path must encode">>,
Example = "testtopic%2F1"
end,
{ topic
, mk( binary(),
#{ desc => Desc
, required => true
, in => In
, example => Example
})
}.
reset_examples() ->
#{ reset_specific_one_topic_metrics =>
#{ summary => <<"reset_specific_one_topic_metrics">>
, value =>
#{ topic => "testtopic/1"
, action => "reset"
}
}
, reset_all_topic_metrics =>
#{ summary => <<"reset_all_topic_metrics">>
, value =>
#{ action => "reset"
}
}
}.
%%--------------------------------------------------------------------
%% HTTP Callbacks
%%--------------------------------------------------------------------
@ -147,19 +247,19 @@ topic_metrics(get, _) ->
topic_metrics(put, #{body := #{<<"topic">> := Topic, <<"action">> := <<"reset">>}}) ->
case reset(Topic) of
ok -> {200};
ok -> {204};
{error, Reason} -> reason2httpresp(Reason)
end;
topic_metrics(put, #{body := #{<<"action">> := <<"reset">>}}) ->
reset(),
{200};
{204};
topic_metrics(post, #{body := #{<<"topic">> := <<>>}}) ->
{400, 'BAD_REQUEST', <<"Topic can not be empty">>};
topic_metrics(post, #{body := #{<<"topic">> := Topic}}) ->
case emqx_modules_conf:add_topic_metrics(Topic) of
{ok, Topic} ->
{200};
{204};
{error, Reason} ->
reason2httpresp(Reason)
end.
@ -174,7 +274,7 @@ operate_topic_metrics(get, #{bindings := #{topic := Topic0}}) ->
operate_topic_metrics(delete, #{bindings := #{topic := Topic0}}) ->
case emqx_modules_conf:remove_topic_metrics(emqx_http_lib:uri_decode(Topic0)) of
ok -> {200};
ok -> {204};
{error, Reason} -> reason2httpresp(Reason)
end.
@ -277,8 +377,8 @@ reason2httpresp(bad_topic) ->
reason2httpresp({quota_exceeded, bad_topic}) ->
Msg = list_to_binary(
io_lib:format(
"Max topic metrics count is ~p, and topic cannot have wildcard",
[emqx_topic_metrics:max_limit()])),
"Max topic metrics count is ~p, and topic cannot have wildcard",
[emqx_topic_metrics:max_limit()])),
{400, #{code => ?BAD_REQUEST, message => Msg}};
reason2httpresp(already_existed) ->
Msg = <<"Topic already registered">>,