fix(emqx_mgmt): clients api query params supports epoch(milliseconds).

This commit is contained in:
Jim Moen 2021-09-16 11:01:28 +08:00 committed by JimMoen
parent 8cbec2ec6e
commit 294c1a5f69
2 changed files with 46 additions and 33 deletions

View File

@ -46,8 +46,8 @@
%% for test suite %% for test suite
-export([ unix_ts_to_rfc3339_bin/1 -export([ unix_ts_to_rfc3339_bin/1
, unix_ts_to_rfc3339_bin/2 , unix_ts_to_rfc3339_bin/2
, rfc3339_to_unix_ts_int/1 , time_string_to_unix_ts_int/1
, rfc3339_to_unix_ts_int/2 , time_string_to_unix_ts_int/2
]). ]).
@ -106,9 +106,9 @@ properties(client) ->
{clean_start, boolean, <<"Indicate whether the client is using a brand new session">>}, {clean_start, boolean, <<"Indicate whether the client is using a brand new session">>},
{clientid, string , <<"Client identifier">>}, {clientid, string , <<"Client identifier">>},
{connected, boolean, <<"Whether the client is connected">>}, {connected, boolean, <<"Whether the client is connected">>},
{connected_at, string , <<"Client connection time">>}, {connected_at, string , <<"Client connection time, rfc3339">>},
{created_at, string , <<"Session creation time">>}, {created_at, string , <<"Session creation time, rfc3339">>},
{disconnected_at, string , <<"Client offline time, This field is only valid and returned when connected is false">>}, {disconnected_at, string , <<"Client offline time, This field is only valid and returned when connected is false, rfc3339">>},
{expiry_interval, integer, <<"Session expiration interval, with the unit of second">>}, {expiry_interval, integer, <<"Session expiration interval, with the unit of second">>},
{heap_size, integer, <<"Process heap size with the unit of byte">>}, {heap_size, integer, <<"Process heap size with the unit of byte">>},
{inflight_cnt, integer, <<"Current length of inflight">>}, {inflight_cnt, integer, <<"Current length of inflight">>},
@ -238,28 +238,28 @@ clients_api() ->
name => gte_created_at, name => gte_created_at,
in => query, in => query,
required => false, required => false,
description => <<"Search client session creation time by greater than or equal method, rfc3339">>, description => <<"Search client session creation time by greater than or equal method, rfc3339 or timestamp(millisecond)">>,
schema => #{type => string} schema => #{type => string}
}, },
#{ #{
name => lte_created_at, name => lte_created_at,
in => query, in => query,
required => false, required => false,
description => <<"Search client session creation time by less than or equal method, rfc3339">>, description => <<"Search client session creation time by less than or equal method, rfc3339 or timestamp(millisecond)">>,
schema => #{type => string} schema => #{type => string}
}, },
#{ #{
name => gte_connected_at, name => gte_connected_at,
in => query, in => query,
required => false, required => false,
description => <<"Search client connection creation time by greater than or equal method, rfc3339">>, description => <<"Search client connection creation time by greater than or equal method, rfc3339 or timestamp(millisecond)">>,
schema => #{type => string} schema => #{type => string}
}, },
#{ #{
name => lte_connected_at, name => lte_connected_at,
in => query, in => query,
required => false, required => false,
description => <<"Search client connection creation time by less than or equal method, rfc3339">>, description => <<"Search client connection creation time by less than or equal method, rfc3339 or timestamp(millisecond) ">>,
schema => #{type => string} schema => #{type => string}
} }
], ],
@ -532,22 +532,25 @@ do_unsubscribe(ClientID, Topic) ->
end. end.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% QueryString Generation (rfc3339 to timestamp) %% QueryString Generation (try rfc3339 to timestamp or keep timestamp)
generate_qs(Qs) -> time_keys() ->
TimeKeys = [ <<"gte_created_at">> [ <<"gte_created_at">>
, <<"lte_created_at">> , <<"lte_created_at">>
, <<"gte_connected_at">> , <<"gte_connected_at">>
, <<"lte_connected_at">>], , <<"lte_connected_at">>].
generate_qs(Qs) ->
Fun = Fun =
fun fun (Key, NQs) ->
(Key, NQs) ->
case NQs of case NQs of
#{Key := Rfc3339Time} -> NQs#{Key => rfc3339_to_unix_ts_int(Rfc3339Time)}; %% TimeString likes "2021-01-01T00:00:00.000+08:00" (in rfc3339)
%% or "1609430400000" (in millisecond)
#{Key := TimeString} -> NQs#{Key => time_string_to_unix_ts_int(TimeString)};
#{} -> NQs #{} -> NQs
end end
end, end,
lists:foldl(Fun, Qs, TimeKeys). lists:foldl(Fun, Qs, time_keys()).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Query Functions %% Query Functions
@ -717,8 +720,13 @@ unix_ts_to_rfc3339_bin(TimeStamp) ->
unix_ts_to_rfc3339_bin(TimeStamp, Unit) when is_integer(TimeStamp) -> unix_ts_to_rfc3339_bin(TimeStamp, Unit) when is_integer(TimeStamp) ->
list_to_binary(calendar:system_time_to_rfc3339(TimeStamp, [{unit, Unit}])). list_to_binary(calendar:system_time_to_rfc3339(TimeStamp, [{unit, Unit}])).
rfc3339_to_unix_ts_int(DateTime) -> time_string_to_unix_ts_int(DateTime) ->
rfc3339_to_unix_ts_int(DateTime, millisecond). time_string_to_unix_ts_int(DateTime, millisecond).
rfc3339_to_unix_ts_int(DateTime, Unit) when is_binary(DateTime) -> time_string_to_unix_ts_int(DateTime, Unit) when is_binary(DateTime) ->
calendar:rfc3339_to_system_time(binary_to_list(DateTime), [{unit, Unit}]). try binary_to_integer(DateTime) of
TimeStamp when is_integer(TimeStamp) -> TimeStamp
catch
error:badarg ->
calendar:rfc3339_to_system_time(binary_to_list(DateTime), [{unit, Unit}])
end.

