diff --git a/big_tests/default.spec b/big_tests/default.spec index 6953766883d..89904764254 100644 --- a/big_tests/default.spec +++ b/big_tests/default.spec @@ -28,6 +28,7 @@ {suites, "tests", graphql_SUITE}. {suites, "tests", graphql_account_SUITE}. {suites, "tests", graphql_domain_SUITE}. +{suites, "tests", graphql_inbox_SUITE}. {suites, "tests", graphql_last_SUITE}. {suites, "tests", graphql_muc_SUITE}. {suites, "tests", graphql_muc_light_SUITE}. diff --git a/big_tests/dynamic_domains.spec b/big_tests/dynamic_domains.spec index 2dafcdb6c8c..7ddd86063af 100644 --- a/big_tests/dynamic_domains.spec +++ b/big_tests/dynamic_domains.spec @@ -44,6 +44,7 @@ {suites, "tests", graphql_SUITE}. {suites, "tests", graphql_account_SUITE}. {suites, "tests", graphql_domain_SUITE}. +{suites, "tests", graphql_inbox_SUITE}. {suites, "tests", graphql_last_SUITE}. {suites, "tests", graphql_muc_SUITE}. {suites, "tests", graphql_muc_light_SUITE}. diff --git a/big_tests/tests/graphql_inbox_SUITE.erl b/big_tests/tests/graphql_inbox_SUITE.erl new file mode 100644 index 00000000000..cc9ab1c5c0d --- /dev/null +++ b/big_tests/tests/graphql_inbox_SUITE.erl @@ -0,0 +1,206 @@ +-module(graphql_inbox_SUITE). + +-compile([export_all, nowarn_export_all]). + +-import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4]). +-import(graphql_helper, [execute_user/3, execute_auth/2, user_to_bin/1, user_to_jid/1, + get_ok_value/2, get_err_msg/1, get_err_code/1]). + +-include_lib("eunit/include/eunit.hrl"). +-include("inbox.hrl"). + +-define(assertErrMsg(Res, ContainsPart), assert_err_msg(ContainsPart, Res)). +-define(assertErrCode(Res, Code), assert_err_code(Code, Res)). + +suite() -> + require_rpc_nodes([mim]) ++ escalus:suite(). + +all() -> + inbox_helper:skip_or_run_inbox_tests(tests()). + +tests() -> + [{group, user_inbox}, + {group, admin_inbox}]. + +groups() -> + [{user_inbox, [], user_inbox_handler()}, + {admin_inbox, [], admin_inbox_handler()}]. + +user_inbox_handler() -> + [user_flush_own_bin]. + +admin_inbox_handler() -> + [admin_flush_user_bin, + admin_try_flush_nonexistent_user_bin, + admin_flush_domain_bin, + admin_try_flush_nonexistent_domain_bin, + admin_flush_global_bin, + admin_flush_global_bin_after_days, + admin_try_flush_nonexistent_host_type_bin]. + +init_per_suite(Config) -> + HostType = domain_helper:host_type(), + SecHostType = domain_helper:secondary_host_type(), + Config1 = dynamic_modules:save_modules([HostType, SecHostType], Config), + Modules = [{mod_inbox, inbox_helper:inbox_opts(async_pools)} | inbox_helper:muclight_modules()], + ok = dynamic_modules:ensure_modules(HostType, Modules), + ok = dynamic_modules:ensure_modules(SecHostType, Modules), + escalus:init_per_suite(Config1). + +end_per_suite(Config) -> + dynamic_modules:restore_modules(Config), + escalus:end_per_suite(Config). + +init_per_group(admin_inbox, Config) -> + graphql_helper:init_admin_handler(Config); +init_per_group(user_inbox, Config) -> + [{schema_endpoint, user} | Config]. + +end_per_group(_, _) -> + ok. + +init_per_testcase(CaseName, Config) -> + escalus:init_per_testcase(CaseName, Config). + +end_per_testcase(CaseName, Config) -> + %% Clean users after each test case to keep inbox empty + escalus_fresh:clean(), + escalus:end_per_testcase(CaseName, Config). + +%% Admin test cases + +admin_flush_user_bin(Config) -> + escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}, {kate, 1}], + fun admin_flush_user_bin/4). + +admin_flush_user_bin(Config, Alice, Bob, Kate) -> + RoomBinJID = create_room_and_make_users_leave(Alice, Bob, Kate), + Res = execute_auth(admin_flush_user_bin_body(Bob, null), Config), + NumOfRows = get_ok_value(p(flushUserBin), Res), + ?assertEqual(1, NumOfRows), + inbox_helper:check_inbox(Bob, [], #{box => bin}), + check_aff_msg_in_inbox_bin(Kate, RoomBinJID). + +admin_flush_domain_bin(Config) -> + escalus:fresh_story_with_config(Config, [{alice, 1}, {alice_bis, 1}, {kate, 1}], + fun admin_flush_domain_bin/4). + +admin_flush_domain_bin(Config, Alice, AliceBis, Kate) -> + RoomBinJID = create_room_and_make_users_leave(Alice, AliceBis, Kate), + Domain = domain_helper:domain(), + Res = execute_auth(admin_flush_domain_bin_body(Domain, null), Config), + NumOfRows = get_ok_value(p(flushDomainBin), Res), + ?assertEqual(1, NumOfRows), + inbox_helper:check_inbox(Kate, [], #{box => bin}), + check_aff_msg_in_inbox_bin(AliceBis, RoomBinJID). + +admin_flush_global_bin(Config) -> + escalus:fresh_story_with_config(Config, [{alice, 1}, {alice_bis, 1}, {kate, 1}], + fun admin_flush_global_bin/4). + +admin_flush_global_bin(Config, Alice, AliceBis, Kate) -> + SecHostType = domain_helper:host_type(), + create_room_and_make_users_leave(Alice, AliceBis, Kate), + Res = execute_auth(admin_flush_global_bin_body(SecHostType, null), Config), + NumOfRows = get_ok_value(p(flushGlobalBin), Res), + ?assertEqual(2, NumOfRows), + inbox_helper:check_inbox(AliceBis, [], #{box => bin}), + inbox_helper:check_inbox(Kate, [], #{box => bin}). + +admin_flush_global_bin_after_days(Config) -> + escalus:fresh_story_with_config(Config, [{alice, 1}, {alice_bis, 1}, {kate, 1}], + fun admin_flush_global_bin_after_days/4). + +admin_flush_global_bin_after_days(Config, Alice, AliceBis, Kate) -> + SecHostType = domain_helper:host_type(), + RoomBinJID = create_room_and_make_users_leave(Alice, AliceBis, Kate), + Res = execute_auth(admin_flush_global_bin_body(SecHostType, 1), Config), + NumOfRows = get_ok_value(p(flushGlobalBin), Res), + ?assertEqual(0, NumOfRows), + check_aff_msg_in_inbox_bin(AliceBis, RoomBinJID), + check_aff_msg_in_inbox_bin(Kate, RoomBinJID). + +admin_try_flush_nonexistent_user_bin(Config) -> + %% Check nonexistent domain error + Res = execute_auth(admin_flush_user_bin_body(<<"user@user.com">>, null), Config), + ?assertErrMsg(Res, <<"not found">>), + ?assertErrCode(Res, domain_not_found), + %% Check nonexistent user error + User = <<"nonexistent-user@", (domain_helper:domain())/binary>>, + Res2 = execute_auth(admin_flush_user_bin_body(User, null), Config), + ?assertErrMsg(Res2, <<"does not exist">>), + ?assertErrCode(Res2, user_does_not_exist). + +admin_try_flush_nonexistent_domain_bin(Config) -> + Res = execute_auth(admin_flush_domain_bin_body(<<"unknown-domain">>, null), Config), + ?assertErrMsg(Res, <<"not found">>), + ?assertErrCode(Res, domain_not_found). + +admin_try_flush_nonexistent_host_type_bin(Config) -> + Res = execute_auth(admin_flush_global_bin_body(<<"nonexistent host type">>, null), Config), + ?assertErrMsg(Res, <<"not found">>), + ?assertErrCode(Res, host_type_not_found). + +%% User test cases + +user_flush_own_bin(Config) -> + escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}, {kate, 1}], + fun user_flush_own_bin/4). + +user_flush_own_bin(Config, Alice, Bob, Kate) -> + create_room_and_make_users_leave(Alice, Bob, Kate), + Res = execute_user(user_flush_own_bin_body(null), Bob, Config), + NumOfRows = get_ok_value(p(flushBin), Res), + ?assertEqual(1, NumOfRows), + inbox_helper:check_inbox(Bob, [], #{box => bin}). + +%% Helpers + +create_room_and_make_users_leave(Alice, Bob, Kate) -> + RoomName = inbox_SUITE:create_room_and_make_users_leave(Alice, Bob, Kate), + muc_light_helper:room_bin_jid(RoomName). + +check_aff_msg_in_inbox_bin(User, RoomBinJID) -> + UserShort = escalus_client:short_jid(User), + Convs = [#conv{unread = 1, from = RoomBinJID, to = UserShort, content = <<>>}], + inbox_helper:check_inbox(User, Convs, #{box => bin}). + +assert_err_msg(Contains, Res) -> + ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), Contains)). + +assert_err_code(Code, Res) -> + ?assertEqual(atom_to_binary(Code), get_err_code(Res)). + +p(Cmd) when is_atom(Cmd) -> + [data, inbox, Cmd]; +p(Path) when is_list(Path) -> + [data, inbox] ++ Path. + +%% Request bodies + +admin_flush_user_bin_body(User, Days) -> + Query = <<"mutation M1($user: JID!, $days: PosInt) + { inbox { flushUserBin(user: $user, days: $days) } }">>, + OpName = <<"M1">>, + Vars = #{user => user_to_bin(User), days => Days}, + #{query => Query, operationName => OpName, variables => Vars}. + +admin_flush_domain_bin_body(Domain, Days) -> + Query = <<"mutation M1($domain: String!, $days: PosInt) + { inbox { flushDomainBin(domain: $domain, days: $days) } }">>, + OpName = <<"M1">>, + Vars = #{domain => Domain, days => Days}, + #{query => Query, operationName => OpName, variables => Vars}. + +admin_flush_global_bin_body(HostType, Days) -> + Query = <<"mutation M1($hostType: String!, $days: PosInt) + { inbox { flushGlobalBin(hostType: $hostType, days: $days) } }">>, + OpName = <<"M1">>, + Vars = #{hostType => HostType, days => Days}, + #{query => Query, operationName => OpName, variables => Vars}. + +user_flush_own_bin_body(Days) -> + Query = <<"mutation M1($days: PosInt) { inbox { flushBin(days: $days) } }">>, + OpName = <<"M1">>, + Vars = #{days => Days}, + #{query => Query, operationName => OpName, variables => Vars}. diff --git a/priv/graphql/schemas/admin/admin_schema.gql b/priv/graphql/schemas/admin/admin_schema.gql index 702021013b0..3822f75c71f 100644 --- a/priv/graphql/schemas/admin/admin_schema.gql +++ b/priv/graphql/schemas/admin/admin_schema.gql @@ -43,6 +43,8 @@ type AdminMutation @protected{ account: AccountAdminMutation "Domain management" domains: DomainAdminMutation + "Inbox bin management" + inbox: InboxAdminMutation "Last activity management" last: LastAdminMutation "MUC room management" diff --git a/priv/graphql/schemas/admin/inbox.gql b/priv/graphql/schemas/admin/inbox.gql new file mode 100644 index 00000000000..fdc6c4aad5f --- /dev/null +++ b/priv/graphql/schemas/admin/inbox.gql @@ -0,0 +1,28 @@ +""" +Allow admin to flush the inbox bin". +""" +type InboxAdminMutation @protected{ + "Flush the user's bin and return the number of deleted rows" + flushUserBin( + "User to clear a bin" + user: JID!, + "Remove older than given days or all if null" + days: PosInt + ): Int @protected(type: DOMAIN, args: ["user"]) + + "Flush the whole domain bin and return the number of deleted rows" + flushDomainBin( + "Domain to be cleared" + domain: String!, + "Remove older than given days or all if null" + days: PosInt + ): Int @protected(type: Domain, args: ["domain"]) + + "Flush the global bin and return the number of deleted rows" + flushGlobalBin( + "Required to identify the DB backend" + hostType: String!, + "Remove older than given days or all if null" + days: PosInt + ): Int @protected(type: GLOBAL) +} diff --git a/priv/graphql/schemas/global/scalar_types.gql b/priv/graphql/schemas/global/scalar_types.gql index 65af7664a6b..4ee27eb6510 100644 --- a/priv/graphql/schemas/global/scalar_types.gql +++ b/priv/graphql/schemas/global/scalar_types.gql @@ -4,3 +4,4 @@ scalar JID "The JID with resource e.g. alice@localhost/res1" scalar FullJID scalar NonEmptyString +scalar PosInt diff --git a/priv/graphql/schemas/user/inbox.gql b/priv/graphql/schemas/user/inbox.gql new file mode 100644 index 00000000000..722180d5d4b --- /dev/null +++ b/priv/graphql/schemas/user/inbox.gql @@ -0,0 +1,10 @@ +""" +Allow user to flush own inbox bin". +""" +type InboxUserMutation @protected{ + "Flush the user's bin and return the number of deleted rows" + flushBin( + "Remove older than given days or all if null" + days: PosInt + ): Int +} diff --git a/priv/graphql/schemas/user/user_schema.gql b/priv/graphql/schemas/user/user_schema.gql index 320fc819332..4593ff72dfe 100644 --- a/priv/graphql/schemas/user/user_schema.gql +++ b/priv/graphql/schemas/user/user_schema.gql @@ -37,6 +37,8 @@ Only an authenticated user can execute these mutations. type UserMutation @protected{ "Account management" account: AccountUserMutation + "Inbox bin management" + inbox: InboxUserMutation "Last activity management" last: LastUserMutation "MUC room management" diff --git a/src/graphql/admin/mongoose_graphql_admin_mutation.erl b/src/graphql/admin/mongoose_graphql_admin_mutation.erl index a76e9169cd8..7288be16466 100644 --- a/src/graphql/admin/mongoose_graphql_admin_mutation.erl +++ b/src/graphql/admin/mongoose_graphql_admin_mutation.erl @@ -5,18 +5,18 @@ -ignore_xref([execute/4]). --include("../mongoose_graphql_types.hrl"). - execute(_Ctx, _Obj, <<"account">>, _Args) -> {ok, account}; execute(_Ctx, _Obj, <<"domains">>, _Args) -> {ok, admin}; +execute(_Ctx, _Obj, <<"httpUpload">>, _Args) -> + {ok, httpUpload}; +execute(_Ctx, _Obj, <<"inbox">>, _Args) -> + {ok, inbox}; execute(_Ctx, _Obj, <<"last">>, _Args) -> {ok, last}; execute(_Ctx, _Obj, <<"muc">>, _Args) -> {ok, muc}; -execute(_Ctx, _Obj, <<"httpUpload">>, _Args) -> - {ok, httpUpload}; execute(_Ctx, _Obj, <<"muc_light">>, _Args) -> {ok, muc_light}; execute(_Ctx, _Obj, <<"offline">>, _Args) -> diff --git a/src/graphql/admin/mongoose_graphql_inbox_admin_mutation.erl b/src/graphql/admin/mongoose_graphql_inbox_admin_mutation.erl new file mode 100644 index 00000000000..275ed56a921 --- /dev/null +++ b/src/graphql/admin/mongoose_graphql_inbox_admin_mutation.erl @@ -0,0 +1,19 @@ +-module(mongoose_graphql_inbox_admin_mutation). + +-behaviour(mongoose_graphql). + +-export([execute/4]). + +-import(mongoose_graphql_helper, [format_result/2, null_to_default/2]). + +-ignore_xref([execute/4]). + +execute(_Ctx, inbox, <<"flushUserBin">>, #{<<"user">> := UserJID, <<"days">> := Days}) -> + Res = mod_inbox_api:flush_user_bin(UserJID, null_to_default(Days, 0)), + format_result(Res, #{user => jid:to_binary(UserJID)}); +execute(_Ctx, inbox, <<"flushDomainBin">>, #{<<"domain">> := Domain, <<"days">> := Days}) -> + Res = mod_inbox_api:flush_domain_bin(Domain, null_to_default(Days, 0)), + format_result(Res, #{domain => Domain}); +execute(_Ctx, inbox, <<"flushGlobalBin">>, #{<<"hostType">> := HostType, <<"days">> := Days}) -> + Res = mod_inbox_api:flush_global_bin(HostType, null_to_default(Days, 0)), + format_result(Res, #{host_type => HostType}). diff --git a/src/graphql/mongoose_graphql.erl b/src/graphql/mongoose_graphql.erl index 84a04c26e1c..3a61a3e4a27 100644 --- a/src/graphql/mongoose_graphql.erl +++ b/src/graphql/mongoose_graphql.erl @@ -132,6 +132,7 @@ admin_mapping_rules() -> 'DomainAdminQuery' => mongoose_graphql_domain_admin_query, 'AdminMutation' => mongoose_graphql_admin_mutation, 'DomainAdminMutation' => mongoose_graphql_domain_admin_mutation, + 'InboxAdminMutation' => mongoose_graphql_inbox_admin_mutation, 'SessionAdminMutation' => mongoose_graphql_session_admin_mutation, 'SessionAdminQuery' => mongoose_graphql_session_admin_query, 'StanzaAdminMutation' => mongoose_graphql_stanza_admin_mutation, @@ -166,6 +167,7 @@ user_mapping_rules() -> 'UserMutation' => mongoose_graphql_user_mutation, 'AccountUserQuery' => mongoose_graphql_account_user_query, 'AccountUserMutation' => mongoose_graphql_account_user_mutation, + 'InboxUserMutation' => mongoose_graphql_inbox_user_mutation, 'MUCUserMutation' => mongoose_graphql_muc_user_mutation, 'MUCUserQuery' => mongoose_graphql_muc_user_query, 'MUCLightUserMutation' => mongoose_graphql_muc_light_user_mutation, diff --git a/src/graphql/mongoose_graphql_scalar.erl b/src/graphql/mongoose_graphql_scalar.erl index f33a5af5b63..bd93d5321ea 100644 --- a/src/graphql/mongoose_graphql_scalar.erl +++ b/src/graphql/mongoose_graphql_scalar.erl @@ -15,6 +15,7 @@ input(<<"Stanza">>, Value) -> exml:parse(Value); input(<<"JID">>, Jid) -> jid_from_binary(Jid); input(<<"FullJID">>, Jid) -> full_jid_from_binary(Jid); input(<<"NonEmptyString">>, Value) -> non_empty_string_to_binary(Value); +input(<<"PosInt">>, Value) -> validate_pos_integer(Value); input(Ty, V) -> error_logger:info_report({coercing_generic_scalar, Ty, V}), {ok, V}. @@ -29,6 +30,7 @@ output(<<"DateTime">>, DT) -> {ok, microseconds_to_binary(DT)}; output(<<"Stanza">>, Elem) -> {ok, exml:to_binary(Elem)}; output(<<"JID">>, Jid) -> {ok, jid:to_binary(Jid)}; output(<<"NonEmptyString">>, Value) -> binary_to_non_empty_string(Value); +output(<<"PosInt">>, Value) -> validate_pos_integer(Value); output(Ty, V) -> error_logger:info_report({output_generic_scalar, Ty, V}), {ok, V}. @@ -67,6 +69,11 @@ binary_to_non_empty_string(<<>>) -> binary_to_non_empty_string(Val) -> {ok, Val}. +validate_pos_integer(PosInt) when is_integer(PosInt), PosInt > 0 -> + {ok, PosInt}; +validate_pos_integer(_Value) -> + {error, "Value is not a positive integer"}. + microseconds_to_binary(Microseconds) -> Opts = [{offset, "Z"}, {unit, microsecond}], list_to_binary(calendar:system_time_to_rfc3339(Microseconds, Opts)). diff --git a/src/graphql/user/mongoose_graphql_inbox_user_mutation.erl b/src/graphql/user/mongoose_graphql_inbox_user_mutation.erl new file mode 100644 index 00000000000..4465b7a7371 --- /dev/null +++ b/src/graphql/user/mongoose_graphql_inbox_user_mutation.erl @@ -0,0 +1,13 @@ +-module(mongoose_graphql_inbox_user_mutation). + +-behaviour(mongoose_graphql). + +-export([execute/4]). + +-import(mongoose_graphql_helper, [format_result/2, null_to_default/2]). + +-ignore_xref([execute/4]). + +execute(#{user := UserJID}, inbox, <<"flushBin">>, #{<<"days">> := Days}) -> + Res = mod_inbox_api:flush_user_bin(UserJID, null_to_default(Days, 0)), + format_result(Res, #{user => jid:to_binary(UserJID)}). diff --git a/src/graphql/user/mongoose_graphql_user_mutation.erl b/src/graphql/user/mongoose_graphql_user_mutation.erl index e5801fcfdf4..d012b224b09 100644 --- a/src/graphql/user/mongoose_graphql_user_mutation.erl +++ b/src/graphql/user/mongoose_graphql_user_mutation.erl @@ -7,12 +7,14 @@ execute(_Ctx, _Obj, <<"account">>, _Args) -> {ok, account}; +execute(_Ctx, _Obj, <<"httpUpload">>, _Args) -> + {ok, httpUpload}; +execute(_Ctx, _Obj, <<"inbox">>, _Args) -> + {ok, inbox}; execute(_Ctx, _Obj, <<"last">>, _Args) -> {ok, last}; execute(_Ctx, _Obj, <<"muc">>, _Args) -> {ok, muc}; -execute(_Ctx, _Obj, <<"httpUpload">>, _Args) -> - {ok, httpUpload}; execute(_Ctx, _Obj, <<"muc_light">>, _Args) -> {ok, muc_light}; execute(_Ctx, _Obj, <<"private">>, _Args) -> diff --git a/src/inbox/mod_inbox_api.erl b/src/inbox/mod_inbox_api.erl new file mode 100644 index 00000000000..32e7fc10a52 --- /dev/null +++ b/src/inbox/mod_inbox_api.erl @@ -0,0 +1,68 @@ +%% @doc Provide an interface for frontends (like graphql or ctl) to manage inbox. +-module(mod_inbox_api). + +-export([flush_user_bin/2, flush_domain_bin/2, flush_global_bin/2]). + +-include("mongoose.hrl"). +-include_lib("jid/include/jid.hrl"). + +-define(DOMAIN_NOT_FOUND_RESULT, {domain_not_found, <<"Domain not found">>}). + +-define(USER_NOT_FOUND_RESULT(User, Server), + {user_does_not_exist, io_lib:format("User ~s@~s does not exist", [User, Server])}). + +-spec flush_user_bin(jid:jid(), Days :: integer()) -> + {ok, integer()} | {domain_not_found, binary()}. +flush_user_bin(#jid{luser = LU, lserver = LS} = JID, Days) -> + case mongoose_domain_api:get_host_type(LS) of + {ok, HostType} -> + case ejabberd_auth:does_user_exist(JID) of + true -> + FromTS = days_to_timestamp(Days), + Count = mod_inbox_backend:empty_user_bin(HostType, LS, LU, FromTS), + {ok, Count}; + false -> + ?USER_NOT_FOUND_RESULT(LU, LS) + end; + {error, not_found} -> + ?DOMAIN_NOT_FOUND_RESULT + end. + +-spec flush_domain_bin(jid:server(), Days :: integer()) -> + {ok, integer()} | {domain_not_found, iodata()}. +flush_domain_bin(Domain, Days) -> + LDomain = jid:nodeprep(Domain), + case mongoose_domain_api:get_host_type(LDomain) of + {ok, HostType} -> + FromTS = days_to_timestamp(Days), + Count = mod_inbox_backend:empty_domain_bin(HostType, Domain, FromTS), + {ok, Count}; + {error, not_found} -> + ?DOMAIN_NOT_FOUND_RESULT + end. + +-spec flush_global_bin(mongooseim:host_type(), Days :: integer()) -> + {ok, integer()} | {host_type_not_found, binary()}. +flush_global_bin(HostType, Days) -> + case validate_host_type(HostType) of + ok -> + FromTS = days_to_timestamp(Days), + Count = mod_inbox_backend:empty_global_bin(HostType, FromTS), + {ok, Count}; + {host_type_not_found, _} = Error -> + Error + end. + +%% Internal + +validate_host_type(HostType) -> + case lists:member(HostType, ?ALL_HOST_TYPES) of + true -> + ok; + false -> + {host_type_not_found, <<"Host type not found">>} + end. + +days_to_timestamp(Days) -> + Now = erlang:system_time(microsecond), + mod_inbox_utils:calculate_ts_from(Now, Days). diff --git a/src/inbox/mod_inbox_backend.erl b/src/inbox/mod_inbox_backend.erl index 202d11903e9..1180a1ee64a 100644 --- a/src/inbox/mod_inbox_backend.erl +++ b/src/inbox/mod_inbox_backend.erl @@ -11,6 +11,7 @@ set_inbox/6, remove_inbox_row/2, empty_user_bin/4, + empty_domain_bin/3, empty_global_bin/2, set_inbox_incr_unread/5, get_inbox_unread/2, @@ -59,6 +60,11 @@ LUser :: jid:luser(), TS :: integer(). +-callback empty_domain_bin(HostType, LServer, TS) -> non_neg_integer() when + HostType :: mongooseim:host_type(), + LServer :: jid:lserver(), + TS :: integer(). + -callback empty_global_bin(HostType, TS) -> non_neg_integer() when HostType :: mongooseim:host_type(), TS :: integer(). @@ -157,6 +163,14 @@ empty_user_bin(HostType, LServer, LUser, TS) -> Args = [HostType, LServer, LUser, TS], mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args). +-spec empty_domain_bin(HostType, LServer, TS) -> non_neg_integer() when + HostType :: mongooseim:host_type(), + LServer :: jid:lserver(), + TS :: integer(). +empty_domain_bin(HostType, LServer, TS) -> + Args = [HostType, LServer, TS], + mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args). + -spec empty_global_bin(HostType, TS) -> non_neg_integer() when HostType :: mongooseim:host_type(), TS :: integer(). diff --git a/src/inbox/mod_inbox_commands.erl b/src/inbox/mod_inbox_commands.erl index 8621595e05c..27b1e4022d2 100644 --- a/src/inbox/mod_inbox_commands.erl +++ b/src/inbox/mod_inbox_commands.erl @@ -49,13 +49,13 @@ commands() -> ]. flush_user_bin(Domain, Name, Days) -> - {LU, LS} = jid:to_lus(jid:make_bare(Name, Domain)), - {ok, HostType} = mongoose_domain_api:get_host_type(LS), - Now = erlang:system_time(microsecond), - FromTS = mod_inbox_utils:calculate_ts_from(Now, Days), - mod_inbox_backend:empty_user_bin(HostType, LS, LU, FromTS). + JID = jid:make_bare(Name, Domain), + Res = mod_inbox_api:flush_user_bin(JID, Days), + format_result(Res). flush_global_bin(HostType, Days) -> - Now = erlang:system_time(microsecond), - FromTS = mod_inbox_utils:calculate_ts_from(Now, Days), - mod_inbox_backend:empty_global_bin(HostType, FromTS). + Res = mod_inbox_api:flush_global_bin(HostType, Days), + format_result(Res). + +format_result({ok, Count}) -> Count; +format_result({_, ErrMsg}) -> {error, bad_request, ErrMsg}. diff --git a/src/inbox/mod_inbox_rdbms.erl b/src/inbox/mod_inbox_rdbms.erl index f3a10ba41a1..9a2017cc5d9 100644 --- a/src/inbox/mod_inbox_rdbms.erl +++ b/src/inbox/mod_inbox_rdbms.erl @@ -19,6 +19,7 @@ set_inbox_incr_unread/5, reset_unread/4, empty_user_bin/4, + empty_domain_bin/3, empty_global_bin/2, remove_inbox_row/2, remove_domain/2, @@ -56,6 +57,9 @@ init(HostType, _Options) -> % removals mongoose_rdbms:prepare(inbox_clean_global_bin, inbox, [timestamp], <<"DELETE FROM inbox WHERE box='bin' AND timestamp < ?">>), + mongoose_rdbms:prepare(inbox_clean_domain_bin, inbox, [lserver, timestamp], + <<"DELETE FROM inbox WHERE", + " lserver = ? AND box='bin' AND timestamp < ?">>), mongoose_rdbms:prepare(inbox_clean_user_bin, inbox, [lserver, luser, timestamp], <<"DELETE FROM inbox WHERE", " lserver = ? AND luser = ? AND box='bin' AND timestamp < ?">>), @@ -139,6 +143,14 @@ empty_user_bin(HostType, LServer, LUser, TS) -> HostType, inbox_clean_user_bin, [LServer, LUser, TS]), mongoose_rdbms:result_to_integer(BinN). +-spec empty_domain_bin(HostType :: mongooseim:host_type(), + LServer :: jid:lserver(), + TS :: integer()) -> non_neg_integer(). +empty_domain_bin(HostType, LServer, TS) -> + {updated, BinN} = mongoose_rdbms:execute_successfully( + HostType, inbox_clean_domain_bin, [LServer, TS]), + mongoose_rdbms:result_to_integer(BinN). + -spec empty_global_bin(HostType :: mongooseim:host_type(), TS :: integer()) -> non_neg_integer(). empty_global_bin(HostType, TS) -> diff --git a/src/inbox/mod_inbox_rdbms_async.erl b/src/inbox/mod_inbox_rdbms_async.erl index 6c270bcb7e4..a4856396511 100644 --- a/src/inbox/mod_inbox_rdbms_async.erl +++ b/src/inbox/mod_inbox_rdbms_async.erl @@ -20,6 +20,7 @@ reset_unread/4, remove_inbox_row/2, empty_user_bin/4, + empty_domain_bin/3, empty_global_bin/2, remove_domain/2, clear_inbox/3, @@ -168,6 +169,12 @@ set_entry_properties(HostType, Entry, Properties) -> empty_user_bin(HostType, LServer, LUser, TS) -> mod_inbox_rdbms:empty_user_bin(HostType, LServer, LUser, TS). +-spec empty_domain_bin(HostType :: mongooseim:host_type(), + LServer :: jid:lserver(), + TS :: integer()) -> non_neg_integer(). +empty_domain_bin(HostType, LServer, TS) -> + mod_inbox_rdbms:empty_domain_bin(HostType, LServer, TS). + -spec empty_global_bin(HostType :: mongooseim:host_type(), TS :: integer()) -> non_neg_integer(). empty_global_bin(HostType, TS) ->