diff --git a/apps/emqx_management/src/emqx_mgmt_api_clients.erl b/apps/emqx_management/src/emqx_mgmt_api_clients.erl index 45c4fdc36..16d2d99af 100644 --- a/apps/emqx_management/src/emqx_mgmt_api_clients.erl +++ b/apps/emqx_management/src/emqx_mgmt_api_clients.erl @@ -46,8 +46,8 @@ %% for test suite -export([ unix_ts_to_rfc3339_bin/1 , unix_ts_to_rfc3339_bin/2 - , rfc3339_to_unix_ts_int/1 - , rfc3339_to_unix_ts_int/2 + , time_string_to_unix_ts_int/1 + , 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">>}, {clientid, string , <<"Client identifier">>}, {connected, boolean, <<"Whether the client is connected">>}, - {connected_at, string , <<"Client connection time">>}, - {created_at, string , <<"Session creation time">>}, - {disconnected_at, string , <<"Client offline time, This field is only valid and returned when connected is false">>}, + {connected_at, string , <<"Client connection time, rfc3339">>}, + {created_at, string , <<"Session creation time, rfc3339">>}, + {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">>}, {heap_size, integer, <<"Process heap size with the unit of byte">>}, {inflight_cnt, integer, <<"Current length of inflight">>}, @@ -238,28 +238,28 @@ clients_api() -> name => gte_created_at, in => query, 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} }, #{ name => lte_created_at, in => query, 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} }, #{ name => gte_connected_at, in => query, 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} }, #{ name => lte_connected_at, in => query, 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} } ], @@ -532,22 +532,25 @@ do_unsubscribe(ClientID, Topic) -> end. %%-------------------------------------------------------------------- -%% QueryString Generation (rfc3339 to timestamp) +%% QueryString Generation (try rfc3339 to timestamp or keep timestamp) + +time_keys() -> + [ <<"gte_created_at">> + , <<"lte_created_at">> + , <<"gte_connected_at">> + , <<"lte_connected_at">>]. generate_qs(Qs) -> - TimeKeys = [ <<"gte_created_at">> - , <<"lte_created_at">> - , <<"gte_connected_at">> - , <<"lte_connected_at">>], Fun = - fun - (Key, NQs) -> + fun (Key, NQs) -> case NQs of - #{Key := Rfc3339Time} -> NQs#{Key => rfc3339_to_unix_ts_int(Rfc3339Time)}; - #{} -> NQs + %% 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 end end, - lists:foldl(Fun, Qs, TimeKeys). + lists:foldl(Fun, Qs, time_keys()). %%-------------------------------------------------------------------- %% Query Functions @@ -717,8 +720,13 @@ unix_ts_to_rfc3339_bin(TimeStamp) -> unix_ts_to_rfc3339_bin(TimeStamp, Unit) when is_integer(TimeStamp) -> list_to_binary(calendar:system_time_to_rfc3339(TimeStamp, [{unit, Unit}])). -rfc3339_to_unix_ts_int(DateTime) -> - rfc3339_to_unix_ts_int(DateTime, millisecond). +time_string_to_unix_ts_int(DateTime) -> + time_string_to_unix_ts_int(DateTime, millisecond). -rfc3339_to_unix_ts_int(DateTime, Unit) when is_binary(DateTime) -> - calendar:rfc3339_to_system_time(binary_to_list(DateTime), [{unit, Unit}]). +time_string_to_unix_ts_int(DateTime, Unit) when is_binary(DateTime) -> + 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. diff --git a/apps/emqx_management/test/emqx_mgmt_clients_api_SUITE.erl b/apps/emqx_management/test/emqx_mgmt_clients_api_SUITE.erl index 9735a7bcf..74838c91b 100644 --- a/apps/emqx_management/test/emqx_mgmt_clients_api_SUITE.erl +++ b/apps/emqx_management/test/emqx_mgmt_clients_api_SUITE.erl @@ -104,6 +104,7 @@ t_clients(_) -> t_query_clients_with_time(_) -> process_flag(trap_exit, true), + Username1 = <<"user1">>, ClientId1 = <<"client1">>, @@ -122,22 +123,26 @@ t_query_clients_with_time(_) -> %% get /clients with time(rfc3339) NowTimeStampInt = erlang:system_time(millisecond), %% 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))), - Parameters = [Param ++ NowTimeString - || Param <- [ "lte_created_at=" - , "lte_connected_at=" - , "gte_created_at=" - , "gte_connected_at="]], + Rfc3339String = emqx_http_lib:uri_encode(binary:bin_to_list(emqx_mgmt_api_clients:unix_ts_to_rfc3339_bin(NowTimeStampInt))), + TimeStampString = emqx_http_lib:uri_encode(integer_to_list(NowTimeStampInt)), + + LteKeys = ["lte_created_at=", "lte_connected_at="], + GteKeys = ["gte_created_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) - || Param <- Parameters], + || Param <- LteParamRfc3339 ++ LteParamStamp ++ GteParamRfc3339 ++ GteParamStamp], DecodedResults = [emqx_json:decode(Response, [return_maps]) || {ok, Response} <- RequestResults], - {LteResponseDecodeds, GteResponseDecodeds} = lists:split(2, DecodedResults), + {LteResponseDecodeds, GteResponseDecodeds} = lists:split(4, DecodedResults), %% 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, #{<<"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, #{<<"connected_at">> := ConnectedAt} <- EachData], [?assertEqual(EachData, [])