Fix ping not return pong (#6285)

* fix: ./bin/emqx ping return pong

* chore: waiting longer for logger flush log to disk

* fix: change swagger page's limit from 100 to 1000

* chore: type wrong

* fix: sync log to disk by logger_disk_log_h:filesync
This commit is contained in:
zhongwencool 2021-11-26 17:02:45 +08:00 committed by GitHub
parent 124ba7a071
commit f697028b70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 165 additions and 75 deletions

View File

@ -22,7 +22,7 @@
-include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-include_lib("emqx/include/emqx.hrl"). -include_lib("emqx/include/emqx.hrl").
-import(emqx_trace_handler_SUITE, [filesync/2]).
-record(emqx_trace, {name, type, filter, enable = true, start_at, end_at}). -record(emqx_trace, {name, type, filter, enable = true, start_at, end_at}).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -237,15 +237,16 @@ t_client_event(_Config) ->
emqtt:ping(Client), emqtt:ping(Client),
ok = emqtt:publish(Client, <<"/test">>, #{}, <<"1">>, [{qos, 0}]), ok = emqtt:publish(Client, <<"/test">>, #{}, <<"1">>, [{qos, 0}]),
ok = emqtt:publish(Client, <<"/test">>, #{}, <<"2">>, [{qos, 0}]), ok = emqtt:publish(Client, <<"/test">>, #{}, <<"2">>, [{qos, 0}]),
ct:sleep(200),
ok = emqx_trace:create([{<<"name">>, <<"test_topic">>}, ok = emqx_trace:create([{<<"name">>, <<"test_topic">>},
{<<"type">>, <<"topic">>}, {<<"topic">>, <<"/test">>}, {<<"start_at">>, Start}]), {<<"type">>, <<"topic">>}, {<<"topic">>, <<"/test">>}, {<<"start_at">>, Start}]),
ct:sleep(200), ok = filesync(Name, clientid),
ok = filesync(<<"test_topic">>, topic),
{ok, Bin} = file:read_file(emqx_trace:log_file(Name, Now)), {ok, Bin} = file:read_file(emqx_trace:log_file(Name, Now)),
ok = emqtt:publish(Client, <<"/test">>, #{}, <<"3">>, [{qos, 0}]), ok = emqtt:publish(Client, <<"/test">>, #{}, <<"3">>, [{qos, 0}]),
ok = emqtt:publish(Client, <<"/test">>, #{}, <<"4">>, [{qos, 0}]), ok = emqtt:publish(Client, <<"/test">>, #{}, <<"4">>, [{qos, 0}]),
ok = emqtt:disconnect(Client), ok = emqtt:disconnect(Client),
ct:sleep(200), ok = filesync(Name, clientid),
ok = filesync(<<"test_topic">>, topic),
{ok, Bin2} = file:read_file(emqx_trace:log_file(Name, Now)), {ok, Bin2} = file:read_file(emqx_trace:log_file(Name, Now)),
{ok, Bin3} = file:read_file(emqx_trace:log_file(<<"test_topic">>, Now)), {ok, Bin3} = file:read_file(emqx_trace:log_file(<<"test_topic">>, Now)),
ct:pal("Bin ~p Bin2 ~p Bin3 ~p", [byte_size(Bin), byte_size(Bin2), byte_size(Bin3)]), ct:pal("Bin ~p Bin2 ~p Bin3 ~p", [byte_size(Bin), byte_size(Bin2), byte_size(Bin3)]),
@ -301,7 +302,7 @@ t_download_log(_Config) ->
{ok, Client} = emqtt:start_link([{clean_start, true}, {clientid, ClientId}]), {ok, Client} = emqtt:start_link([{clean_start, true}, {clientid, ClientId}]),
{ok, _} = emqtt:connect(Client), {ok, _} = emqtt:connect(Client),
[begin _ = emqtt:ping(Client) end ||_ <- lists:seq(1, 5)], [begin _ = emqtt:ping(Client) end ||_ <- lists:seq(1, 5)],
ct:sleep(100), ok = filesync(Name, clientid),
{ok, ZipFile} = emqx_trace_api:download_zip_log(#{name => Name}, []), {ok, ZipFile} = emqx_trace_api:download_zip_log(#{name => Name}, []),
?assert(filelib:file_size(ZipFile) > 0), ?assert(filelib:file_size(ZipFile) > 0),
ok = emqtt:disconnect(Client), ok = emqtt:disconnect(Client),

View File

@ -62,7 +62,9 @@ t_trace_clientid(_Config) ->
emqx_trace_handler:install(clientid, <<"client4">>, bad_level, "tmp/client4.log"), emqx_trace_handler:install(clientid, <<"client4">>, bad_level, "tmp/client4.log"),
{error, {handler_not_added, {file_error, ".", eisdir}}} = {error, {handler_not_added, {file_error, ".", eisdir}}} =
emqx_trace_handler:install(clientid, <<"client5">>, debug, "."), emqx_trace_handler:install(clientid, <<"client5">>, debug, "."),
ct:sleep(100), ok = filesync(<<"client">>, clientid),
ok = filesync(<<"client2">>, clientid),
ok = filesync(<<"client3">>, clientid),
%% Verify the tracing file exits %% Verify the tracing file exits
?assert(filelib:is_regular("tmp/client.log")), ?assert(filelib:is_regular("tmp/client.log")),
@ -83,7 +85,10 @@ t_trace_clientid(_Config) ->
emqtt:connect(T), emqtt:connect(T),
emqtt:publish(T, <<"a/b/c">>, <<"hi">>), emqtt:publish(T, <<"a/b/c">>, <<"hi">>),
emqtt:ping(T), emqtt:ping(T),
ct:sleep(200),
ok = filesync(<<"client">>, clientid),
ok = filesync(<<"client2">>, clientid),
ok = filesync(<<"client3">>, clientid),
%% Verify messages are logged to "tmp/client.log" but not "tmp/client2.log". %% Verify messages are logged to "tmp/client.log" but not "tmp/client2.log".
{ok, Bin} = file:read_file("tmp/client.log"), {ok, Bin} = file:read_file("tmp/client.log"),
@ -109,7 +114,8 @@ t_trace_topic(_Config) ->
emqx_logger:set_log_level(debug), emqx_logger:set_log_level(debug),
ok = emqx_trace_handler:install(topic, <<"x/#">>, all, "tmp/topic_trace_x.log"), ok = emqx_trace_handler:install(topic, <<"x/#">>, all, "tmp/topic_trace_x.log"),
ok = emqx_trace_handler:install(topic, <<"y/#">>, all, "tmp/topic_trace_y.log"), ok = emqx_trace_handler:install(topic, <<"y/#">>, all, "tmp/topic_trace_y.log"),
ct:sleep(100), ok = filesync(<<"x/#">>, topic),
ok = filesync(<<"y/#">>, topic),
%% Verify the tracing file exits %% Verify the tracing file exits
?assert(filelib:is_regular("tmp/topic_trace_x.log")), ?assert(filelib:is_regular("tmp/topic_trace_x.log")),
@ -128,7 +134,8 @@ t_trace_topic(_Config) ->
emqtt:publish(T, <<"x/y/z">>, <<"hi2">>), emqtt:publish(T, <<"x/y/z">>, <<"hi2">>),
emqtt:subscribe(T, <<"x/y/z">>), emqtt:subscribe(T, <<"x/y/z">>),
emqtt:unsubscribe(T, <<"x/y/z">>), emqtt:unsubscribe(T, <<"x/y/z">>),
ct:sleep(200), ok = filesync(<<"x/#">>, topic),
ok = filesync(<<"y/#">>, topic),
{ok, Bin} = file:read_file("tmp/topic_trace_x.log"), {ok, Bin} = file:read_file("tmp/topic_trace_x.log"),
?assertNotEqual(nomatch, binary:match(Bin, [<<"hi1">>])), ?assertNotEqual(nomatch, binary:match(Bin, [<<"hi1">>])),
@ -152,8 +159,8 @@ t_trace_ip_address(_Config) ->
%% Start tracing %% Start tracing
ok = emqx_trace_handler:install(ip_address, "127.0.0.1", all, "tmp/ip_trace_x.log"), ok = emqx_trace_handler:install(ip_address, "127.0.0.1", all, "tmp/ip_trace_x.log"),
ok = emqx_trace_handler:install(ip_address, "192.168.1.1", all, "tmp/ip_trace_y.log"), ok = emqx_trace_handler:install(ip_address, "192.168.1.1", all, "tmp/ip_trace_y.log"),
ct:sleep(100), ok = filesync(<<"127.0.0.1">>, ip_address),
ok = filesync(<<"192.168.1.1">>, ip_address),
%% Verify the tracing file exits %% Verify the tracing file exits
?assert(filelib:is_regular("tmp/ip_trace_x.log")), ?assert(filelib:is_regular("tmp/ip_trace_x.log")),
?assert(filelib:is_regular("tmp/ip_trace_y.log")), ?assert(filelib:is_regular("tmp/ip_trace_y.log")),
@ -173,7 +180,8 @@ t_trace_ip_address(_Config) ->
emqtt:publish(T, <<"x/y/z">>, <<"hi2">>), emqtt:publish(T, <<"x/y/z">>, <<"hi2">>),
emqtt:subscribe(T, <<"x/y/z">>), emqtt:subscribe(T, <<"x/y/z">>),
emqtt:unsubscribe(T, <<"x/y/z">>), emqtt:unsubscribe(T, <<"x/y/z">>),
ct:sleep(200), ok = filesync(<<"127.0.0.1">>, ip_address),
ok = filesync(<<"192.168.1.1">>, ip_address),
{ok, Bin} = file:read_file("tmp/ip_trace_x.log"), {ok, Bin} = file:read_file("tmp/ip_trace_x.log"),
?assertNotEqual(nomatch, binary:match(Bin, [<<"hi1">>])), ?assertNotEqual(nomatch, binary:match(Bin, [<<"hi1">>])),
@ -189,3 +197,19 @@ t_trace_ip_address(_Config) ->
{error, _Reason} = emqx_trace_handler:uninstall(ip_address, <<"127.0.0.2">>), {error, _Reason} = emqx_trace_handler:uninstall(ip_address, <<"127.0.0.2">>),
emqtt:disconnect(T), emqtt:disconnect(T),
?assertEqual([], emqx_trace_handler:running()). ?assertEqual([], emqx_trace_handler:running()).
filesync(Name, Type) ->
filesync(Name, Type, 3).
%% sometime the handler process is not started yet.
filesync(_Name, _Type, 0) -> ok;
filesync(Name, Type, Retry) ->
try
Handler = binary_to_atom(<<"trace_",
(atom_to_binary(Type))/binary, "_", Name/binary>>),
ok = logger_disk_log_h:filesync(Handler)
catch E:R ->
ct:pal("Filesync error:~p ~p~n", [{Name, Type, Retry}, {E, R}]),
ct:sleep(100),
filesync(Name, Type, Retry - 1)
end.

View File

@ -21,6 +21,7 @@
-include_lib("typerefl/include/types.hrl"). -include_lib("typerefl/include/types.hrl").
-include("emqx_authn.hrl"). -include("emqx_authn.hrl").
-include_lib("emqx/include/emqx_placeholder.hrl"). -include_lib("emqx/include/emqx_placeholder.hrl").
-include_lib("emqx/include/logger.hrl").
-import(hoconsc, [mk/2, ref/1]). -import(hoconsc, [mk/2, ref/1]).
-import(emqx_dashboard_swagger, [error_codes/2]). -import(emqx_dashboard_swagger, [error_codes/2]).
@ -590,7 +591,7 @@ listener_authenticator(delete,
authenticator_move(post, authenticator_move(post,
#{bindings := #{id := AuthenticatorID}, #{bindings := #{id := AuthenticatorID},
body := #{<<"position">> := Position}}) -> body := #{<<"position">> := Position}}) ->
move_authenitcator([authentication], ?GLOBAL, AuthenticatorID, Position); move_authenticator([authentication], ?GLOBAL, AuthenticatorID, Position);
authenticator_move(post, #{bindings := #{id := _}, body := _}) -> authenticator_move(post, #{bindings := #{id := _}, body := _}) ->
serialize_error({missing_parameter, position}). serialize_error({missing_parameter, position}).
@ -599,7 +600,7 @@ listener_authenticator_move(post,
body := #{<<"position">> := Position}}) -> body := #{<<"position">> := Position}}) ->
with_listener(ListenerID, with_listener(ListenerID,
fun(Type, Name, ChainName) -> fun(Type, Name, ChainName) ->
move_authenitcator([listeners, Type, Name, authentication], move_authenticator([listeners, Type, Name, authentication],
ChainName, ChainName,
AuthenticatorID, AuthenticatorID,
Position) Position)
@ -771,7 +772,7 @@ delete_authenticator(ConfKeyPath, ChainName, AuthenticatorID) ->
serialize_error(Reason) serialize_error(Reason)
end. end.
move_authenitcator(ConfKeyPath, ChainName, AuthenticatorID, Position) -> move_authenticator(ConfKeyPath, ChainName, AuthenticatorID, Position) ->
case parse_position(Position) of case parse_position(Position) of
{ok, NPosition} -> {ok, NPosition} ->
case update_config( case update_config(

View File

@ -31,7 +31,8 @@
-define(TO_COMPONENTS_PARAM(_M_, _F_), iolist_to_binary([<<"#/components/parameters/">>, -define(TO_COMPONENTS_PARAM(_M_, _F_), iolist_to_binary([<<"#/components/parameters/">>,
?TO_REF(namespace(_M_), _F_)])). ?TO_REF(namespace(_M_), _F_)])).
-define(MAX_ROW_LIMIT, 100). -define(MAX_ROW_LIMIT, 1000).
-define(DEFAULT_ROW, 100).
-type(request() :: #{bindings => map(), query_string => map(), body => map()}). -type(request() :: #{bindings => map(), query_string => map(), body => map()}).
-type(request_meta() :: #{module => module(), path => string(), method => atom()}). -type(request_meta() :: #{module => module(), path => string(), method => atom()}).
@ -80,7 +81,7 @@ fields(page) ->
fields(limit) -> fields(limit) ->
Desc = iolist_to_binary([<<"Results per page(max ">>, Desc = iolist_to_binary([<<"Results per page(max ">>,
integer_to_binary(?MAX_ROW_LIMIT), <<")">>]), integer_to_binary(?MAX_ROW_LIMIT), <<")">>]),
Meta = #{in => query, desc => Desc, default => ?MAX_ROW_LIMIT, example => 50}, Meta = #{in => query, desc => Desc, default => ?DEFAULT_ROW, example => 50},
[{limit, hoconsc:mk(range(1, ?MAX_ROW_LIMIT), Meta)}]. [{limit, hoconsc:mk(range(1, ?MAX_ROW_LIMIT), Meta)}].
-spec(schema_with_example(hocon_schema:type(), term()) -> hocon_schema:field_schema_map()). -spec(schema_with_example(hocon_schema:type(), term()) -> hocon_schema:field_schema_map()).
@ -123,10 +124,10 @@ translate_req(Request, #{module := Module, path := Path, method := Method}, Chec
{Bindings, QueryStr} = check_parameters(Request, Params, Module), {Bindings, QueryStr} = check_parameters(Request, Params, Module),
NewBody = check_request_body(Request, Body, Module, CheckFun, hoconsc:is_schema(Body)), NewBody = check_request_body(Request, Body, Module, CheckFun, hoconsc:is_schema(Body)),
{ok, Request#{bindings => Bindings, query_string => QueryStr, body => NewBody}} {ok, Request#{bindings => Bindings, query_string => QueryStr, body => NewBody}}
catch throw:Error -> catch throw:{_, ValidErrors} ->
{_, [{validation_error, ValidErr}]} = Error, Msg = [io_lib:format("~ts : ~p", [Key, Reason]) ||
#{path := Key, reason := Reason} = ValidErr, {validation_error, #{path := Key, reason := Reason}} <- ValidErrors],
{400, 'BAD_REQUEST', iolist_to_binary(io_lib:format("~ts : ~p", [Key, Reason]))} {400, 'BAD_REQUEST', iolist_to_binary(string:join(Msg, ","))}
end. end.
check_and_translate(Schema, Map, Opts) -> check_and_translate(Schema, Map, Opts) ->

View File

@ -71,8 +71,10 @@ t_public_ref(_Config) ->
{emqx_dashboard_swagger, page, parameter} {emqx_dashboard_swagger, page, parameter}
], Refs), ], Refs),
ExpectRefs = [ ExpectRefs = [
#{<<"public.limit">> => #{description => <<"Results per page(max 100)">>, example => 50,in => query,name => limit, #{<<"public.limit">> => #{description => <<"Results per page(max 1000)">>,
schema => #{default => 100,example => 1,maximum => 100, minimum => 1,type => integer}}}, example => 50,in => query,name => limit,
schema => #{default => 100,example => 1,maximum => 1000,
minimum => 1,type => integer}}},
#{<<"public.page">> => #{description => <<"Page number of the results to fetch.">>, #{<<"public.page">> => #{description => <<"Page number of the results to fetch.">>,
example => 1,in => query,name => page, example => 1,in => query,name => page,
schema => #{default => 1,example => 100,type => integer}}}], schema => #{default => 1,example => 100,type => integer}}}],
@ -176,7 +178,8 @@ t_in_mix_trans(_Config) ->
Expect = {ok, Expect = {ok,
#{body => #{}, #{body => #{},
bindings => #{state => 720}, bindings => #{state => 720},
query_string => #{<<"filter">> => created,<<"is_admin">> => true, <<"per_page">> => 5,<<"timeout">> => 34}}}, query_string => #{<<"filter">> => created,<<"is_admin">> => true,
<<"per_page">> => 5,<<"timeout">> => 34}}},
?assertEqual(Expect, trans_parameters(Path, Bindings, Query)), ?assertEqual(Expect, trans_parameters(Path, Bindings, Query)),
ok. ok.
@ -268,7 +271,10 @@ schema("/test/in/:filter") ->
parameters => [ parameters => [
{filter, {filter,
mk(hoconsc:enum([assigned, created, mentioned, all]), mk(hoconsc:enum([assigned, created, mentioned, all]),
#{in => path, desc => <<"Indicates which sorts of issues to return">>, example => "all"})} #{in => path,
desc => <<"Indicates which sorts of issues to return">>,
example => "all"
})}
], ],
responses => #{200 => <<"ok">>} responses => #{200 => <<"ok">>}
} }
@ -323,9 +329,11 @@ schema("/test/in/mix/:state") ->
deprecated => true, deprecated => true,
parameters => [ parameters => [
{filter, hoconsc:mk(hoconsc:enum([assigned, created, mentioned, all]), {filter, hoconsc:mk(hoconsc:enum([assigned, created, mentioned, all]),
#{in => query, desc => <<"Indicates which sorts of issues to return">>, example => "all"})}, #{in => query, desc => <<"Indicates which sorts of issues to return">>,
example => "all"})},
{state, mk(emqx_schema:duration_s(), {state, mk(emqx_schema:duration_s(),
#{in => path, required => true, example => "12m", desc => <<"Indicates the state of the issues to return.">>})}, #{in => path, required => true, example => "12m",
desc => <<"Indicates the state of the issues to return.">>})},
{per_page, mk(range(1, 50), {per_page, mk(range(1, 50),
#{in => query, required => false, example => 10, default => 5})}, #{in => query, required => false, example => 10, default => 5})},
{is_admin, mk(boolean(), #{in => query})}, {is_admin, mk(boolean(), #{in => query})},

View File

@ -47,9 +47,14 @@ t_object(_Config) ->
#{<<"schema">> => #{<<"schema">> =>
#{required => [<<"timeout">>, <<"per_page">>], #{required => [<<"timeout">>, <<"per_page">>],
<<"properties">> =>[ <<"properties">> =>[
{<<"per_page">>, #{description => <<"good per page desc">>, example => 1, maximum => 100, minimum => 1, type => integer}}, {<<"per_page">>, #{description => <<"good per page desc">>,
{<<"timeout">>, #{default => 5, <<"oneOf">> => [#{example => <<"1h">>, type => string}, #{enum => [infinity], type => string}]}}, example => 1, maximum => 100, minimum => 1, type => integer}},
{<<"inner_ref">>, #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>}}], {<<"timeout">>, #{default => 5, <<"oneOf">> =>
[#{example => <<"1h">>, type => string},
#{enum => [infinity], type => string}]}},
{<<"inner_ref">>,
#{<<"$ref">> =>
<<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>}}],
<<"type">> => object}}}}, <<"type">> => object}}}},
responses => #{<<"200">> => #{description => <<"ok">>}}}}, responses => #{<<"200">> => #{description => <<"ok">>}}}},
Refs = [{?MODULE, good_ref}], Refs = [{?MODULE, good_ref}],
@ -63,14 +68,20 @@ t_nest_object(_Config) ->
#{<<"schema">> => #{<<"schema">> =>
#{required => [<<"timeout">>], #{required => [<<"timeout">>],
<<"properties">> => <<"properties">> =>
[{<<"per_page">>, #{description => <<"good per page desc">>, example => 1, maximum => 100, minimum => 1, type => integer}}, [{<<"per_page">>, #{description => <<"good per page desc">>,
example => 1, maximum => 100, minimum => 1, type => integer}},
{<<"timeout">>, #{default => 5, <<"oneOf">> => {<<"timeout">>, #{default => 5, <<"oneOf">> =>
[#{example => <<"1h">>, type => string}, #{enum => [infinity], type => string}]}}, [#{example => <<"1h">>, type => string},
#{enum => [infinity], type => string}]}},
{<<"nest_object">>, {<<"nest_object">>,
#{<<"properties">> => #{<<"properties">> =>
[{<<"good_nest_1">>, #{example => 100, type => integer}}, [{<<"good_nest_1">>, #{example => 100, type => integer}},
{<<"good_nest_2">>, #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>}}],<<"type">> => object}}, {<<"good_nest_2">>, #{<<"$ref">> =>
{<<"inner_ref">>, #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>}}], <<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>}}],
<<"type">> => object}},
{<<"inner_ref">>,
#{<<"$ref">> =>
<<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>}}],
<<"type">> => object}}}}, <<"type">> => object}}}},
responses => #{<<"200">> => #{description => <<"ok">>}}}}, responses => #{<<"200">> => #{description => <<"ok">>}}}},
Refs = [{?MODULE, good_ref}], Refs = [{?MODULE, good_ref}],
@ -81,7 +92,8 @@ t_local_ref(_Config) ->
Spec = #{ Spec = #{
post => #{parameters => [], post => #{parameters => [],
requestBody => #{<<"content">> => #{<<"application/json">> => requestBody => #{<<"content">> => #{<<"application/json">> =>
#{<<"schema">> => #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>}}}}, #{<<"schema">> => #{<<"$ref">> =>
<<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>}}}},
responses => #{<<"200">> => #{description => <<"ok">>}}}}, responses => #{<<"200">> => #{description => <<"ok">>}}}},
Refs = [{?MODULE, good_ref}], Refs = [{?MODULE, good_ref}],
validate("/ref/local", Spec, Refs), validate("/ref/local", Spec, Refs),
@ -91,17 +103,22 @@ t_remote_ref(_Config) ->
Spec = #{ Spec = #{
post => #{parameters => [], post => #{parameters => [],
requestBody => #{<<"content">> => #{<<"application/json">> => requestBody => #{<<"content">> => #{<<"application/json">> =>
#{<<"schema">> => #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_remote_schema.ref2">>}}}}, #{<<"schema">> => #{<<"$ref">> =>
<<"#/components/schemas/emqx_swagger_remote_schema.ref2">>}}}},
responses => #{<<"200">> => #{description => <<"ok">>}}}}, responses => #{<<"200">> => #{description => <<"ok">>}}}},
Refs = [{emqx_swagger_remote_schema, "ref2"}], Refs = [{emqx_swagger_remote_schema, "ref2"}],
{_, Components} = validate("/ref/remote", Spec, Refs), {_, Components} = validate("/ref/remote", Spec, Refs),
ExpectComponents = [ ExpectComponents = [
#{<<"emqx_swagger_remote_schema.ref2">> => #{<<"properties">> => [ #{<<"emqx_swagger_remote_schema.ref2">> => #{<<"properties">> => [
{<<"page">>, #{description => <<"good page">>,example => 1, maximum => 100,minimum => 1,type => integer}}, {<<"page">>, #{description => <<"good page">>,example => 1,
{<<"another_ref">>, #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_remote_schema.ref3">>}}], <<"type">> => object}}, maximum => 100,minimum => 1,type => integer}},
{<<"another_ref">>, #{<<"$ref">> =>
<<"#/components/schemas/emqx_swagger_remote_schema.ref3">>}}], <<"type">> => object}},
#{<<"emqx_swagger_remote_schema.ref3">> => #{<<"properties">> => [ #{<<"emqx_swagger_remote_schema.ref3">> => #{<<"properties">> => [
{<<"ip">>, #{description => <<"IP:Port">>, example => <<"127.0.0.1:80">>,type => string}}, {<<"ip">>, #{description => <<"IP:Port">>,
{<<"version">>, #{description => <<"a good version">>, example => <<"1.0.0">>,type => string}}], example => <<"127.0.0.1:80">>,type => string}},
{<<"version">>, #{description => <<"a good version">>,
example => <<"1.0.0">>,type => string}}],
<<"type">> => object}}], <<"type">> => object}}],
?assertEqual(ExpectComponents, Components), ?assertEqual(ExpectComponents, Components),
ok. ok.
@ -110,18 +127,22 @@ t_nest_ref(_Config) ->
Spec = #{ Spec = #{
post => #{parameters => [], post => #{parameters => [],
requestBody => #{<<"content">> => #{<<"application/json">> => requestBody => #{<<"content">> => #{<<"application/json">> =>
#{<<"schema">> => #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_requestBody_SUITE.nest_ref">>}}}}, #{<<"schema">> => #{<<"$ref">> =>
<<"#/components/schemas/emqx_swagger_requestBody_SUITE.nest_ref">>}}}},
responses => #{<<"200">> => #{description => <<"ok">>}}}}, responses => #{<<"200">> => #{description => <<"ok">>}}}},
Refs = [{?MODULE, nest_ref}], Refs = [{?MODULE, nest_ref}],
ExpectComponents = lists:sort([ ExpectComponents = lists:sort([
#{<<"emqx_swagger_requestBody_SUITE.nest_ref">> => #{<<"properties">> => [ #{<<"emqx_swagger_requestBody_SUITE.nest_ref">> => #{<<"properties">> => [
{<<"env">>, #{enum => [test,dev,prod],type => string}}, {<<"env">>, #{enum => [test,dev,prod],type => string}},
{<<"another_ref">>, #{description => <<"nest ref">>, <<"$ref">> => <<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>}}], {<<"another_ref">>, #{description => <<"nest ref">>,
<<"$ref">> => <<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>}}],
<<"type">> => object}}, <<"type">> => object}},
#{<<"emqx_swagger_requestBody_SUITE.good_ref">> => #{<<"properties">> => [ #{<<"emqx_swagger_requestBody_SUITE.good_ref">> => #{<<"properties">> => [
{<<"webhook-host">>, #{default => <<"127.0.0.1:80">>, example => <<"127.0.0.1:80">>,type => string}}, {<<"webhook-host">>, #{default => <<"127.0.0.1:80">>,
example => <<"127.0.0.1:80">>,type => string}},
{<<"log_dir">>, #{example => <<"var/log/emqx">>,type => string}}, {<<"log_dir">>, #{example => <<"var/log/emqx">>,type => string}},
{<<"tag">>, #{description => <<"tag">>, example => <<"binary-example">>,type => string}}], {<<"tag">>, #{description => <<"tag">>,
example => <<"binary-example">>,type => string}}],
<<"type">> => object}}]), <<"type">> => object}}]),
{_, Components} = validate("/ref/nest/ref", Spec, Refs), {_, Components} = validate("/ref/nest/ref", Spec, Refs),
?assertEqual(ExpectComponents, Components), ?assertEqual(ExpectComponents, Components),
@ -153,9 +174,14 @@ t_ref_array_with_key(_Config) ->
#{<<"schema">> => #{required => [<<"timeout">>], #{<<"schema">> => #{required => [<<"timeout">>],
<<"type">> => object, <<"properties">> => <<"type">> => object, <<"properties">> =>
[ [
{<<"per_page">>, #{description => <<"good per page desc">>, example => 1, maximum => 100, minimum => 1, type => integer}}, {<<"per_page">>, #{description => <<"good per page desc">>,
{<<"timeout">>, #{default => 5, <<"oneOf">> => [#{example => <<"1h">>, type => string}, #{enum => [infinity], type => string}]}}, example => 1, maximum => 100, minimum => 1, type => integer}},
{<<"array_refs">>, #{items => #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>}, type => array}} {<<"timeout">>, #{default => 5, <<"oneOf">> =>
[#{example => <<"1h">>, type => string},
#{enum => [infinity], type => string}]}},
{<<"array_refs">>, #{items => #{<<"$ref">> =>
<<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>},
type => array}}
]}}}}, ]}}}},
responses => #{<<"200">> => #{description => <<"ok">>}}}}, responses => #{<<"200">> => #{description => <<"ok">>}}}},
Refs = [{?MODULE, good_ref}], Refs = [{?MODULE, good_ref}],
@ -166,7 +192,8 @@ t_ref_array_without_key(_Config) ->
Spec = #{ Spec = #{
post => #{parameters => [], post => #{parameters => [],
requestBody => #{<<"content">> => #{<<"application/json">> => #{<<"schema">> => requestBody => #{<<"content">> => #{<<"application/json">> => #{<<"schema">> =>
#{items => #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>}, type => array}}}}, #{items => #{<<"$ref">> =>
<<"#/components/schemas/emqx_swagger_requestBody_SUITE.good_ref">>}, type => array}}}},
responses => #{<<"200">> => #{description => <<"ok">>}}}}, responses => #{<<"200">> => #{description => <<"ok">>}}}},
Refs = [{?MODULE, good_ref}], Refs = [{?MODULE, good_ref}],
validate("/ref/array/without/key", Spec, Refs), validate("/ref/array/without/key", Spec, Refs),
@ -190,7 +217,8 @@ t_api_spec(_Config) ->
{ok, #{body := #{<<"timeout">> := <<"infinity">>}}}, {ok, #{body := #{<<"timeout">> := <<"infinity">>}}},
trans_requestBody(Path, Body, Filter0)), trans_requestBody(Path, Body, Filter0)),
{Spec1, _} = emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true, translate_body => true}), {Spec1, _} = emqx_dashboard_swagger:spec(?MODULE,
#{check_schema => true, translate_body => true}),
Filter1 = filter(Spec1, Path), Filter1 = filter(Spec1, Path),
?assertMatch( ?assertMatch(
{ok, #{body := #{<<"timeout">> := infinity}}}, {ok, #{body := #{<<"timeout">> := infinity}}},
@ -237,7 +265,8 @@ t_object_notrans(_Config) ->
<<"tag">> => <<"god_tag">> <<"tag">> => <<"god_tag">>
} }
}, },
{ok, #{body := ActualBody}} = trans_requestBody(Path, Body, fun emqx_dashboard_swagger:filter_check_request/2), {ok, #{body := ActualBody}} = trans_requestBody(Path, Body,
fun emqx_dashboard_swagger:filter_check_request/2),
?assertEqual(Body, ActualBody), ?assertEqual(Body, ActualBody),
ok. ok.
@ -444,7 +473,8 @@ filter(ApiSpec, Path) ->
Filter. Filter.
trans_requestBody(Path, Body) -> trans_requestBody(Path, Body) ->
trans_requestBody(Path, Body, fun emqx_dashboard_swagger:filter_check_request_and_translate_body/2). trans_requestBody(Path, Body,
fun emqx_dashboard_swagger:filter_check_request_and_translate_body/2).
trans_requestBody(Path, Body, Filter) -> trans_requestBody(Path, Body, Filter) ->
Meta = #{module => ?MODULE, method => post, path => Path}, Meta = #{module => ?MODULE, method => post, path => Path},
@ -453,7 +483,8 @@ trans_requestBody(Path, Body, Filter) ->
api_spec() -> emqx_dashboard_swagger:spec(?MODULE). api_spec() -> emqx_dashboard_swagger:spec(?MODULE).
paths() -> paths() ->
["/object", "/nest/object", "/ref/local", "/ref/nest/ref", "/ref/array/with/key", "/ref/array/without/key"]. ["/object", "/nest/object", "/ref/local", "/ref/nest/ref",
"/ref/array/with/key", "/ref/array/without/key"].
schema("/object") -> schema("/object") ->
to_schema([ to_schema([

View File

@ -40,9 +40,12 @@ t_object(_config) ->
#{<<"content">> => #{<<"application/json">> => #{<<"content">> => #{<<"application/json">> =>
#{<<"schema">> => #{required => [<<"timeout">>, <<"per_page">>], #{<<"schema">> => #{required => [<<"timeout">>, <<"per_page">>],
<<"properties">> => [ <<"properties">> => [
{<<"per_page">>, #{description => <<"good per page desc">>, example => 1, maximum => 100, minimum => 1, type => integer}}, {<<"per_page">>, #{description => <<"good per page desc">>,
{<<"timeout">>, #{default => 5, <<"oneOf">> => [#{example => <<"1h">>, type => string}, #{enum => [infinity], type => string}]}}, example => 1, maximum => 100, minimum => 1, type => integer}},
{<<"inner_ref">>, #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_response_SUITE.good_ref">>}}], {<<"timeout">>, #{default => 5, <<"oneOf">> =>
[#{example => <<"1h">>, type => string}, #{enum => [infinity], type => string}]}},
{<<"inner_ref">>, #{<<"$ref">> =>
<<"#/components/schemas/emqx_swagger_response_SUITE.good_ref">>}}],
<<"type">> => object}}}}, <<"type">> => object}}}},
ExpectRefs = [{?MODULE, good_ref}], ExpectRefs = [{?MODULE, good_ref}],
validate(Path, Object, ExpectRefs), validate(Path, Object, ExpectRefs),
@ -80,14 +83,17 @@ t_nest_object(_Config) ->
Object = Object =
#{<<"content">> => #{<<"application/json">> => #{<<"schema">> => #{<<"content">> => #{<<"application/json">> => #{<<"schema">> =>
#{required => [<<"timeout">>], <<"type">> => object, <<"properties">> => [ #{required => [<<"timeout">>], <<"type">> => object, <<"properties">> => [
{<<"per_page">>, #{description => <<"good per page desc">>, example => 1, maximum => 100, minimum => 1, type => integer}}, {<<"per_page">>, #{description => <<"good per page desc">>, example => 1,
maximum => 100, minimum => 1, type => integer}},
{<<"timeout">>, #{default => 5, <<"oneOf">> => {<<"timeout">>, #{default => 5, <<"oneOf">> =>
[#{example => <<"1h">>, type => string}, #{enum => [infinity], type => string}]}}, [#{example => <<"1h">>, type => string}, #{enum => [infinity], type => string}]}},
{<<"nest_object">>, #{<<"type">> => object, <<"properties">> => [ {<<"nest_object">>, #{<<"type">> => object, <<"properties">> => [
{<<"good_nest_1">>, #{example => 100, type => integer}}, {<<"good_nest_1">>, #{example => 100, type => integer}},
{<<"good_nest_2">>, #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_response_SUITE.good_ref">>} {<<"good_nest_2">>, #{<<"$ref">> =>
<<"#/components/schemas/emqx_swagger_response_SUITE.good_ref">>}
}]}}, }]}},
{<<"inner_ref">>, #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_response_SUITE.good_ref">>}}] {<<"inner_ref">>, #{<<"$ref">> =>
<<"#/components/schemas/emqx_swagger_response_SUITE.good_ref">>}}]
}}}}, }}}},
ExpectRefs = [{?MODULE, good_ref}], ExpectRefs = [{?MODULE, good_ref}],
validate(Path, Object, ExpectRefs), validate(Path, Object, ExpectRefs),
@ -138,7 +144,8 @@ t_bad_ref(_Config) ->
Object = #{<<"content">> => #{<<"application/json">> => #{<<"schema">> => Object = #{<<"content">> => #{<<"application/json">> => #{<<"schema">> =>
#{<<"$ref">> => <<"#/components/schemas/emqx_swagger_response_SUITE.bad_ref">>}}}}, #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_response_SUITE.bad_ref">>}}}},
ExpectRefs = [{?MODULE, bad_ref}], ExpectRefs = [{?MODULE, bad_ref}],
?assertThrow({error, #{module := ?MODULE, msg := <<"Object only supports not empty proplists">>}}, ?assertThrow({error, #{module := ?MODULE,
msg := <<"Object only supports not empty proplists">>}},
validate(Path, Object, ExpectRefs)), validate(Path, Object, ExpectRefs)),
ok. ok.
@ -158,7 +165,8 @@ t_nest_ref(_Config) ->
t_complicated_type(_Config) -> t_complicated_type(_Config) ->
Path = "/ref/complicated_type", Path = "/ref/complicated_type",
Object = #{<<"content">> => #{<<"application/json">> => #{<<"schema">> => #{<<"properties">> => Object = #{<<"content">> => #{<<"application/json">> =>
#{<<"schema">> => #{<<"properties">> =>
[ [
{<<"no_neg_integer">>, #{example => 100, minimum => 1, type => integer}}, {<<"no_neg_integer">>, #{example => 100, minimum => 1, type => integer}},
{<<"url">>, #{example => <<"http://127.0.0.1">>, type => string}}, {<<"url">>, #{example => <<"http://127.0.0.1">>, type => string}},
@ -176,7 +184,8 @@ t_complicated_type(_Config) ->
{<<"comma_separated_list">>, #{example => <<"item1,item2">>, type => string}}, {<<"comma_separated_list">>, #{example => <<"item1,item2">>, type => string}},
{<<"comma_separated_atoms">>, #{example => <<"item1,item2">>, type => string}}, {<<"comma_separated_atoms">>, #{example => <<"item1,item2">>, type => string}},
{<<"log_level">>, {<<"log_level">>,
#{enum => [debug, info, notice, warning, error, critical, alert, emergency, all], type => string}}, #{enum => [debug, info, notice, warning, error, critical, alert, emergency, all],
type => string}},
{<<"fix_integer">>, #{default => 100, enum => [100], example => 100,type => integer}} {<<"fix_integer">>, #{default => 100, enum => [100], example => 100,type => integer}}
], ],
<<"type">> => object}}}}, <<"type">> => object}}}},
@ -192,15 +201,20 @@ t_ref_array_with_key(_Config) ->
Path = "/ref/array/with/key", Path = "/ref/array/with/key",
Object = #{<<"content">> => #{<<"application/json">> => #{<<"schema">> => #{ Object = #{<<"content">> => #{<<"application/json">> => #{<<"schema">> => #{
required => [<<"timeout">>], <<"type">> => object, <<"properties">> => [ required => [<<"timeout">>], <<"type">> => object, <<"properties">> => [
{<<"per_page">>, #{description => <<"good per page desc">>, example => 1, maximum => 100, minimum => 1, type => integer}}, {<<"per_page">>, #{description => <<"good per page desc">>,
example => 1, maximum => 100, minimum => 1, type => integer}},
{<<"timeout">>, #{default => 5, <<"oneOf">> => {<<"timeout">>, #{default => 5, <<"oneOf">> =>
[#{example => <<"1h">>, type => string}, #{enum => [infinity], type => string}]}}, [#{example => <<"1h">>, type => string}, #{enum => [infinity], type => string}]}},
{<<"assert">>, #{description => <<"money">>, example => 3.14159, type => number}}, {<<"assert">>, #{description => <<"money">>, example => 3.14159, type => number}},
{<<"number_ex">>, #{description => <<"number example">>, example => 42, type => number}}, {<<"number_ex">>, #{description => <<"number example">>,
{<<"percent_ex">>, #{description => <<"percent example">>, example => <<"12%">>, type => number}}, example => 42, type => number}},
{<<"duration_ms_ex">>, #{description => <<"duration ms example">>, example => <<"32s">>, type => string}}, {<<"percent_ex">>, #{description => <<"percent example">>,
example => <<"12%">>, type => number}},
{<<"duration_ms_ex">>, #{description => <<"duration ms example">>,
example => <<"32s">>, type => string}},
{<<"atom_ex">>, #{description => <<"atom ex">>, example => atom, type => string}}, {<<"atom_ex">>, #{description => <<"atom ex">>, example => atom, type => string}},
{<<"array_refs">>, #{items => #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_response_SUITE.good_ref">>}, type => array}} {<<"array_refs">>, #{items => #{<<"$ref">> =>
<<"#/components/schemas/emqx_swagger_response_SUITE.good_ref">>}, type => array}}
]} ]}
}}}, }}},
ExpectRefs = [{?MODULE, good_ref}], ExpectRefs = [{?MODULE, good_ref}],
@ -227,25 +241,34 @@ t_hocon_schema_function(_Config) ->
}}, }},
#{<<"emqx_swagger_remote_schema.ref2">> => #{<<"type">> => object, #{<<"emqx_swagger_remote_schema.ref2">> => #{<<"type">> => object,
<<"properties">> => [ <<"properties">> => [
{<<"page">>, #{description => <<"good page">>, example => 1, maximum => 100, minimum => 1, type => integer}}, {<<"page">>, #{description => <<"good page">>,
{<<"another_ref">>, #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_remote_schema.ref3">>}} example => 1, maximum => 100, minimum => 1, type => integer}},
{<<"another_ref">>, #{<<"$ref">> =>
<<"#/components/schemas/emqx_swagger_remote_schema.ref3">>}}
] ]
}}, }},
#{<<"emqx_swagger_remote_schema.ref3">> => #{<<"type">> => object, #{<<"emqx_swagger_remote_schema.ref3">> => #{<<"type">> => object,
<<"properties">> => [ <<"properties">> => [
{<<"ip">>, #{description => <<"IP:Port">>, example => <<"127.0.0.1:80">>, type => string}}, {<<"ip">>, #{description => <<"IP:Port">>,
{<<"version">>, #{description => <<"a good version">>, example => <<"1.0.0">>, type => string}}] example => <<"127.0.0.1:80">>, type => string}},
{<<"version">>, #{description => <<"a good version">>,
example => <<"1.0.0">>, type => string}}]
}}, }},
#{<<"emqx_swagger_remote_schema.root">> => #{required => [<<"default_password">>, <<"default_username">>], #{<<"emqx_swagger_remote_schema.root">> =>
#{required => [<<"default_password">>, <<"default_username">>],
<<"properties">> => [{<<"listeners">>, #{items => <<"properties">> => [{<<"listeners">>, #{items =>
#{<<"oneOf">> => #{<<"oneOf">> =>
[#{<<"$ref">> => <<"#/components/schemas/emqx_swagger_remote_schema.ref2">>}, [#{<<"$ref">> => <<"#/components/schemas/emqx_swagger_remote_schema.ref2">>},
#{<<"$ref">> => <<"#/components/schemas/emqx_swagger_remote_schema.ref1">>}]}, type => array}}, #{<<"$ref">> => <<"#/components/schemas/emqx_swagger_remote_schema.ref1">>}]},
type => array}},
{<<"default_username">>, {<<"default_username">>,
#{default => <<"admin">>, example => <<"string-example">>, type => string}}, #{default => <<"admin">>, example => <<"string-example">>, type => string}},
{<<"default_password">>, #{default => <<"public">>, example => <<"string-example">>, type => string}}, {<<"default_password">>,
{<<"sample_interval">>, #{default => <<"10s">>, example => <<"1h">>, type => string}}, #{default => <<"public">>, example => <<"string-example">>, type => string}},
{<<"token_expired_time">>, #{default => <<"30m">>, example => <<"12m">>, type => string}}], {<<"sample_interval">>,
#{default => <<"10s">>, example => <<"1h">>, type => string}},
{<<"token_expired_time">>,
#{default => <<"30m">>, example => <<"12m">>, type => string}}],
<<"type">> => object}}], <<"type">> => object}}],
ExpectRefs = [{emqx_swagger_remote_schema, "root"}], ExpectRefs = [{emqx_swagger_remote_schema, "root"}],
{_, Components} = validate(Path, Object, ExpectRefs), {_, Components} = validate(Path, Object, ExpectRefs),

View File

@ -587,6 +587,7 @@ case "${COMMAND}" in
ping) ping)
assert_node_alive assert_node_alive
echo pong
;; ;;
escript) escript)