-
Notifications
You must be signed in to change notification settings - Fork 428
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3688 from esl/graphql/offline
Mod offline graphql
- Loading branch information
Showing
10 changed files
with
274 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
-module(graphql_offline_SUITE). | ||
|
||
-compile([export_all, nowarn_export_all]). | ||
|
||
-import(distributed_helper, [require_rpc_nodes/1]). | ||
-import(domain_helper, [host_type/0, domain/0]). | ||
-import(graphql_helper, [execute_user/3, execute_auth/2, user_to_bin/1]). | ||
-import(config_parser_helper, [mod_config/2]). | ||
-import(mongooseimctl_helper, [mongooseimctl/3, rpc_call/3]). | ||
|
||
-include_lib("common_test/include/ct.hrl"). | ||
-include_lib("eunit/include/eunit.hrl"). | ||
-include_lib("exml/include/exml.hrl"). | ||
-include_lib("escalus/include/escalus.hrl"). | ||
-include("../../include/mod_roster.hrl"). | ||
|
||
-record(offline_msg, {us, timestamp, expire, from, to, packet, permanent_fields = []}). | ||
|
||
suite() -> | ||
require_rpc_nodes([mim]) ++ escalus:suite(). | ||
|
||
all() -> | ||
[{group, admin_offline}, | ||
{group, admin_offline_not_configured}]. | ||
|
||
groups() -> | ||
[{admin_offline, [], admin_offline_handler()}, | ||
{admin_offline_not_configured, [], admin_offline_not_configured_handler()}]. | ||
|
||
admin_offline_handler() -> | ||
[admin_delete_expired_messages_test, | ||
admin_delete_old_messages_test, | ||
admin_delete_expired_messages2_test, | ||
admin_delete_old_messages2_test, | ||
admin_delete_expired_messages_no_domain_test, | ||
admin_delete_old_messages_no_domain_test]. | ||
|
||
admin_offline_not_configured_handler() -> | ||
[admin_delete_expired_messages_offline_not_configured_test, | ||
admin_delete_old_messages_offline_not_configured_test]. | ||
|
||
init_per_suite(Config) -> | ||
Config1 = dynamic_modules:save_modules(host_type(), Config), | ||
escalus:init_per_suite(Config1). | ||
|
||
-spec create_config(atom()) -> [{mod_offline, gen_mod:module_opts()}]. | ||
create_config(riak) -> | ||
[{mod_offline, mod_config(mod_offline, #{backend => riak, | ||
riak => #{bucket_type => <<"offline">>}})}]; | ||
create_config(Backend) -> | ||
[{mod_offline, mod_config(mod_offline, #{backend => Backend})}]. | ||
|
||
|
||
end_per_suite(Config) -> | ||
dynamic_modules:restore_modules(Config), | ||
escalus:end_per_suite(Config). | ||
|
||
init_per_group(admin_offline, Config) -> | ||
HostType = host_type(), | ||
Backend = mongoose_helper:get_backend_mnesia_rdbms_riak(HostType), | ||
ModConfig = create_config(Backend), | ||
dynamic_modules:ensure_modules(HostType, ModConfig), | ||
Config1 = [{backend, Backend} | escalus:init_per_suite(Config)], | ||
graphql_helper:init_admin_handler(Config1); | ||
init_per_group(admin_offline_not_configured, Config) -> | ||
dynamic_modules:ensure_modules(host_type(), [{mod_offline, stopped}]), | ||
graphql_helper:init_admin_handler(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_delete_expired_messages_test(Config) -> | ||
Vars = #{<<"domain">> => domain()}, | ||
GraphQlRequest = admin_delete_expired_messages_mutation(Config, Vars), | ||
Message = ok_result(<<"offline">>, <<"deleteExpiredMessages">>, GraphQlRequest), | ||
?assertEqual(<<"Removed 0 messages">>, Message). | ||
|
||
admin_delete_old_messages_test(Config) -> | ||
Vars = #{<<"domain">> => domain(), <<"days">> => 2}, | ||
GraphQlRequest = admin_delete_old_messages_mutation(Config, Vars), | ||
Message = ok_result(<<"offline">>, <<"deleteOldMessages">>, GraphQlRequest), | ||
?assertEqual(<<"Removed 0 messages">>, Message). | ||
|
||
admin_delete_expired_messages2_test(Config) -> | ||
escalus:fresh_story_with_config(Config, [{mike, 1}, {kate, 1}], fun admin_delete_expired_messages2_test/3). | ||
|
||
admin_delete_expired_messages2_test(Config, JidMike, JidKate) -> | ||
generate_message(JidMike, JidKate, 10, 2), | ||
generate_message(JidMike, JidKate, 10, 2), | ||
Vars = #{<<"domain">> => domain()}, | ||
GraphQlRequest = admin_delete_expired_messages_mutation(Config, Vars), | ||
Message = ok_result(<<"offline">>, <<"deleteExpiredMessages">>, GraphQlRequest), | ||
?assertEqual(<<"Removed 2 messages">>, Message). | ||
|
||
admin_delete_old_messages2_test(Config) -> | ||
escalus:fresh_story_with_config(Config, [{mike, 1}, {kate, 1}], fun admin_delete_old_messages2_test/3). | ||
|
||
admin_delete_old_messages2_test(Config, JidMike, JidKate) -> | ||
generate_message(JidMike, JidKate, 2, 10), | ||
generate_message(JidMike, JidKate, 2, 10), | ||
Vars = #{<<"domain">> => domain(), <<"days">> => 2}, | ||
GraphQlRequest = admin_delete_old_messages_mutation(Config, Vars), | ||
Message = ok_result(<<"offline">>, <<"deleteOldMessages">>, GraphQlRequest), | ||
?assertEqual(<<"Removed 2 messages">>, Message). | ||
|
||
admin_delete_expired_messages_no_domain_test(Config) -> | ||
Vars = #{<<"domain">> => <<"AAAA">>}, | ||
GraphQlRequest = admin_delete_expired_messages_mutation(Config, Vars), | ||
ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest), | ||
?assertEqual(<<"domain_not_found">>, ParsedResult). | ||
|
||
admin_delete_old_messages_no_domain_test(Config) -> | ||
Vars = #{<<"domain">> => <<"AAAA">>, <<"days">> => 2}, | ||
GraphQlRequest = admin_delete_old_messages_mutation(Config, Vars), | ||
ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest), | ||
?assertEqual(<<"domain_not_found">>, ParsedResult). | ||
|
||
admin_delete_expired_messages_offline_not_configured_test(Config) -> | ||
Vars = #{<<"domain">> => domain()}, | ||
GraphQlRequest = admin_delete_expired_messages_mutation(Config, Vars), | ||
ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest), | ||
?assertEqual(<<"module_not_loaded_error">>, ParsedResult). | ||
|
||
admin_delete_old_messages_offline_not_configured_test(Config) -> | ||
Vars = #{<<"domain">> => domain(), <<"days">> => 2}, | ||
GraphQlRequest = admin_delete_old_messages_mutation(Config, Vars), | ||
ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest), | ||
?assertEqual(<<"module_not_loaded_error">>, ParsedResult). | ||
|
||
% Helpers | ||
|
||
admin_delete_expired_messages_mutation(Config, Vars) -> | ||
Mutation = <<"mutation M1($domain: String!) | ||
{offline{deleteExpiredMessages(domain: $domain)}}">>, | ||
admin_send_mutation(Config, Vars, Mutation). | ||
|
||
admin_delete_old_messages_mutation(Config, Vars) -> | ||
Mutation = <<"mutation M1($domain: String!, $days: Int!) | ||
{offline{deleteOldMessages(domain: $domain, days: $days)}}">>, | ||
admin_send_mutation(Config, Vars, Mutation). | ||
|
||
admin_send_mutation(Config, Vars, Mutation) -> | ||
Body = #{query => Mutation, operationName => <<"M1">>, variables => Vars}, | ||
execute_auth(Body, Config). | ||
|
||
error_result(What1, What2, {{<<"200">>, <<"OK">>}, #{<<"errors">> := [Data]}}) -> | ||
maps:get(What2, maps:get(What1, Data)). | ||
|
||
ok_result(What1, What2, {{<<"200">>, <<"OK">>}, #{<<"data">> := Data}}) -> | ||
maps:get(What2, maps:get(What1, Data)). | ||
|
||
generate_message(JidMike, JidKate, TimestampDaysAgo, TimestampExpiringDaysAgo) -> | ||
JidRecordMike = jid:from_binary(user_to_bin(JidMike)), | ||
JidRecordKate = jid:from_binary(user_to_bin(JidKate)), | ||
Domain = domain(), | ||
Msg1 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "Rolling stones"), | ||
OldTimestamp = fallback_timestamp(TimestampDaysAgo, os:system_time(microsecond)), | ||
ExpirationTime = fallback_timestamp(TimestampExpiringDaysAgo, os:system_time(microsecond)), | ||
OfflineOld = generate_offline_expired_message(JidRecordMike, | ||
JidRecordKate, Msg1, | ||
OldTimestamp, | ||
ExpirationTime), | ||
{LUser, LServer} = jid:to_lus(JidRecordKate), | ||
rpc_call(mod_offline_backend, write_messages, [host_type(), LUser, LServer, [OfflineOld]]). | ||
|
||
generate_offline_expired_message(From, To, Msg, TimeStamp, ExpirationTime) -> | ||
{LUser, LServer} = jid:to_lus(To), | ||
#offline_msg{us = {LUser, LServer}, timestamp = TimeStamp, | ||
expire = ExpirationTime, from = From, to = To, packet = Msg}. | ||
|
||
fallback_timestamp(HowManyDays, TS_MicroSeconds) -> | ||
HowManySeconds = HowManyDays * 86400, | ||
HowManyMicroSeconds = erlang:convert_time_unit(HowManySeconds, second, microsecond), | ||
TS_MicroSeconds - HowManyMicroSeconds. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
""" | ||
Allow admin to delete offline messages from specified domain | ||
""" | ||
type OfflineAdminMutation @protected{ | ||
"Allow admin to delete offline messages whose date has expired" | ||
deleteExpiredMessages(domain: String!): String | ||
@protected(type: DOMAIN, args: ["domain"]) | ||
"Allow the admin to delete messages at least as old as the number of days specified in the parameter" | ||
deleteOldMessages(domain: String!, days: Int!): String | ||
@protected(type: DOMAIN, args: ["domain"]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
src/graphql/admin/mongoose_graphql_offline_admin_mutation.erl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
-module(mongoose_graphql_offline_admin_mutation). | ||
-behaviour(mongoose_graphql). | ||
|
||
-export([execute/4]). | ||
|
||
-import(mongoose_graphql_helper, [make_error/2]). | ||
|
||
-ignore_xref([execute/4]). | ||
|
||
-include("../mongoose_graphql_types.hrl"). | ||
-include("mongoose.hrl"). | ||
-include("jlib.hrl"). | ||
|
||
execute(_Ctx, offline, <<"deleteExpiredMessages">>, #{<<"domain">> := Domain} = Input) -> | ||
case mod_offline_api:delete_expired_messages(Domain) of | ||
{ok, _} = Result -> Result; | ||
Error -> make_error(Error, Input) | ||
end; | ||
|
||
execute(_Ctx, offline, <<"deleteOldMessages">>, #{<<"domain">> := Domain, <<"days">> := Days} = Input) -> | ||
case mod_offline_api:delete_old_messages(Domain, Days) of | ||
{ok, _} = Result -> Result; | ||
Error -> make_error(Error, Input) | ||
end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
-module(mod_offline_api). | ||
|
||
-export([delete_expired_messages/1, delete_old_messages/2]). | ||
|
||
-spec delete_expired_messages(jid:lserver()) -> | ||
{ok | domain_not_found | server_error | module_not_loaded_error, iolist()}. | ||
delete_expired_messages(Domain) -> | ||
call_for_loaded_module(Domain, fun remove_expired_messages/2, {Domain}). | ||
|
||
-spec delete_old_messages(jid:lserver(), Days :: integer()) -> | ||
{ok | domain_not_found | server_error | module_not_loaded_error, iolist()}. | ||
delete_old_messages(Domain, Days) -> | ||
call_for_loaded_module(Domain, fun remove_old_messages/2, {Domain, Days}). | ||
|
||
call_for_loaded_module(Domain, Function, Args) -> | ||
case mongoose_domain_api:get_domain_host_type(Domain) of | ||
{ok, HostType} -> | ||
case gen_mod:is_loaded(HostType, mod_offline) of | ||
true -> | ||
Function(Args, HostType); | ||
false -> | ||
{module_not_loaded_error, "mod_offline is not loaded for this host"} | ||
end; | ||
{error, not_found} -> | ||
{domain_not_found, "Unknown domain"} | ||
end. | ||
|
||
remove_old_messages({Domain, Days}, HostType) -> | ||
case mod_offline:remove_old_messages(HostType, Domain, Days) of | ||
{ok, C} -> | ||
{ok, io_lib:format("Removed ~p messages", [C])}; | ||
{error, Reason} -> | ||
{server_error, io_lib:format("Can't remove old messages: ~n~p", [Reason])} | ||
end. | ||
|
||
remove_expired_messages({Domain}, HostType) -> | ||
case mod_offline:remove_expired_messages(HostType, Domain) of | ||
{ok, C} -> | ||
{ok, io_lib:format("Removed ~p messages", [C])}; | ||
{error, Reason} -> | ||
{server_error, io_lib:format("Can't remove old messages: ~n~p", [Reason])} | ||
end. |