From cae79a05840cdf185aabf030936f0c4c20481b3b Mon Sep 17 00:00:00 2001 From: zhongwencool Date: Fri, 24 Sep 2021 21:51:53 +0800 Subject: [PATCH] feat(swagger): move public page/limit/error_code to emqx_dashboard_swagger module --- .../src/emqx_dashboard_swagger.erl | 29 +++++++++ apps/emqx_dashboard/src/emqx_swagger.erl | 61 ------------------- .../test/emqx_swagger_parameter_SUITE.erl | 38 +++++++++++- .../test/emqx_swagger_response_SUITE.erl | 41 ++++++++++++- .../test/emqx_cluster_rpc_SUITE.erl | 10 +-- apps/emqx_modules/src/emqx_delayed_api.erl | 12 ++-- 6 files changed, 114 insertions(+), 77 deletions(-) delete mode 100644 apps/emqx_dashboard/src/emqx_swagger.erl diff --git a/apps/emqx_dashboard/src/emqx_dashboard_swagger.erl b/apps/emqx_dashboard/src/emqx_dashboard_swagger.erl index 1a580f86e..75b3ab201 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard_swagger.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard_swagger.erl @@ -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}. diff --git a/apps/emqx_dashboard/src/emqx_swagger.erl b/apps/emqx_dashboard/src/emqx_swagger.erl deleted file mode 100644 index 6ff430410..000000000 --- a/apps/emqx_dashboard/src/emqx_swagger.erl +++ /dev/null @@ -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), #{})}. diff --git a/apps/emqx_dashboard/test/emqx_swagger_parameter_SUITE.erl b/apps/emqx_dashboard/test/emqx_swagger_parameter_SUITE.erl index f481b3a8b..cea0a915d 100644 --- a/apps/emqx_dashboard/test/emqx_swagger_parameter_SUITE.erl +++ b/apps/emqx_dashboard/test/emqx_swagger_parameter_SUITE.erl @@ -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, diff --git a/apps/emqx_dashboard/test/emqx_swagger_response_SUITE.erl b/apps/emqx_dashboard/test/emqx_swagger_response_SUITE.erl index 569d717ee..fd6920549 100644 --- a/apps/emqx_dashboard/test/emqx_swagger_response_SUITE.erl +++ b/apps/emqx_dashboard/test/emqx_swagger_response_SUITE.erl @@ -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), diff --git a/apps/emqx_machine/test/emqx_cluster_rpc_SUITE.erl b/apps/emqx_machine/test/emqx_cluster_rpc_SUITE.erl index d39dc3c6e..b1530afd2 100644 --- a/apps/emqx_machine/test/emqx_cluster_rpc_SUITE.erl +++ b/apps/emqx_machine/test/emqx_cluster_rpc_SUITE.erl @@ -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 diff --git a/apps/emqx_modules/src/emqx_delayed_api.erl b/apps/emqx_modules/src/emqx_delayed_api.erl index 3261c1a2c..d51579d01 100644 --- a/apps/emqx_modules/src/emqx_delayed_api.erl +++ b/apps/emqx_modules/src/emqx_delayed_api.erl @@ -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 => [