From 1d11bc624cbf4618dc3ec5ed593520e0b8baf317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= Date: Wed, 29 Jun 2022 00:35:41 +0200 Subject: [PATCH 1/6] Create schemas for inbox category --- priv/graphql/schemas/admin/admin_schema.gql | 2 ++ priv/graphql/schemas/admin/inbox.gql | 28 +++++++++++++++++++++ priv/graphql/schemas/user/inbox.gql | 10 ++++++++ priv/graphql/schemas/user/user_schema.gql | 2 ++ 4 files changed, 42 insertions(+) create mode 100644 priv/graphql/schemas/admin/inbox.gql create mode 100644 priv/graphql/schemas/user/inbox.gql diff --git a/priv/graphql/schemas/admin/admin_schema.gql b/priv/graphql/schemas/admin/admin_schema.gql index 702021013b..3822f75c71 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 0000000000..0c60433ed6 --- /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 deleted rows number" + flushUserBin( + "User to clear a bin" + user: JID!, + "Remove older than given days. Use 0 to clear all" + days: Int! + ): Int @protected(type: DOMAIN, args: ["user"]) + + "Flush the whole domain bin and return deleted rows number" + flushDomainBin( + "Domain to be cleared" + domain: String!, + "Remove older than given days. Use 0 to clear all" + days: Int! + ): Int @protected(type: Domain, args: ["domain"]) + + "Flush the global bin and return deleted rows number" + flushGlobalBin( + "Required to identify the DB backend" + hostType: String!, + "Remove older than given days. Use 0 to clear all" + days: Int! + ): Int @protected(type: GLOBAL) +} diff --git a/priv/graphql/schemas/user/inbox.gql b/priv/graphql/schemas/user/inbox.gql new file mode 100644 index 0000000000..e61a80d070 --- /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 deleted rows number" + flushBin( + "Remove older than given days. Use 0 to clear all" + days: Int! + ): Int +} diff --git a/priv/graphql/schemas/user/user_schema.gql b/priv/graphql/schemas/user/user_schema.gql index 320fc81933..4593ff72df 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" From dedc1d6041d705624d8cc876354ece200c5104a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= Date: Wed, 29 Jun 2022 00:38:01 +0200 Subject: [PATCH 2/6] Extract inbox commands to the API module and allow to flush domain --- src/inbox/mod_inbox_api.erl | 67 +++++++++++++++++++++++++++++ src/inbox/mod_inbox_backend.erl | 14 ++++++ src/inbox/mod_inbox_commands.erl | 16 +++---- src/inbox/mod_inbox_rdbms.erl | 12 ++++++ src/inbox/mod_inbox_rdbms_async.erl | 7 +++ 5 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 src/inbox/mod_inbox_api.erl diff --git a/src/inbox/mod_inbox_api.erl b/src/inbox/mod_inbox_api.erl new file mode 100644 index 0000000000..22867d4bce --- /dev/null +++ b/src/inbox/mod_inbox_api.erl @@ -0,0 +1,67 @@ +-module(mod_inbox_api). + +-export([flush_user_bin/2, flush_domain_bin/2, flush_global_bin/2]). + +-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) -> + HostTypes = mongoose_config:get_opt(host_types) ++ mongoose_config:get_opt(hosts), + case lists:member(HostType, HostTypes) 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 202d11903e..1180a1ee64 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 8621595e05..27b1e4022d 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 f3a10ba41a..9a2017cc5d 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 6c270bcb7e..a485639651 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) -> From a1e9173650a09d9313ef0c54220c08bc3aa3af6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= Date: Wed, 29 Jun 2022 00:40:27 +0200 Subject: [PATCH 3/6] Add resolvers for the user's and admin's mutations --- .../admin/mongoose_graphql_admin_mutation.erl | 8 ++++---- .../mongoose_graphql_inbox_admin_mutation.erl | 19 +++++++++++++++++++ src/graphql/mongoose_graphql.erl | 2 ++ .../mongoose_graphql_inbox_user_mutation.erl | 13 +++++++++++++ .../user/mongoose_graphql_user_mutation.erl | 6 ++++-- 5 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 src/graphql/admin/mongoose_graphql_inbox_admin_mutation.erl create mode 100644 src/graphql/user/mongoose_graphql_inbox_user_mutation.erl diff --git a/src/graphql/admin/mongoose_graphql_admin_mutation.erl b/src/graphql/admin/mongoose_graphql_admin_mutation.erl index a76e9169cd..7288be1646 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 0000000000..4d28278b2b --- /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]). + +-ignore_xref([execute/4]). + +execute(_Ctx, inbox, <<"flushUserBin">>, #{<<"user">> := UserJID, <<"days">> := Days}) -> + Res = mod_inbox_api:flush_user_bin(UserJID, Days), + format_result(Res, #{user => jid:to_binary(UserJID)}); +execute(_Ctx, inbox, <<"flushDomainBin">>, #{<<"domain">> := Domain, <<"days">> := Days}) -> + Res = mod_inbox_api:flush_domain_bin(Domain, Days), + format_result(Res, #{domain => Domain}); +execute(_Ctx, inbox, <<"flushGlobalBin">>, #{<<"hostType">> := HostType, <<"days">> := Days}) -> + Res = mod_inbox_api:flush_global_bin(HostType, Days), + format_result(Res, #{host_type => HostType}). diff --git a/src/graphql/mongoose_graphql.erl b/src/graphql/mongoose_graphql.erl index 84a04c26e1..3a61a3e4a2 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/user/mongoose_graphql_inbox_user_mutation.erl b/src/graphql/user/mongoose_graphql_inbox_user_mutation.erl new file mode 100644 index 0000000000..3b3231c6e7 --- /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]). + +-ignore_xref([execute/4]). + +execute(#{user := UserJID}, inbox, <<"flushBin">>, #{<<"days">> := Days}) -> + Res = mod_inbox_api:flush_user_bin(UserJID, Days), + 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 e5801fcfdf..d012b224b0 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) -> From de8704925c29540fc193f5b8a692581e929352bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= Date: Wed, 29 Jun 2022 00:42:42 +0200 Subject: [PATCH 4/6] Add tests for both admin and user --- big_tests/default.spec | 1 + big_tests/dynamic_domains.spec | 1 + big_tests/tests/graphql_inbox_SUITE.erl | 180 ++++++++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 big_tests/tests/graphql_inbox_SUITE.erl diff --git a/big_tests/default.spec b/big_tests/default.spec index 3e853d5d18..8b538d724a 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 72fad8156a..f84529e1e5 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 0000000000..9eee1c6353 --- /dev/null +++ b/big_tests/tests/graphql_inbox_SUITE.erl @@ -0,0 +1,180 @@ +-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"). + +-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_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 = inbox_helper:inbox_modules(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(_, _Config) -> + escalus_fresh:clean(). + +init_per_testcase(CaseName, Config) -> + escalus:init_per_testcase(CaseName, Config). + +end_per_testcase(CaseName, Config) -> + 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) -> + inbox_SUITE:create_room_and_make_users_leave(Alice, Bob, Kate), + Res = execute_auth(admin_flush_user_bin_body(Bob, 0), Config), + NumOfRows = get_ok_value(p(flushUserBin), Res), + ?assertEqual(1, NumOfRows), + inbox_helper:check_inbox(Bob, [], #{box => bin}). + +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) -> + Domain = domain_helper:domain(), + inbox_SUITE:create_room_and_make_users_leave(Alice, AliceBis, Kate), + Res = execute_auth(admin_flush_domain_bin_body(Domain, 0), Config), + NumOfRows = get_ok_value(p(flushDomainBin), Res), + ?assertEqual(2, NumOfRows), + inbox_helper:check_inbox(Alice, [], #{box => bin}), + inbox_helper:check_inbox(Kate, [], #{box => bin}). + +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(), + inbox_SUITE:create_room_and_make_users_leave(Alice, AliceBis, Kate), + Res = execute_auth(admin_flush_global_bin_body(SecHostType, 0), Config), + NumOfRows = get_ok_value(p(flushGlobalBin), Res), + ?assertEqual(3, NumOfRows), + inbox_helper:check_inbox(Alice, [], #{box => bin}), + inbox_helper:check_inbox(AliceBis, [], #{box => bin}), + inbox_helper:check_inbox(Kate, [], #{box => bin}). + +admin_try_flush_nonexistent_user_bin(Config) -> + %% Check nonexistent domain error + Res = execute_auth(admin_flush_user_bin_body(<<"user@user.com">>, 0), 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, 0), 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">>, 0), 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">>, 0), 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) -> + inbox_SUITE:create_room_and_make_users_leave(Alice, Bob, Kate), + Res = execute_user(user_flush_own_bin_body(0), Bob, Config), + NumOfRows = get_ok_value(p(flushBin), Res), + ?assertEqual(1, NumOfRows), + inbox_helper:check_inbox(Bob, [], #{box => bin}). + +%% Helpers + +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: Int!) + { 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: Int!) + { 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: Int!) + { 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: Int!) { inbox { flushBin(days: $days) } }">>, + OpName = <<"M1">>, + Vars = #{days => Days}, + #{query => Query, operationName => OpName, variables => Vars}. From 07f7dd3d12ee88bc67d322214b947de763056855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= Date: Wed, 29 Jun 2022 18:15:19 +0200 Subject: [PATCH 5/6] Apply review - Add PosInt scalar type and use it in inbox schemas Now the user needs to give a positive integer value to remove older than `n` days or null to flush all. --- priv/graphql/schemas/admin/inbox.gql | 18 +++++++++--------- priv/graphql/schemas/global/scalar_types.gql | 1 + priv/graphql/schemas/user/inbox.gql | 6 +++--- .../mongoose_graphql_inbox_admin_mutation.erl | 8 ++++---- src/graphql/mongoose_graphql_scalar.erl | 7 +++++++ .../mongoose_graphql_inbox_user_mutation.erl | 4 ++-- 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/priv/graphql/schemas/admin/inbox.gql b/priv/graphql/schemas/admin/inbox.gql index 0c60433ed6..fdc6c4aad5 100644 --- a/priv/graphql/schemas/admin/inbox.gql +++ b/priv/graphql/schemas/admin/inbox.gql @@ -2,27 +2,27 @@ Allow admin to flush the inbox bin". """ type InboxAdminMutation @protected{ - "Flush the user's bin and return deleted rows number" + "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. Use 0 to clear all" - days: Int! + "Remove older than given days or all if null" + days: PosInt ): Int @protected(type: DOMAIN, args: ["user"]) - "Flush the whole domain bin and return deleted rows number" + "Flush the whole domain bin and return the number of deleted rows" flushDomainBin( "Domain to be cleared" domain: String!, - "Remove older than given days. Use 0 to clear all" - days: Int! + "Remove older than given days or all if null" + days: PosInt ): Int @protected(type: Domain, args: ["domain"]) - "Flush the global bin and return deleted rows number" + "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. Use 0 to clear all" - days: Int! + "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 65af7664a6..4ee27eb651 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 index e61a80d070..722180d5d4 100644 --- a/priv/graphql/schemas/user/inbox.gql +++ b/priv/graphql/schemas/user/inbox.gql @@ -2,9 +2,9 @@ Allow user to flush own inbox bin". """ type InboxUserMutation @protected{ - "Flush the user's bin and return deleted rows number" + "Flush the user's bin and return the number of deleted rows" flushBin( - "Remove older than given days. Use 0 to clear all" - days: Int! + "Remove older than given days or all if null" + days: PosInt ): Int } diff --git a/src/graphql/admin/mongoose_graphql_inbox_admin_mutation.erl b/src/graphql/admin/mongoose_graphql_inbox_admin_mutation.erl index 4d28278b2b..275ed56a92 100644 --- a/src/graphql/admin/mongoose_graphql_inbox_admin_mutation.erl +++ b/src/graphql/admin/mongoose_graphql_inbox_admin_mutation.erl @@ -4,16 +4,16 @@ -export([execute/4]). --import(mongoose_graphql_helper, [format_result/2]). +-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, 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, 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, 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_scalar.erl b/src/graphql/mongoose_graphql_scalar.erl index f33a5af5b6..bd93d5321e 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 index 3b3231c6e7..4465b7a737 100644 --- a/src/graphql/user/mongoose_graphql_inbox_user_mutation.erl +++ b/src/graphql/user/mongoose_graphql_inbox_user_mutation.erl @@ -4,10 +4,10 @@ -export([execute/4]). --import(mongoose_graphql_helper, [format_result/2]). +-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, Days), + Res = mod_inbox_api:flush_user_bin(UserJID, null_to_default(Days, 0)), format_result(Res, #{user => jid:to_binary(UserJID)}). From cb6ae8fad37f8b52027e75a7357dc40110435c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= Date: Wed, 29 Jun 2022 18:21:11 +0200 Subject: [PATCH 6/6] Apply review - Add sanity checks in tests and use ?ALL_HOST_TYPES macro in api --- big_tests/tests/graphql_inbox_SUITE.erl | 76 +++++++++++++++++-------- src/inbox/mod_inbox_api.erl | 5 +- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/big_tests/tests/graphql_inbox_SUITE.erl b/big_tests/tests/graphql_inbox_SUITE.erl index 9eee1c6353..cc9ab1c5c0 100644 --- a/big_tests/tests/graphql_inbox_SUITE.erl +++ b/big_tests/tests/graphql_inbox_SUITE.erl @@ -7,6 +7,7 @@ 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)). @@ -34,13 +35,14 @@ admin_inbox_handler() -> 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 = inbox_helper:inbox_modules(async_pools) ++ inbox_helper:muclight_modules(), + 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). @@ -54,13 +56,15 @@ init_per_group(admin_inbox, Config) -> init_per_group(user_inbox, Config) -> [{schema_endpoint, user} | Config]. -end_per_group(_, _Config) -> - escalus_fresh:clean(). +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 @@ -70,24 +74,25 @@ admin_flush_user_bin(Config) -> fun admin_flush_user_bin/4). admin_flush_user_bin(Config, Alice, Bob, Kate) -> - inbox_SUITE:create_room_and_make_users_leave(Alice, Bob, Kate), - Res = execute_auth(admin_flush_user_bin_body(Bob, 0), Config), + 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}). + 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(), - inbox_SUITE:create_room_and_make_users_leave(Alice, AliceBis, Kate), - Res = execute_auth(admin_flush_domain_bin_body(Domain, 0), Config), + Res = execute_auth(admin_flush_domain_bin_body(Domain, null), Config), NumOfRows = get_ok_value(p(flushDomainBin), Res), - ?assertEqual(2, NumOfRows), - inbox_helper:check_inbox(Alice, [], #{box => bin}), - inbox_helper:check_inbox(Kate, [], #{box => bin}). + ?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}], @@ -95,32 +100,44 @@ admin_flush_global_bin(Config) -> admin_flush_global_bin(Config, Alice, AliceBis, Kate) -> SecHostType = domain_helper:host_type(), - inbox_SUITE:create_room_and_make_users_leave(Alice, AliceBis, Kate), - Res = execute_auth(admin_flush_global_bin_body(SecHostType, 0), Config), + 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(3, NumOfRows), - inbox_helper:check_inbox(Alice, [], #{box => bin}), + ?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">>, 0), Config), + 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, 0), Config), + 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">>, 0), 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">>, 0), Config), + Res = execute_auth(admin_flush_global_bin_body(<<"nonexistent host type">>, null), Config), ?assertErrMsg(Res, <<"not found">>), ?assertErrCode(Res, host_type_not_found). @@ -131,14 +148,23 @@ user_flush_own_bin(Config) -> fun user_flush_own_bin/4). user_flush_own_bin(Config, Alice, Bob, Kate) -> - inbox_SUITE:create_room_and_make_users_leave(Alice, Bob, Kate), - Res = execute_user(user_flush_own_bin_body(0), Bob, Config), + 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)). @@ -153,28 +179,28 @@ p(Path) when is_list(Path) -> %% Request bodies admin_flush_user_bin_body(User, Days) -> - Query = <<"mutation M1($user: JID!, $days: Int!) + 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: Int!) + 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: Int!) + 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: Int!) { inbox { flushBin(days: $days) } }">>, + Query = <<"mutation M1($days: PosInt) { inbox { flushBin(days: $days) } }">>, OpName = <<"M1">>, Vars = #{days => Days}, #{query => Query, operationName => OpName, variables => Vars}. diff --git a/src/inbox/mod_inbox_api.erl b/src/inbox/mod_inbox_api.erl index 22867d4bce..32e7fc10a5 100644 --- a/src/inbox/mod_inbox_api.erl +++ b/src/inbox/mod_inbox_api.erl @@ -1,7 +1,9 @@ +%% @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">>}). @@ -54,8 +56,7 @@ flush_global_bin(HostType, Days) -> %% Internal validate_host_type(HostType) -> - HostTypes = mongoose_config:get_opt(host_types) ++ mongoose_config:get_opt(hosts), - case lists:member(HostType, HostTypes) of + case lists:member(HostType, ?ALL_HOST_TYPES) of true -> ok; false ->