View File

@ -104,6 +104,7 @@ t_clients(_) ->
t_query_clients_with_time(_) -> t_query_clients_with_time(_) ->
process_flag(trap_exit, true), process_flag(trap_exit, true),
Username1 = <<"user1">>, Username1 = <<"user1">>,
ClientId1 = <<"client1">>, ClientId1 = <<"client1">>,
@ -122,22 +123,26 @@ t_query_clients_with_time(_) ->
%% get /clients with time(rfc3339) %% get /clients with time(rfc3339)
NowTimeStampInt = erlang:system_time(millisecond), NowTimeStampInt = erlang:system_time(millisecond),
%% Do not uri_encode `=` to `%3D` %% Do not uri_encode `=` to `%3D`
NowTimeString = emqx_http_lib:uri_encode(binary:bin_to_list(emqx_mgmt_api_clients:unix_ts_to_rfc3339_bin(NowTimeStampInt))), Rfc3339String = emqx_http_lib:uri_encode(binary:bin_to_list(emqx_mgmt_api_clients:unix_ts_to_rfc3339_bin(NowTimeStampInt))),
Parameters = [Param ++ NowTimeString TimeStampString = emqx_http_lib:uri_encode(integer_to_list(NowTimeStampInt)),
|| Param <- [ "lte_created_at="
, "lte_connected_at=" LteKeys = ["lte_created_at=", "lte_connected_at="],
, "gte_created_at=" GteKeys = ["gte_created_at=", "gte_connected_at="],
, "gte_connected_at="]], LteParamRfc3339 = [Param ++ Rfc3339String || Param <- LteKeys],
LteParamStamp = [Param ++ TimeStampString || Param <- LteKeys],
GteParamRfc3339 = [Param ++ Rfc3339String || Param <- GteKeys],
GteParamStamp = [Param ++ TimeStampString || Param <- GteKeys],
RequestResults = [emqx_mgmt_api_test_util:request_api(get, ClientsPath, Param, AuthHeader) RequestResults = [emqx_mgmt_api_test_util:request_api(get, ClientsPath, Param, AuthHeader)
|| Param <- Parameters], || Param <- LteParamRfc3339 ++ LteParamStamp ++ GteParamRfc3339 ++ GteParamStamp],
DecodedResults = [emqx_json:decode(Response, [return_maps]) DecodedResults = [emqx_json:decode(Response, [return_maps])
|| {ok, Response} <- RequestResults], || {ok, Response} <- RequestResults],
{LteResponseDecodeds, GteResponseDecodeds} = lists:split(2, DecodedResults), {LteResponseDecodeds, GteResponseDecodeds} = lists:split(4, DecodedResults),
%% EachData :: list() %% EachData :: list()
[?assert( emqx_mgmt_api_clients:rfc3339_to_unix_ts_int(CreatedAt) < NowTimeStampInt) [?assert( emqx_mgmt_api_clients:time_string_to_unix_ts_int(CreatedAt) < NowTimeStampInt)
|| #{<<"data">> := EachData} <- LteResponseDecodeds, || #{<<"data">> := EachData} <- LteResponseDecodeds,
#{<<"created_at">> := CreatedAt} <- EachData], #{<<"created_at">> := CreatedAt} <- EachData],
[?assert(emqx_mgmt_api_clients:rfc3339_to_unix_ts_int(ConnectedAt) < NowTimeStampInt) [?assert(emqx_mgmt_api_clients:time_string_to_unix_ts_int(ConnectedAt) < NowTimeStampInt)
|| #{<<"data">> := EachData} <- LteResponseDecodeds, || #{<<"data">> := EachData} <- LteResponseDecodeds,
#{<<"connected_at">> := ConnectedAt} <- EachData], #{<<"connected_at">> := ConnectedAt} <- EachData],
[?assertEqual(EachData, []) [?assertEqual(EachData, [])