feat(swagger): move public page/limit/error_code to emqx_dashboard_swagger module
This commit is contained in:
parent
7e494afd98
commit
cae79a0584
|
@ -6,6 +6,11 @@
|
|||
%% API
|
||||
-export([spec/1, spec/2]).
|
||||
-export([translate_req/2]).
|
||||
-export([namespace/0, fields/1]).
|
||||
-export([error_codes/1, error_codes/2]).
|
||||
-define(MAX_ROW_LIMIT, 100).
|
||||
|
||||
%% API
|
||||
|
||||
-ifdef(TEST).
|
||||
-compile(export_all).
|
||||
|
@ -73,6 +78,30 @@ translate_req(Request, #{module := Module, path := Path, method := Method}) ->
|
|||
{400, 'BAD_REQUEST', iolist_to_binary(io_lib:format("~s : ~p", [Key, Reason]))}
|
||||
end.
|
||||
|
||||
namespace() -> "public".
|
||||
|
||||
fields(page) ->
|
||||
Desc = <<"Page number of the results to fetch.">>,
|
||||
Meta = #{in => query, desc => Desc, default => 1, example => 1},
|
||||
[{page, hoconsc:mk(integer(), Meta)}];
|
||||
fields(limit) ->
|
||||
Desc = iolist_to_binary([<<"Results per page(max ">>,
|
||||
integer_to_binary(?MAX_ROW_LIMIT), <<")">>]),
|
||||
Meta = #{in => query, desc => Desc, default => ?MAX_ROW_LIMIT, example => 50},
|
||||
[{limit, hoconsc:mk(range(1, ?MAX_ROW_LIMIT), Meta)}].
|
||||
|
||||
error_codes(Codes) ->
|
||||
error_codes(Codes, <<"Error code to troubleshoot problems.">>).
|
||||
|
||||
error_codes(Codes = [_ | _], MsgExample) ->
|
||||
[
|
||||
{code, hoconsc:mk(hoconsc:enum(Codes))},
|
||||
{message, hoconsc:mk(string(), #{
|
||||
desc => <<"Details description of the error.">>,
|
||||
example => MsgExample
|
||||
})}
|
||||
].
|
||||
|
||||
support_check_schema(#{check_schema := true}) -> ?DEFAULT_FILTER;
|
||||
support_check_schema(#{check_schema := Func})when is_function(Func, 2) -> #{filter => Func};
|
||||
support_check_schema(_) -> #{filter => undefined}.
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
-module(emqx_swagger).
|
||||
|
||||
-include_lib("typerefl/include/types.hrl").
|
||||
-import(hoconsc, [mk/2]).
|
||||
|
||||
-define(MAX_ROW_LIMIT, 100).
|
||||
|
||||
%% API
|
||||
-export([fields/1]).
|
||||
-export([error_codes/1, error_codes/2]).
|
||||
|
||||
fields(page) ->
|
||||
[{page,
|
||||
mk(integer(),
|
||||
#{
|
||||
in => query,
|
||||
desc => <<"Page number of the results to fetch.">>,
|
||||
default => 1,
|
||||
example => 1})
|
||||
}];
|
||||
fields(limit) ->
|
||||
[{limit,
|
||||
mk(range(1, ?MAX_ROW_LIMIT),
|
||||
#{
|
||||
in => query,
|
||||
desc => iolist_to_binary([<<"Results per page(max ">>,
|
||||
integer_to_binary(?MAX_ROW_LIMIT), <<")">>]),
|
||||
default => ?MAX_ROW_LIMIT,
|
||||
example => 50
|
||||
})
|
||||
}].
|
||||
|
||||
error_codes(Codes) ->
|
||||
error_codes(Codes, <<"Error code to troubleshoot problems.">>).
|
||||
|
||||
error_codes(Codes = [_ | _], MsgExample) ->
|
||||
[code(Codes), message(MsgExample)].
|
||||
|
||||
message(Example) ->
|
||||
{message, mk(string(), #{
|
||||
desc => <<"Detailed description of the error.">>,
|
||||
example => Example
|
||||
})}.
|
||||
|
||||
code(Codes) ->
|
||||
{code, mk(hoconsc:enum(Codes), #{})}.
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
%% API
|
||||
-export([paths/0, api_spec/0, schema/1, fields/1]).
|
||||
-export([t_in_path/1, t_in_query/1, t_in_mix/1, t_without_in/1, t_ref/1]).
|
||||
-export([t_in_path/1, t_in_query/1, t_in_mix/1, t_without_in/1, t_ref/1, t_public_ref/1]).
|
||||
-export([t_require/1, t_nullable/1, t_method/1, t_api_spec/1]).
|
||||
-export([t_in_path_trans/1, t_in_query_trans/1, t_in_mix_trans/1, t_ref_trans/1]).
|
||||
-export([t_in_path_trans_error/1, t_in_query_trans_error/1, t_in_mix_trans_error/1]).
|
||||
|
@ -21,7 +21,7 @@ all() -> [{group, spec}, {group, validation}].
|
|||
suite() -> [{timetrap, {minutes, 1}}].
|
||||
groups() -> [
|
||||
{spec, [parallel], [t_api_spec, t_in_path, t_ref, t_in_query, t_in_mix,
|
||||
t_without_in, t_require, t_nullable, t_method]},
|
||||
t_without_in, t_require, t_nullable, t_method, t_public_ref]},
|
||||
{validation, [parallel], [t_in_path_trans, t_ref_trans, t_in_query_trans, t_in_mix_trans,
|
||||
t_in_path_trans_error, t_in_query_trans_error, t_in_mix_trans_error]}
|
||||
].
|
||||
|
@ -56,6 +56,29 @@ t_ref(_Config) ->
|
|||
?assertEqual([{?MODULE, page, parameter}], Refs),
|
||||
ok.
|
||||
|
||||
t_public_ref(_Config) ->
|
||||
Path = "/test/in/ref/public",
|
||||
Expect = [
|
||||
#{<<"$ref">> => <<"#/components/parameters/public.page">>},
|
||||
#{<<"$ref">> => <<"#/components/parameters/public.limit">>}
|
||||
],
|
||||
{OperationId, Spec, Refs} = emqx_dashboard_swagger:parse_spec_ref(?MODULE, Path),
|
||||
?assertEqual(test, OperationId),
|
||||
Params = maps:get(parameters, maps:get(post, Spec)),
|
||||
?assertEqual(Expect, Params),
|
||||
?assertEqual([
|
||||
{emqx_dashboard_swagger, limit, parameter},
|
||||
{emqx_dashboard_swagger, page, parameter}
|
||||
], Refs),
|
||||
ExpectRefs = [
|
||||
#{<<"public.limit">> => #{description => <<"Results per page(max 100)">>, example => 50,in => query,name => limit,
|
||||
schema => #{default => 100,example => 1,maximum => 100, minimum => 1,type => integer}}},
|
||||
#{<<"public.page">> => #{description => <<"Page number of the results to fetch.">>,
|
||||
example => 1,in => query,name => page,
|
||||
schema => #{default => 1,example => 100,type => integer}}}],
|
||||
?assertEqual(ExpectRefs, emqx_dashboard_swagger:components(Refs)),
|
||||
ok.
|
||||
|
||||
t_in_mix(_Config) ->
|
||||
Expect =
|
||||
[#{description => <<"Indicates which sorts of issues to return">>,
|
||||
|
@ -253,6 +276,17 @@ schema("/test/in/ref") ->
|
|||
responses => #{200 => <<"ok">>}
|
||||
}
|
||||
};
|
||||
schema("/test/in/ref/public") ->
|
||||
#{
|
||||
operationId => test,
|
||||
post => #{
|
||||
parameters => [
|
||||
hoconsc:ref(emqx_dashboard_swagger, page),
|
||||
hoconsc:ref(emqx_dashboard_swagger, limit)
|
||||
],
|
||||
responses => #{200 => <<"ok">>}
|
||||
}
|
||||
};
|
||||
schema("/test/in/mix/:state") ->
|
||||
#{
|
||||
operationId => test,
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
-export([all/0, suite/0, groups/0]).
|
||||
-export([paths/0, api_spec/0, schema/1, fields/1]).
|
||||
-export([t_simple_binary/1, t_object/1, t_nest_object/1, t_empty/1,
|
||||
-export([t_simple_binary/1, t_object/1, t_nest_object/1, t_empty/1, t_error/1,
|
||||
t_raw_local_ref/1, t_raw_remote_ref/1, t_hocon_schema_function/1,
|
||||
t_local_ref/1, t_remote_ref/1, t_bad_ref/1, t_none_ref/1, t_nest_ref/1,
|
||||
t_ref_array_with_key/1, t_ref_array_without_key/1, t_api_spec/1]).
|
||||
|
@ -21,7 +21,7 @@ all() -> [{group, spec}].
|
|||
suite() -> [{timetrap, {minutes, 1}}].
|
||||
groups() -> [
|
||||
{spec, [parallel], [
|
||||
t_api_spec, t_simple_binary, t_object, t_nest_object,
|
||||
t_api_spec, t_simple_binary, t_object, t_nest_object, t_error,
|
||||
t_raw_local_ref, t_raw_remote_ref, t_empty, t_hocon_schema_function,
|
||||
t_local_ref, t_remote_ref, t_bad_ref, t_none_ref,
|
||||
t_ref_array_with_key, t_ref_array_without_key, t_nest_ref]}
|
||||
|
@ -48,6 +48,33 @@ t_object(_config) ->
|
|||
validate(Path, Object, ExpectRefs),
|
||||
ok.
|
||||
|
||||
t_error(_Config) ->
|
||||
Path = "/error",
|
||||
Error400 = #{<<"content">> =>
|
||||
#{<<"application/json">> => #{<<"schema">> => #{<<"type">> => object,
|
||||
<<"properties">> =>
|
||||
[
|
||||
{<<"code">>, #{enum => ['Bad1','Bad2'], type => string}},
|
||||
{<<"message">>, #{description => <<"Details description of the error.">>,
|
||||
example => <<"Bad request desc">>, type => string}}]
|
||||
}}}},
|
||||
Error404 = #{<<"content">> =>
|
||||
#{<<"application/json">> => #{<<"schema">> => #{<<"type">> => object,
|
||||
<<"properties">> =>
|
||||
[
|
||||
{<<"code">>, #{enum => ['Not-Found'], type => string}},
|
||||
{<<"message">>, #{description => <<"Details description of the error.">>,
|
||||
example => <<"Error code to troubleshoot problems.">>, type => string}}]
|
||||
}}}},
|
||||
{OperationId, Spec, Refs} = emqx_dashboard_swagger:parse_spec_ref(?MODULE, Path),
|
||||
?assertEqual(test, OperationId),
|
||||
Response = maps:get(responses, maps:get(get, Spec)),
|
||||
?assertEqual(Error400, maps:get(<<"400">>, Response)),
|
||||
?assertEqual(Error404, maps:get(<<"404">>, Response)),
|
||||
?assertEqual(#{}, maps:without([<<"400">>, <<"404">>], Response)),
|
||||
?assertEqual([], Refs),
|
||||
ok.
|
||||
|
||||
t_nest_object(_Config) ->
|
||||
Path = "/nest/object",
|
||||
Object =
|
||||
|
@ -255,7 +282,15 @@ schema("/ref/array/with/key") ->
|
|||
schema("/ref/array/without/key") ->
|
||||
to_schema(mk(hoconsc:array(hoconsc:ref(?MODULE, good_ref)), #{}));
|
||||
schema("/ref/hocon/schema/function") ->
|
||||
to_schema(mk(hoconsc:ref(emqx_swagger_remote_schema, "root"), #{})).
|
||||
to_schema(mk(hoconsc:ref(emqx_swagger_remote_schema, "root"), #{}));
|
||||
schema("/error") ->
|
||||
#{
|
||||
operationId => test,
|
||||
get => #{responses => #{
|
||||
400 => emqx_dashboard_swagger:error_codes(['Bad1', 'Bad2'], <<"Bad request desc">>),
|
||||
404 => emqx_dashboard_swagger:error_codes(['Not-Found'])
|
||||
}}
|
||||
}.
|
||||
|
||||
validate(Path, ExpectObject, ExpectRefs) ->
|
||||
{OperationId, Spec, Refs} = emqx_dashboard_swagger:parse_spec_ref(?MODULE, Path),
|
||||
|
|
|
@ -124,7 +124,7 @@ t_catch_up_status_handle_next_commit(_Config) ->
|
|||
t_commit_ok_apply_fail_on_other_node_then_recover(_Config) ->
|
||||
emqx_cluster_rpc:reset(),
|
||||
{atomic, []} = emqx_cluster_rpc:status(),
|
||||
Now = erlang:system_time(second),
|
||||
Now = erlang:system_time(millisecond),
|
||||
{M, F, A} = {?MODULE, failed_on_other_recover_after_5_second, [erlang:whereis(?NODE1), Now]},
|
||||
{ok, _, ok} = emqx_cluster_rpc:multicall(M, F, A, 1, 1000),
|
||||
{ok, _, ok} = emqx_cluster_rpc:multicall(io, format, ["test"], 1, 1000),
|
||||
|
@ -132,10 +132,10 @@ t_commit_ok_apply_fail_on_other_node_then_recover(_Config) ->
|
|||
?assertEqual([], L),
|
||||
?assertEqual({io, format, ["test"]}, maps:get(mfa, Status)),
|
||||
?assertEqual(node(), maps:get(node, Status)),
|
||||
sleep(3000),
|
||||
sleep(2300),
|
||||
{atomic, [Status1]} = emqx_cluster_rpc:status(),
|
||||
?assertEqual(Status, Status1),
|
||||
sleep(2600),
|
||||
sleep(3600),
|
||||
{atomic, NewStatus} = emqx_cluster_rpc:status(),
|
||||
?assertEqual(3, length(NewStatus)),
|
||||
Pid = self(),
|
||||
|
@ -243,11 +243,11 @@ failed_on_node_by_odd(Pid) ->
|
|||
end.
|
||||
|
||||
failed_on_other_recover_after_5_second(Pid, CreatedAt) ->
|
||||
Now = erlang:system_time(second),
|
||||
Now = erlang:system_time(millisecond),
|
||||
case Pid =:= self() of
|
||||
true -> ok;
|
||||
false ->
|
||||
case Now < CreatedAt + 5 of
|
||||
case Now < CreatedAt + 5001 of
|
||||
true -> "MFA return not ok";
|
||||
false -> ok
|
||||
end
|
||||
|
|
|
@ -68,7 +68,7 @@ schema("/mqtt/delayed") ->
|
|||
responses => #{
|
||||
200 => mk(ref(emqx_modules_schema, "delayed"),
|
||||
#{desc => <<"Enable or disable delayed successfully">>}),
|
||||
400 => emqx_swagger:error_codes([?BAD_REQUEST], <<"Max limit illegality">>)
|
||||
400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Max limit illegality">>)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -81,8 +81,8 @@ schema("/mqtt/delayed/messages/:msgid") ->
|
|||
parameters => [{msgid, mk(binary(), #{in => path, desc => <<"delay message ID">>})}],
|
||||
responses => #{
|
||||
200 => ref("message_without_payload"),
|
||||
400 => emqx_swagger:error_codes([?MESSAGE_ID_SCHEMA_ERROR], <<"Bad MsgId format">>),
|
||||
404 => emqx_swagger:error_codes([?MESSAGE_ID_NOT_FOUND], <<"MsgId not found">>)
|
||||
400 => emqx_dashboard_swagger:error_codes([?MESSAGE_ID_SCHEMA_ERROR], <<"Bad MsgId format">>),
|
||||
404 => emqx_dashboard_swagger:error_codes([?MESSAGE_ID_NOT_FOUND], <<"MsgId not found">>)
|
||||
}
|
||||
},
|
||||
delete => #{
|
||||
|
@ -91,8 +91,8 @@ schema("/mqtt/delayed/messages/:msgid") ->
|
|||
parameters => [{msgid, mk(binary(), #{in => path, desc => <<"delay message ID">>})}],
|
||||
responses => #{
|
||||
200 => <<"Delete delayed message success">>,
|
||||
400 => emqx_swagger:error_codes([?MESSAGE_ID_SCHEMA_ERROR], <<"Bad MsgId format">>),
|
||||
404 => emqx_swagger:error_codes([?MESSAGE_ID_NOT_FOUND], <<"MsgId not found">>)
|
||||
400 => emqx_dashboard_swagger:error_codes([?MESSAGE_ID_SCHEMA_ERROR], <<"Bad MsgId format">>),
|
||||
404 => emqx_dashboard_swagger:error_codes([?MESSAGE_ID_NOT_FOUND], <<"MsgId not found">>)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -102,7 +102,7 @@ schema("/mqtt/delayed/messages") ->
|
|||
get => #{
|
||||
tags => [<<"mqtt">>],
|
||||
description => <<"List delayed messages">>,
|
||||
parameters => [ref(emqx_swagger, page), ref(emqx_swagger, limit)],
|
||||
parameters => [ref(emqx_dashboard_swagger, page), ref(emqx_dashboard_swagger, limit)],
|
||||
responses => #{
|
||||
200 =>
|
||||
[
|
||||
|
|
Loading…
Reference in New Issue