From 5b8136da6bb7f1310d4f7e92b6d1e3cbe6ca7f15 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Mon, 7 Feb 2022 13:17:00 +0100 Subject: [PATCH 1/5] Implement remove_domain, with the DB changes and tests required --- big_tests/tests/domain_removal_SUITE.erl | 31 ++++++- big_tests/tests/mongoose_helper.erl | 6 +- doc/migrations/5.0.0_5.1.0.md | 3 + priv/mssql2012.sql | 10 ++- priv/mysql.sql | 11 +-- priv/pg.sql | 5 +- src/smart_markers/mod_smart_markers.erl | 26 ++++-- .../mod_smart_markers_backend.erl | 10 ++- src/smart_markers/mod_smart_markers_rdbms.erl | 87 ++++++++----------- 9 files changed, 113 insertions(+), 76 deletions(-) diff --git a/big_tests/tests/domain_removal_SUITE.erl b/big_tests/tests/domain_removal_SUITE.erl index 8e363720da1..b1232eceea3 100644 --- a/big_tests/tests/domain_removal_SUITE.erl +++ b/big_tests/tests/domain_removal_SUITE.erl @@ -12,7 +12,8 @@ -include_lib("common_test/include/ct.hrl"). all() -> - [{group, auth_removal}, + [ + {group, auth_removal}, {group, cache_removal}, {group, mam_removal}, {group, inbox_removal}, @@ -21,8 +22,10 @@ all() -> {group, private_removal}, {group, roster_removal}, {group, offline_removal}, + {group, markers_removal}, {group, vcard_removal}, - {group, last_removal}]. + {group, last_removal} + ]. groups() -> [ @@ -37,6 +40,7 @@ groups() -> {private_removal, [], [private_removal]}, {roster_removal, [], [roster_removal]}, {offline_removal, [], [offline_removal]}, + {markers_removal, [], [markers_removal]}, {vcard_removal, [], [vcard_removal]}, {last_removal, [], [last_removal]} ]. @@ -98,6 +102,8 @@ group_to_modules(roster_removal) -> [{mod_roster, [{backend, rdbms}]}]; group_to_modules(offline_removal) -> [{mod_offline, [{backend, rdbms}]}]; +group_to_modules(markers_removal) -> + [{mod_smart_markers, [{backend, rdbms}]}]; group_to_modules(vcard_removal) -> [{mod_vcard, config_parser_helper:mod_config(mod_vcard, #{backend => rdbms})}]; group_to_modules(last_removal) -> @@ -290,6 +296,27 @@ offline_removal(Config) -> ?assertMatch({ok, []}, rpc(mim(), mod_offline_rdbms, fetch_messages, [host_type(), BobJid])) end). +markers_removal(Config) -> + escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> + Body = <<"Hello Bob!">>, + MsgId = escalus_stanza:id(), + Msg = escalus_stanza:set_id(escalus_stanza:chat_to(Bob, Body), MsgId), + escalus:send(Alice, Msg), + escalus:wait_for_stanza(Bob), + ChatMarker = escalus_stanza:chat_marker(Alice, <<"displayed">>, MsgId), + escalus:send(Bob, ChatMarker), + escalus:wait_for_stanza(Alice), + mongoose_helper:wait_until( + fun() -> 1 =< mongoose_helper:generic_count(mod_smart_markers) end, true), + % check messages in DB + AliceJid = jid:from_binary(escalus_client:full_jid(Alice)), + ?assertMatch([_], rpc(mim(), mod_smart_markers_backend, get_chat_markers, + [host_type(), AliceJid, undefined, 0])), + run_remove_domain(), + ?assertMatch([], rpc(mim(), mod_smart_markers_backend, get_chat_markers, + [host_type(), AliceJid, undefined, 0])) + end). + roster_removal(Config) -> escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> %% add contact diff --git a/big_tests/tests/mongoose_helper.erl b/big_tests/tests/mongoose_helper.erl index 8904439f8c0..a24d1fe8a65 100644 --- a/big_tests/tests/mongoose_helper.erl +++ b/big_tests/tests/mongoose_helper.erl @@ -19,7 +19,8 @@ total_privacy_items/0, total_private_items/0, total_vcard_items/0, - total_roster_items/0]). + total_roster_items/0, + generic_count/1]). -export([clear_last_activity/2, clear_caps_cache/1]). @@ -188,6 +189,7 @@ generic_count_per_host_type(HostType, Module) -> generic_count_backend(B) end. +generic_count_backend(mod_smart_markers_rdbms) -> count_rdbms(<<"smart_markers">>); generic_count_backend(mod_offline_mnesia) -> count_wildpattern(offline_msg); generic_count_backend(mod_offline_rdbms) -> count_rdbms(<<"offline_message">>); generic_count_backend(mod_offline_riak) -> count_riak(<<"offline">>); @@ -222,7 +224,7 @@ count_wildpattern(Table) -> count_rdbms(Table) -> {selected, [{N}]} = rpc(mim(), mongoose_rdbms, sql_query, - [<<"localhost">>, [<<"select count(*) from ", Table/binary, " ;">>]]), + [domain_helper:host_type(), [<<"select count(*) from ", Table/binary, " ;">>]]), count_to_integer(N). count_to_integer(N) when is_binary(N) -> diff --git a/doc/migrations/5.0.0_5.1.0.md b/doc/migrations/5.0.0_5.1.0.md index 815f7724d0e..a1d7a30e503 100644 --- a/doc/migrations/5.0.0_5.1.0.md +++ b/doc/migrations/5.0.0_5.1.0.md @@ -45,3 +45,6 @@ now becomes async_writer.batch_size = 100 muc.async_writer.enabled = false ``` + +## Smart markers +There's an experimental and undocumented module called `mod_smart_markers`, that had a default table in the RDBMS schema, which you probably never used (or shouldn't have, as it was undocumented). If you rely on this table, the column `from_jid` has been split in `from_luser` and `lserver`, in order to support the `remove_domain` callback for the dynamic domains functionality. You might need to migrate it, or simply drop the previously defined table and recreate the new one. diff --git a/priv/mssql2012.sql b/priv/mssql2012.sql index df8bf99e0d3..9a530f4ab2c 100644 --- a/priv/mssql2012.sql +++ b/priv/mssql2012.sql @@ -686,9 +686,10 @@ CREATE TABLE muc_registered( -- from_jid, to_jid and thread have 250 characters in MySQL -- but here we are limited by index size (900 bytes) CREATE TABLE smart_markers ( - from_jid NVARCHAR(150) NOT NULL, - to_jid NVARCHAR(150) NOT NULL, - thread NVARCHAR(145) NOT NULL, + lserver NVARCHAR(100) NOT NULL, + from_luser NVARCHAR(150) NOT NULL, + to_jid NVARCHAR(250) NOT NULL, + thread NVARCHAR(80) NOT NULL, -- chat marker types: -- 'R' - received -- 'D' - displayed @@ -697,7 +698,8 @@ CREATE TABLE smart_markers ( msg_id NVARCHAR(250) NOT NULL, timestamp BIGINT NOT NULL, CONSTRAINT pk_smart_markers PRIMARY KEY CLUSTERED( - from_jid ASC, + lserver ASC, + from_luser ASC, to_jid ASC, thread ASC, type ASC diff --git a/priv/mysql.sql b/priv/mysql.sql index 734ca16121e..fca85a29794 100644 --- a/priv/mysql.sql +++ b/priv/mysql.sql @@ -493,16 +493,17 @@ CREATE TABLE mongoose_cluster_id ( ); CREATE TABLE smart_markers ( - from_jid VARCHAR(250) NOT NULL, - to_jid VARCHAR(250) NOT NULL, - thread VARCHAR(250) NOT NULL, + lserver VARBINARY(255) NOT NULL, + from_luser VARBINARY(1023) NOT NULL, + to_jid VARBINARY(1542) NOT NULL, + thread VARBINARY(250) NOT NULL, -- 'R' - received -- 'D' - displayed -- 'A' - acknowledged type ENUM('R', 'D', 'A') NOT NULL, - msg_id VARCHAR(250) NOT NULL, + msg_id VARBINARY(250) NOT NULL, timestamp BIGINT NOT NULL, - PRIMARY KEY(from_jid, to_jid, thread, type) + PRIMARY KEY(lserver, from_luser, to_jid, thread, type) ) CHARACTER SET utf8mb4; CREATE INDEX i_smart_markers USING BTREE ON smart_markers(to_jid, thread); diff --git a/priv/pg.sql b/priv/pg.sql index f9d57eeffd9..3b43b2fd47d 100644 --- a/priv/pg.sql +++ b/priv/pg.sql @@ -455,13 +455,14 @@ CREATE TABLE mongoose_cluster_id ( CREATE TYPE chat_marker_type AS ENUM('R', 'D', 'A'); CREATE TABLE smart_markers ( - from_jid VARCHAR(250) NOT NULL, + lserver VARCHAR(250) NOT NULL, + from_luser VARCHAR(250) NOT NULL, to_jid VARCHAR(250) NOT NULL, thread VARCHAR(250) NOT NULL, type chat_marker_type NOT NULL, msg_id VARCHAR(250) NOT NULL, timestamp BIGINT NOT NULL, - PRIMARY KEY(from_jid, to_jid, thread, type) + PRIMARY KEY(lserver, from_luser, to_jid, thread, type) ); CREATE INDEX i_smart_markers ON smart_markers(to_jid, thread); diff --git a/src/smart_markers/mod_smart_markers.erl b/src/smart_markers/mod_smart_markers.erl index d9f01f9e679..6dcf4dd0b63 100644 --- a/src/smart_markers/mod_smart_markers.erl +++ b/src/smart_markers/mod_smart_markers.erl @@ -69,11 +69,8 @@ -export([get_chat_markers/3]). %% Hook handlers --export([user_send_packet/4]). - --ignore_xref([ - behaviour_info/1, user_send_packet/4 -]). +-export([user_send_packet/4, remove_domain/3]). +-ignore_xref([user_send_packet/4, remove_domain/3]). %%-------------------------------------------------------------------- %% Type declarations @@ -111,7 +108,8 @@ supported_features() -> %%-------------------------------------------------------------------- -spec hooks(mongooseim:host_type()) -> [ejabberd_hooks:hook()]. hooks(HostType) -> - [{user_send_packet, HostType, ?MODULE, user_send_packet, 90}]. + [{user_send_packet, HostType, ?MODULE, user_send_packet, 90}, + {remove_domain, HostType, ?MODULE, remove_domain, 60}]. -spec user_send_packet(mongoose_acc:t(), jid:jid(), jid:jid(), exml:element()) -> mongoose_acc:t(). @@ -124,6 +122,13 @@ user_send_packet(Acc, From, To, Packet = #xmlel{name = <<"message">>}) -> user_send_packet(Acc, _From, _To, _Packet) -> Acc. +-spec remove_domain(mongoose_hooks:simple_acc(), + mongooseim:host_type(), jid:lserver()) -> + mongoose_hooks:simple_acc(). +remove_domain(Acc, HostType, Domain) -> + mod_smart_markers_backend:remove_domain(HostType, Domain), + Acc. + %%-------------------------------------------------------------------- %% Other API %%-------------------------------------------------------------------- @@ -167,11 +172,16 @@ is_valid_host(Acc, From, To) -> -spec extract_chat_markers(mongoose_acc:t(), jid:jid(), jid:jid(), exml:element()) -> [chat_marker()]. extract_chat_markers(Acc, From, To, Packet) -> - TS = mongoose_acc:timestamp(Acc), case mongoose_chat_markers:list_chat_markers(Packet) of [] -> []; ChatMarkers -> - CM = #{from => From, to => To, thread => get_thread(Packet), timestamp => TS}, + TS = mongoose_acc:timestamp(Acc), + CM = #{from => From, + to => To, + thread => get_thread(Packet), + timestamp => TS, + type => undefined, + id => undefined}, [CM#{type => Type, id => Id} || {Type, Id} <- ChatMarkers] end. diff --git a/src/smart_markers/mod_smart_markers_backend.erl b/src/smart_markers/mod_smart_markers_backend.erl index a8db0dd7bff..98f7bd3ed3c 100644 --- a/src/smart_markers/mod_smart_markers_backend.erl +++ b/src/smart_markers/mod_smart_markers_backend.erl @@ -5,13 +5,14 @@ -export([init/2]). -export([update_chat_marker/2]). -export([get_chat_markers/4]). +-export([remove_domain/2]). %%-------------------------------------------------------------------- %% DB backend behaviour definition %%-------------------------------------------------------------------- -callback init(mongooseim:host_type(), gen_mod:module_opts()) -> ok. -%%% 'from', 'to', 'thread' and 'type' keys of the ChatMarker map serve +%%% 'domain', from', 'to', 'thread' and 'type' keys of the ChatMarker map serve %%% as a composite database key. If key is not available in the database, %%% then chat marker must be added. Otherwise this function must update %%% chat marker record for that composite key. @@ -25,6 +26,7 @@ Timestamp :: integer()) -> [mod_smart_markers:chat_marker()]. +-callback remove_domain(mongooseim:host_type(), jid:lserver()) -> term(). -spec init(mongooseim:host_type(), gen_mod:module_opts()) -> ok. init(HostType, Opts) -> @@ -48,6 +50,12 @@ get_chat_markers(HostType, To, Thread, TS) -> Args = [HostType, To, Thread, TS], mongoose_backend:call_tracked(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args). +%% @doc remove all entries for a given domain +-spec remove_domain(mongooseim:host_type(), jid:lserver()) -> term(). +remove_domain(HostType, Domain) -> + Args = [HostType, Domain], + mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args). + add_default_backend(Opts) -> case lists:keyfind(backend, 2, Opts) of false -> [{backend, rdbms} | Opts]; diff --git a/src/smart_markers/mod_smart_markers_rdbms.erl b/src/smart_markers/mod_smart_markers_rdbms.erl index ec8803251f8..b847a565b3e 100644 --- a/src/smart_markers/mod_smart_markers_rdbms.erl +++ b/src/smart_markers/mod_smart_markers_rdbms.erl @@ -8,23 +8,27 @@ -author("denysgonchar"). -behavior(mod_smart_markers_backend). +-include("jlib.hrl"). + -export([init/2, update_chat_marker/2, get_chat_markers/4]). +-export([remove_domain/2]). %%-------------------------------------------------------------------- %% API %%-------------------------------------------------------------------- -spec init(mongooseim:host_type(), gen_mod:module_opts()) -> ok. init(HostType, _) -> - KeyFields = [<<"from_jid">>, <<"to_jid">>, <<"thread">>, <<"type">>], + KeyFields = [<<"lserver">>, <<"from_luser">>, <<"to_jid">>, <<"thread">>, <<"type">>], UpdateFields = [<<"msg_id">>, <<"timestamp">>], InsertFields = KeyFields ++ UpdateFields, - QueryName = smart_markers_upsert, - rdbms_queries:prepare_upsert(HostType, QueryName, smart_markers, + rdbms_queries:prepare_upsert(HostType, smart_markers_upsert, smart_markers, InsertFields, UpdateFields, KeyFields), mongoose_rdbms:prepare(smart_markers_select, smart_markers, [to_jid, thread, timestamp], - <<"SELECT from_jid, to_jid, thread, type, msg_id, timestamp FROM smart_markers " + <<"SELECT lserver, from_luser, type, msg_id, timestamp FROM smart_markers " "WHERE to_jid = ? AND thread = ? AND timestamp >= ?">>), + mongoose_rdbms:prepare(markers_remove_domain, smart_markers, + [lserver], <<"DELETE FROM smart_markers WHERE lserver=?">>), ok. %%% @doc @@ -35,8 +39,18 @@ init(HostType, _) -> %%% @end -spec update_chat_marker(mongooseim:host_type(), mod_smart_markers:chat_marker()) -> ok. -update_chat_marker(HostType, ChatMarker) -> - do_update_chat_marker(HostType, ChatMarker). +update_chat_marker(HostType, #{from := #jid{luser = LU, lserver = LS}, + to := To, thread := Thread, + type := Type, timestamp := TS, id := Id}) -> + ToEncoded = encode_jid(To), + ThreadEncoded = encode_thread(Thread), + TypeEncoded = encode_type(Type), + KeyValues = [LS, LU, ToEncoded, ThreadEncoded, TypeEncoded], + UpdateValues = [Id, TS], + InsertValues = KeyValues ++ UpdateValues, + Res = rdbms_queries:execute_upsert(HostType, smart_markers_upsert, + InsertValues, UpdateValues, KeyValues), + ok = check_upsert_result(Res). %%% @doc %%% This function must return the latest chat markers sent to the @@ -47,40 +61,25 @@ update_chat_marker(HostType, ChatMarker) -> Thread :: mod_smart_markers:maybe_thread(), Timestamp :: integer()) -> [mod_smart_markers:chat_marker()]. get_chat_markers(HostType, To, Thread, TS) -> - do_get_chat_markers(HostType, To, Thread, TS). + {selected, ChatMarkers} = mongoose_rdbms:execute_successfully( + HostType, smart_markers_select, + [encode_jid(To), encode_thread(Thread), TS]), + [ #{from => jid:make_noprep(CLUser, CLServer, <<>>), + to => To, + thread => Thread, + type => decode_type(CType), + timestamp => decode_timestamp(CTS), + id => CMsgId} + || {CLServer, CLUser, CType, CMsgId, CTS} <- ChatMarkers]. + + +-spec remove_domain(mongooseim:host_type(), jid:lserver()) -> mongoose_rdbms:query_result(). +remove_domain(HostType, Domain) -> + mongoose_rdbms:execute_successfully(HostType, markers_remove_domain, [Domain]). %%-------------------------------------------------------------------- %% local functions %%-------------------------------------------------------------------- --spec execute_select_chat_markers(HostType :: mongooseim:host_type(), - To :: binary(), - Thread :: binary(), - Timestamp :: integer()) -> - mongoose_rdbms:query_result(). -execute_select_chat_markers(HostType, To, Thread, Timestamp) -> - mongoose_rdbms:execute_successfully(HostType, smart_markers_select, - [To, Thread, Timestamp]). - -do_update_chat_marker(HostType, #{from := From, to := To, thread := Thread, - type := Type, timestamp := TS, id := Id}) -> - FromEncoded = encode_jid(From), - ToEncoded = encode_jid(To), - ThreadEncoded = encode_thread(Thread), - TypeEncoded = encode_type(Type), - KeyValues = [FromEncoded, ToEncoded, ThreadEncoded, TypeEncoded], - UpdateValues = [Id, TS], - InsertValues = KeyValues ++ UpdateValues, - Res = rdbms_queries:execute_upsert(HostType, smart_markers_upsert, - InsertValues, UpdateValues, KeyValues), - ok = check_upsert_result(Res). - -do_get_chat_markers(HostType, To, Thread, TS) -> - {selected, ChatMarkers} = execute_select_chat_markers(HostType, - encode_jid(To), - encode_thread(Thread), - TS), - decode(ChatMarkers). - encode_jid(JID) -> jid:to_binary(jid:to_lus(JID)). encode_thread(undefined) -> <<>>; @@ -97,22 +96,6 @@ check_upsert_result({updated, 2}) -> ok; check_upsert_result(Result) -> {error, {bad_result, Result}}. -decode(ChatMarkersList) -> - [decode_record(ChatMarker) || ChatMarker <- ChatMarkersList]. - -decode_record({From, To, Thread, Type, Id, TS}) -> - #{from => decode_jid(From), - to => decode_jid(To), - thread => decode_thread(Thread), - type => decode_type(Type), - timestamp => decode_timestamp(TS), - id => Id}. - -decode_jid(EncodedJID) -> jid:from_binary(EncodedJID). - -decode_thread(<<>>) -> undefined; -decode_thread(Thread) -> Thread. - decode_type(<<"R">>) -> received; decode_type(<<"D">>) -> displayed; decode_type(<<"A">>) -> acknowledged. From c81a19f39746cad0f1b3f8b3313870dcc021f7f6 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Mon, 14 Feb 2022 18:52:04 +0100 Subject: [PATCH 2/5] Implement remove_user and some basic test suite --- big_tests/default.spec | 1 + big_tests/dynamic_domains.spec | 1 + big_tests/tests/smart_markers_SUITE.erl | 99 +++++++++++++++++++ src/smart_markers/mod_smart_markers.erl | 10 +- .../mod_smart_markers_backend.erl | 9 ++ src/smart_markers/mod_smart_markers_rdbms.erl | 8 +- 6 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 big_tests/tests/smart_markers_SUITE.erl diff --git a/big_tests/default.spec b/big_tests/default.spec index 5d79ee0252a..defbfa2c10e 100644 --- a/big_tests/default.spec +++ b/big_tests/default.spec @@ -78,6 +78,7 @@ {suites, "tests", service_mongoose_system_metrics_SUITE}. {suites, "tests", shared_roster_SUITE}. {suites, "tests", sic_SUITE}. +{suites, "tests", smart_markers_SUITE}. {suites, "tests", sm_SUITE}. {suites, "tests", users_api_SUITE}. {suites, "tests", vcard_SUITE}. diff --git a/big_tests/dynamic_domains.spec b/big_tests/dynamic_domains.spec index ef826617d46..083038c16a1 100644 --- a/big_tests/dynamic_domains.spec +++ b/big_tests/dynamic_domains.spec @@ -125,6 +125,7 @@ {suites, "tests", sic_SUITE}. +{suites, "tests", smart_markers_SUITE}. {suites, "tests", sm_SUITE}. {suites, "tests", users_api_SUITE}. {suites, "tests", vcard_SUITE}. diff --git a/big_tests/tests/smart_markers_SUITE.erl b/big_tests/tests/smart_markers_SUITE.erl new file mode 100644 index 00000000000..8e137ea203d --- /dev/null +++ b/big_tests/tests/smart_markers_SUITE.erl @@ -0,0 +1,99 @@ +-module(smart_markers_SUITE). +-compile([export_all, nowarn_export_all]). + +-import(distributed_helper, [mim/0, rpc/4, subhost_pattern/1]). +-import(domain_helper, [host_type/0]). + +%%% Suite configuration +all() -> + case (not ct_helper:is_ct_running()) + orelse mongoose_helper:is_rdbms_enabled(host_type()) of + true -> all_cases(); + false -> {skip, require_rdbms} + end. + +all_cases() -> + [ + {group, one2one} + ]. + +groups() -> + [ + {one2one, [], + [ + marker_is_stored, + remove_markers_when_removed_user + ]} + ]. + +suite() -> + escalus:suite(). + +init_per_suite(Config0) -> + HostType = host_type(), + Config1 = dynamic_modules:save_modules(HostType, Config0), + dynamic_modules:ensure_modules(HostType, [{mod_smart_markers, [{backend, rdbms}]}]), + escalus:init_per_suite(Config1). + +end_per_suite(Config) -> + escalus_fresh:clean(), + dynamic_modules:restore_modules(Config), + escalus:end_per_suite(Config). + +init_per_group(_, Config) -> + Config. + +end_per_group(_, Config) -> + Config. + +init_per_testcase(Name, Config) -> + escalus:init_per_testcase(Name, Config). +end_per_testcase(Name, Config) -> + escalus:end_per_testcase(Name, Config). + +%%% tests +marker_is_stored(Config) -> + escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> + send_message_respond_marker(Alice, Bob), + AliceJid = jid:from_binary(escalus_client:full_jid(Alice)), + BobJid = jid:from_binary(escalus_client:full_jid(Bob)), + mongoose_helper:wait_until( + fun() -> length(fetch_markers_for_users(BobJid, AliceJid)) > 0 end, true) + end). + +remove_markers_when_removed_user(Config) -> + escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> + Body = <<"Hello Bob!">>, + MsgId = escalus_stanza:id(), + Msg = escalus_stanza:set_id(escalus_stanza:chat_to(Bob, Body), MsgId), + escalus:send(Alice, Msg), + escalus:wait_for_stanza(Bob), + ChatMarker = escalus_stanza:chat_marker(Alice, <<"displayed">>, MsgId), + escalus:send(Bob, ChatMarker), + escalus:wait_for_stanza(Alice), + AliceJid = jid:from_binary(escalus_client:full_jid(Alice)), + BobJid = jid:from_binary(escalus_client:full_jid(Bob)), + mongoose_helper:wait_until(fun() -> length(fetch_markers_for_users(BobJid, AliceJid)) > 0 end, true), + unregister_user(Bob), + mongoose_helper:wait_until(fun() -> length(fetch_markers_for_users(BobJid, AliceJid)) end, 0) + end). + +%%% helpers +fetch_markers_for_users(From, To) -> + MRs = rpc(mim(), mod_smart_markers_backend, get_chat_markers, + [host_type(), To, undefined, 0]), + [MR || #{from := FR} = MR <- MRs, jid:are_bare_equal(From, FR)]. + +send_message_respond_marker(MsgWriter, MarkerAnswerer) -> + Body = <<"Hello">>, + MsgId = escalus_stanza:id(), + Msg = escalus_stanza:set_id(escalus_stanza:chat_to(MarkerAnswerer, Body), MsgId), + escalus:send(MsgWriter, Msg), + escalus:wait_for_stanza(MarkerAnswerer), + ChatMarker = escalus_stanza:chat_marker(MsgWriter, <<"displayed">>, MsgId), + escalus:send(MarkerAnswerer, ChatMarker), + escalus:wait_for_stanza(MsgWriter). + +unregister_user(Client) -> + Jid = jid:from_binary(escalus_client:short_jid(Client)), + rpc(mim(), ejabberd_auth, remove_user, [Jid]). diff --git a/src/smart_markers/mod_smart_markers.erl b/src/smart_markers/mod_smart_markers.erl index 6dcf4dd0b63..da4666774db 100644 --- a/src/smart_markers/mod_smart_markers.erl +++ b/src/smart_markers/mod_smart_markers.erl @@ -69,8 +69,8 @@ -export([get_chat_markers/3]). %% Hook handlers --export([user_send_packet/4, remove_domain/3]). --ignore_xref([user_send_packet/4, remove_domain/3]). +-export([user_send_packet/4, remove_user/3, remove_domain/3]). +-ignore_xref([user_send_packet/4, remove_user/3, remove_domain/3]). %%-------------------------------------------------------------------- %% Type declarations @@ -109,6 +109,7 @@ supported_features() -> -spec hooks(mongooseim:host_type()) -> [ejabberd_hooks:hook()]. hooks(HostType) -> [{user_send_packet, HostType, ?MODULE, user_send_packet, 90}, + {remove_user, HostType, ?MODULE, remove_user, 60}, {remove_domain, HostType, ?MODULE, remove_domain, 60}]. -spec user_send_packet(mongoose_acc:t(), jid:jid(), jid:jid(), exml:element()) -> @@ -122,6 +123,11 @@ user_send_packet(Acc, From, To, Packet = #xmlel{name = <<"message">>}) -> user_send_packet(Acc, _From, _To, _Packet) -> Acc. +remove_user(Acc, User, Server) -> + HostType = mongoose_acc:host_type(Acc), + mod_smart_markers_backend:remove_user(HostType, jid:make_bare(User, Server)), + Acc. + -spec remove_domain(mongoose_hooks:simple_acc(), mongooseim:host_type(), jid:lserver()) -> mongoose_hooks:simple_acc(). diff --git a/src/smart_markers/mod_smart_markers_backend.erl b/src/smart_markers/mod_smart_markers_backend.erl index 98f7bd3ed3c..52481f519b9 100644 --- a/src/smart_markers/mod_smart_markers_backend.erl +++ b/src/smart_markers/mod_smart_markers_backend.erl @@ -6,6 +6,7 @@ -export([update_chat_marker/2]). -export([get_chat_markers/4]). -export([remove_domain/2]). +-export([remove_user/2]). %%-------------------------------------------------------------------- %% DB backend behaviour definition @@ -28,6 +29,8 @@ -callback remove_domain(mongooseim:host_type(), jid:lserver()) -> term(). +-callback remove_user(mongooseim:host_type(), jid:jid()) -> term(). + -spec init(mongooseim:host_type(), gen_mod:module_opts()) -> ok. init(HostType, Opts) -> FOpts = add_default_backend(Opts), @@ -56,6 +59,12 @@ remove_domain(HostType, Domain) -> Args = [HostType, Domain], mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args). +%% @doc remove all stored interactions with a given user +-spec remove_user(mongooseim:host_type(), jid:jid()) -> term(). +remove_user(HostType, User) -> + Args = [HostType, User], + mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args). + add_default_backend(Opts) -> case lists:keyfind(backend, 2, Opts) of false -> [{backend, rdbms} | Opts]; diff --git a/src/smart_markers/mod_smart_markers_rdbms.erl b/src/smart_markers/mod_smart_markers_rdbms.erl index b847a565b3e..82c16747808 100644 --- a/src/smart_markers/mod_smart_markers_rdbms.erl +++ b/src/smart_markers/mod_smart_markers_rdbms.erl @@ -11,7 +11,7 @@ -include("jlib.hrl"). -export([init/2, update_chat_marker/2, get_chat_markers/4]). --export([remove_domain/2]). +-export([remove_domain/2, remove_user/2]). %%-------------------------------------------------------------------- %% API @@ -29,6 +29,8 @@ init(HostType, _) -> "WHERE to_jid = ? AND thread = ? AND timestamp >= ?">>), mongoose_rdbms:prepare(markers_remove_domain, smart_markers, [lserver], <<"DELETE FROM smart_markers WHERE lserver=?">>), + mongoose_rdbms:prepare(markers_remove_user, smart_markers, + [lserver, from_luser], <<"DELETE FROM smart_markers WHERE lserver=? AND from_luser=?">>), ok. %%% @doc @@ -77,6 +79,10 @@ get_chat_markers(HostType, To, Thread, TS) -> remove_domain(HostType, Domain) -> mongoose_rdbms:execute_successfully(HostType, markers_remove_domain, [Domain]). +-spec remove_user(mongooseim:host_type(), jid:jid()) -> mongoose_rdbms:query_result(). +remove_user(HostType, #jid{luser = LU, lserver = LS}) -> + mongoose_rdbms:execute_successfully(HostType, markers_remove_user, [LS, LU]). + %%-------------------------------------------------------------------- %% local functions %%-------------------------------------------------------------------- From 71294fefb8631ec425495b43c5dac53ddfd583b3 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Tue, 15 Feb 2022 16:07:14 +0100 Subject: [PATCH 3/5] Implement forget_room --- big_tests/tests/smart_markers_SUITE.erl | 92 +++++++++++++++++-- src/smart_markers/mod_smart_markers.erl | 14 ++- .../mod_smart_markers_backend.erl | 11 +++ src/smart_markers/mod_smart_markers_rdbms.erl | 8 +- 4 files changed, 112 insertions(+), 13 deletions(-) diff --git a/big_tests/tests/smart_markers_SUITE.erl b/big_tests/tests/smart_markers_SUITE.erl index 8e137ea203d..d7dfbf44a2e 100644 --- a/big_tests/tests/smart_markers_SUITE.erl +++ b/big_tests/tests/smart_markers_SUITE.erl @@ -1,6 +1,8 @@ -module(smart_markers_SUITE). -compile([export_all, nowarn_export_all]). +-include("muc_light.hrl"). + -import(distributed_helper, [mim/0, rpc/4, subhost_pattern/1]). -import(domain_helper, [host_type/0]). @@ -14,7 +16,8 @@ all() -> all_cases() -> [ - {group, one2one} + {group, one2one}, + {group, muclight} ]. groups() -> @@ -23,27 +26,41 @@ groups() -> [ marker_is_stored, remove_markers_when_removed_user + ]}, + {muclight, [], + [ + marker_is_stored_for_room, + markers_are_removed_when_room_is_removed ]} ]. suite() -> escalus:suite(). -init_per_suite(Config0) -> - HostType = host_type(), - Config1 = dynamic_modules:save_modules(HostType, Config0), - dynamic_modules:ensure_modules(HostType, [{mod_smart_markers, [{backend, rdbms}]}]), - escalus:init_per_suite(Config1). +init_per_suite(Config) -> + escalus:init_per_suite(Config). end_per_suite(Config) -> - escalus_fresh:clean(), - dynamic_modules:restore_modules(Config), escalus:end_per_suite(Config). -init_per_group(_, Config) -> +init_per_group(GroupName, Config) -> + ok = dynamic_modules:ensure_modules(host_type(), group_to_module(GroupName)), Config. +group_to_module(one2one) -> + [{mod_smart_markers, [{backend, rdbms}]}]; +group_to_module(muclight) -> + [{mod_smart_markers, [{backend, rdbms}]}, + {mod_muc_light, + [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}, + {backend, rdbms}]}]. + +end_per_group(muclight, Config) -> + muc_light_helper:clear_db(host_type()), + end_per_group(generic, Config); end_per_group(_, Config) -> + escalus_fresh:clean(), + dynamic_modules:restore_modules(Config), Config. init_per_testcase(Name, Config) -> @@ -78,12 +95,69 @@ remove_markers_when_removed_user(Config) -> mongoose_helper:wait_until(fun() -> length(fetch_markers_for_users(BobJid, AliceJid)) end, 0) end). +marker_is_stored_for_room(Config) -> + escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], + fun(Alice, Bob, Kate) -> + Users = [Alice, Bob, Kate], + RoomId = create_room(Alice, [Bob, Kate], Config), + RoomBinJid = muc_light_helper:room_bin_jid(RoomId), + one_marker_in_room(Users, RoomBinJid, Alice, Bob), + BobJid = jid:from_binary(escalus_client:full_jid(Bob)), + mongoose_helper:wait_until( + fun() -> length(fetch_markers_for_users(BobJid, jid:from_binary(RoomBinJid))) > 0 end, true) + end). + +markers_are_removed_when_room_is_removed(Config) -> + escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> + Users = [Alice, Bob], + RoomId = create_room(Alice, [Bob], Config), + RoomBinJid = muc_light_helper:room_bin_jid(RoomId), + RoomJid = jid:from_binary(RoomBinJid), + one_marker_in_room(Users, RoomBinJid, Alice, Bob), + BobJid = jid:from_binary(escalus_client:full_jid(Bob)), + mongoose_helper:wait_until( + fun() -> length(fetch_markers_for_users(BobJid, RoomJid)) > 0 end, true), + %% The room is then deleted + delete_room(Alice, Users, RoomBinJid), + [ begin + Jid = jid:from_binary(escalus_client:full_jid(User)), + mongoose_helper:wait_until( + fun() -> length(fetch_markers_for_users(Jid, RoomJid)) end, 0) + end || User <- Users] + end). + %%% helpers fetch_markers_for_users(From, To) -> MRs = rpc(mim(), mod_smart_markers_backend, get_chat_markers, [host_type(), To, undefined, 0]), [MR || #{from := FR} = MR <- MRs, jid:are_bare_equal(From, FR)]. +create_room(Owner, Members, Config) -> + RoomId = muc_helper:fresh_room_name(), + MucHost = muc_light_helper:muc_host(), + muc_light_helper:create_room(RoomId, MucHost, Owner, Members, Config, muc_light_helper:ver(1)), + RoomId. + +delete_room(Owner, Users, RoomBinJid) -> + Destroy = escalus_stanza:to(escalus_stanza:iq_set(?NS_MUC_LIGHT_DESTROY, []), RoomBinJid), + escalus:send(Owner, Destroy), + AffUsersChanges = [{User, none} || User <- Users ], + muc_light_helper:verify_aff_bcast([], AffUsersChanges, [?NS_MUC_LIGHT_DESTROY]), + escalus:assert(is_iq_result, escalus:wait_for_stanza(Owner)). + +one_marker_in_room(Users, RoomBinJid, Writer, Marker) -> + MsgId = escalus_stanza:id(), + Msg = escalus_stanza:set_id( + escalus_stanza:groupchat_to(RoomBinJid, <<"Hello">>), MsgId), + escalus:send(Writer, Msg), + [ escalus:wait_for_stanza(User) || User <- Users], + ChatMarker = escalus_stanza:setattr( + escalus_stanza:chat_marker(RoomBinJid, <<"displayed">>, MsgId), + <<"type">>, <<"groupchat">>), + escalus:send(Marker, ChatMarker), + [ escalus:wait_for_stanza(User) || User <- Users], + MsgId. + send_message_respond_marker(MsgWriter, MarkerAnswerer) -> Body = <<"Hello">>, MsgId = escalus_stanza:id(), diff --git a/src/smart_markers/mod_smart_markers.erl b/src/smart_markers/mod_smart_markers.erl index da4666774db..6918b1d4a4d 100644 --- a/src/smart_markers/mod_smart_markers.erl +++ b/src/smart_markers/mod_smart_markers.erl @@ -69,8 +69,8 @@ -export([get_chat_markers/3]). %% Hook handlers --export([user_send_packet/4, remove_user/3, remove_domain/3]). --ignore_xref([user_send_packet/4, remove_user/3, remove_domain/3]). +-export([user_send_packet/4, remove_user/3, remove_domain/3, forget_room/4]). +-ignore_xref([user_send_packet/4, remove_user/3, remove_domain/3, forget_room/4]). %%-------------------------------------------------------------------- %% Type declarations @@ -110,7 +110,9 @@ supported_features() -> hooks(HostType) -> [{user_send_packet, HostType, ?MODULE, user_send_packet, 90}, {remove_user, HostType, ?MODULE, remove_user, 60}, - {remove_domain, HostType, ?MODULE, remove_domain, 60}]. + {remove_domain, HostType, ?MODULE, remove_domain, 60}, + {forget_room, HostType, ?MODULE, forget_room, 85} + ]. -spec user_send_packet(mongoose_acc:t(), jid:jid(), jid:jid(), exml:element()) -> mongoose_acc:t(). @@ -135,6 +137,12 @@ remove_domain(Acc, HostType, Domain) -> mod_smart_markers_backend:remove_domain(HostType, Domain), Acc. +-spec forget_room(mongoose_hooks:simple_acc(), mongooseim:host_type(), jid:lserver(), jid:luser()) -> + mongoose_hooks:simple_acc(). +forget_room(Acc, HostType, RoomS, RoomU) -> + mod_smart_markers_backend:remove_to(HostType, jid:make_noprep(RoomU, RoomS, <<>>)), + Acc. + %%-------------------------------------------------------------------- %% Other API %%-------------------------------------------------------------------- diff --git a/src/smart_markers/mod_smart_markers_backend.erl b/src/smart_markers/mod_smart_markers_backend.erl index 52481f519b9..fa2f30a54ef 100644 --- a/src/smart_markers/mod_smart_markers_backend.erl +++ b/src/smart_markers/mod_smart_markers_backend.erl @@ -7,6 +7,7 @@ -export([get_chat_markers/4]). -export([remove_domain/2]). -export([remove_user/2]). +-export([remove_to/2]). %%-------------------------------------------------------------------- %% DB backend behaviour definition @@ -31,6 +32,8 @@ -callback remove_user(mongooseim:host_type(), jid:jid()) -> term(). +-callback remove_to(mongooseim:host_type(), jid:jid()) -> term(). + -spec init(mongooseim:host_type(), gen_mod:module_opts()) -> ok. init(HostType, Opts) -> FOpts = add_default_backend(Opts), @@ -65,6 +68,14 @@ remove_user(HostType, User) -> Args = [HostType, User], mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args). +%% @doc remove all markers a user has ever been sent +%% Useful for example for `forget_room', when we need to drop all knowledge +%% other users had of this room +-spec remove_to(mongooseim:host_type(), jid:jid()) -> term(). +remove_to(HostType, To) -> + Args = [HostType, To], + mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args). + add_default_backend(Opts) -> case lists:keyfind(backend, 2, Opts) of false -> [{backend, rdbms} | Opts]; diff --git a/src/smart_markers/mod_smart_markers_rdbms.erl b/src/smart_markers/mod_smart_markers_rdbms.erl index 82c16747808..2dbc12c0a27 100644 --- a/src/smart_markers/mod_smart_markers_rdbms.erl +++ b/src/smart_markers/mod_smart_markers_rdbms.erl @@ -11,7 +11,7 @@ -include("jlib.hrl"). -export([init/2, update_chat_marker/2, get_chat_markers/4]). --export([remove_domain/2, remove_user/2]). +-export([remove_domain/2, remove_user/2, remove_to/2]). %%-------------------------------------------------------------------- %% API @@ -31,6 +31,8 @@ init(HostType, _) -> [lserver], <<"DELETE FROM smart_markers WHERE lserver=?">>), mongoose_rdbms:prepare(markers_remove_user, smart_markers, [lserver, from_luser], <<"DELETE FROM smart_markers WHERE lserver=? AND from_luser=?">>), + mongoose_rdbms:prepare(markers_remove_to, smart_markers, + [to_jid], <<"DELETE FROM smart_markers WHERE to_jid=?">>), ok. %%% @doc @@ -83,6 +85,10 @@ remove_domain(HostType, Domain) -> remove_user(HostType, #jid{luser = LU, lserver = LS}) -> mongoose_rdbms:execute_successfully(HostType, markers_remove_user, [LS, LU]). +-spec remove_to(mongooseim:host_type(), jid:jid()) -> mongoose_rdbms:query_result(). +remove_to(HostType, To) -> + mongoose_rdbms:execute_successfully(HostType, markers_remove_to, [encode_jid(To)]). + %%-------------------------------------------------------------------- %% local functions %%-------------------------------------------------------------------- From b00e0c222c49c36c8f940de511c0028493f82b6d Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Tue, 15 Feb 2022 16:21:49 +0100 Subject: [PATCH 4/5] Implement remove marker on user leaves room --- big_tests/tests/smart_markers_SUITE.erl | 18 +++++++++++ src/smart_markers/mod_smart_markers.erl | 31 +++++++++++++++++-- .../mod_smart_markers_backend.erl | 10 ++++++ src/smart_markers/mod_smart_markers_rdbms.erl | 10 +++++- 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/big_tests/tests/smart_markers_SUITE.erl b/big_tests/tests/smart_markers_SUITE.erl index d7dfbf44a2e..ed2d395150b 100644 --- a/big_tests/tests/smart_markers_SUITE.erl +++ b/big_tests/tests/smart_markers_SUITE.erl @@ -30,6 +30,7 @@ groups() -> {muclight, [], [ marker_is_stored_for_room, + marker_is_removed_when_user_leaves_room, markers_are_removed_when_room_is_removed ]} ]. @@ -107,6 +108,23 @@ marker_is_stored_for_room(Config) -> fun() -> length(fetch_markers_for_users(BobJid, jid:from_binary(RoomBinJid))) > 0 end, true) end). +marker_is_removed_when_user_leaves_room(Config) -> + escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], + fun(Alice, Bob) -> + Users = [Alice, Bob], + RoomId = create_room(Alice, [Bob], Config), + RoomBinJid = muc_light_helper:room_bin_jid(RoomId), + RoomJid = jid:from_binary(RoomBinJid), + one_marker_in_room(Users, RoomBinJid, Alice, Bob), + BobJid = jid:from_binary(escalus_client:full_jid(Bob)), + mongoose_helper:wait_until( + fun() -> length(fetch_markers_for_users(BobJid, RoomJid)) > 0 end, true), + % Remove Bob from the room + muc_light_helper:user_leave(RoomId, Bob, [Alice]), + mongoose_helper:wait_until( + fun() -> length(fetch_markers_for_users(BobJid, RoomJid)) end, 0) + end). + markers_are_removed_when_room_is_removed(Config) -> escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> Users = [Alice, Bob], diff --git a/src/smart_markers/mod_smart_markers.erl b/src/smart_markers/mod_smart_markers.erl index 6918b1d4a4d..d04dd8647df 100644 --- a/src/smart_markers/mod_smart_markers.erl +++ b/src/smart_markers/mod_smart_markers.erl @@ -56,6 +56,7 @@ -module(mod_smart_markers). -include("jlib.hrl"). +-include("mod_muc_light.hrl"). -xep([{xep, 333}, {version, "0.3"}]). -behaviour(gen_mod). @@ -69,8 +70,10 @@ -export([get_chat_markers/3]). %% Hook handlers --export([user_send_packet/4, remove_user/3, remove_domain/3, forget_room/4]). --ignore_xref([user_send_packet/4, remove_user/3, remove_domain/3, forget_room/4]). +-export([user_send_packet/4, remove_user/3, remove_domain/3, + forget_room/4, room_new_affiliations/4]). +-ignore_xref([user_send_packet/4, remove_user/3, remove_domain/3, + forget_room/4, room_new_affiliations/4]). %%-------------------------------------------------------------------- %% Type declarations @@ -111,7 +114,8 @@ hooks(HostType) -> [{user_send_packet, HostType, ?MODULE, user_send_packet, 90}, {remove_user, HostType, ?MODULE, remove_user, 60}, {remove_domain, HostType, ?MODULE, remove_domain, 60}, - {forget_room, HostType, ?MODULE, forget_room, 85} + {forget_room, HostType, ?MODULE, forget_room, 85}, + {room_new_affiliations, HostType, ?MODULE, room_new_affiliations, 60} ]. -spec user_send_packet(mongoose_acc:t(), jid:jid(), jid:jid(), exml:element()) -> @@ -143,6 +147,27 @@ forget_room(Acc, HostType, RoomS, RoomU) -> mod_smart_markers_backend:remove_to(HostType, jid:make_noprep(RoomU, RoomS, <<>>)), Acc. +%% The new affs can be found in the Acc:element, where we can scan for 'none' ones +-spec room_new_affiliations(mongoose_acc:t(), jid:jid(), mod_muc_light:aff_users(), binary()) -> + mongoose_acc:t(). +room_new_affiliations(Acc, RoomJid, _NewAffs, _NewVersion) -> + HostType = mod_muc_light_utils:acc_to_host_type(Acc), + case mongoose_acc:element(Acc) of + undefined -> Acc; + Packet -> + case exml_query:paths(Packet, [{element_with_ns, ?NS_MUC_LIGHT_AFFILIATIONS}, + {element_with_attr, <<"affiliation">>, <<"none">>}, + cdata]) of + [] -> Acc; + Users -> + [begin + FromJid = jid:to_bare(jid:from_binary(User)), + mod_smart_markers_backend:remove_to_for_user(HostType, FromJid, RoomJid) + end || User <- Users ], + Acc + end + end. + %%-------------------------------------------------------------------- %% Other API %%-------------------------------------------------------------------- diff --git a/src/smart_markers/mod_smart_markers_backend.erl b/src/smart_markers/mod_smart_markers_backend.erl index fa2f30a54ef..bad6fee68d5 100644 --- a/src/smart_markers/mod_smart_markers_backend.erl +++ b/src/smart_markers/mod_smart_markers_backend.erl @@ -8,6 +8,7 @@ -export([remove_domain/2]). -export([remove_user/2]). -export([remove_to/2]). +-export([remove_to_for_user/3]). %%-------------------------------------------------------------------- %% DB backend behaviour definition @@ -34,6 +35,8 @@ -callback remove_to(mongooseim:host_type(), jid:jid()) -> term(). +-callback remove_to_for_user(mongooseim:host_type(), From :: jid:jid(), To :: jid:jid()) -> term(). + -spec init(mongooseim:host_type(), gen_mod:module_opts()) -> ok. init(HostType, Opts) -> FOpts = add_default_backend(Opts), @@ -76,6 +79,13 @@ remove_to(HostType, To) -> Args = [HostType, To], mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args). +%% @doc remove markers a user sent to a given other +%% Useful for when a user leaves a room, but the room still exists +-spec remove_to_for_user(mongooseim:host_type(), From :: jid:jid(), To :: jid:jid()) -> term(). +remove_to_for_user(HostType, From, To) -> + Args = [HostType, From, To], + mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args). + add_default_backend(Opts) -> case lists:keyfind(backend, 2, Opts) of false -> [{backend, rdbms} | Opts]; diff --git a/src/smart_markers/mod_smart_markers_rdbms.erl b/src/smart_markers/mod_smart_markers_rdbms.erl index 2dbc12c0a27..7c3fde4cbc6 100644 --- a/src/smart_markers/mod_smart_markers_rdbms.erl +++ b/src/smart_markers/mod_smart_markers_rdbms.erl @@ -11,7 +11,7 @@ -include("jlib.hrl"). -export([init/2, update_chat_marker/2, get_chat_markers/4]). --export([remove_domain/2, remove_user/2, remove_to/2]). +-export([remove_domain/2, remove_user/2, remove_to/2, remove_to_for_user/3]). %%-------------------------------------------------------------------- %% API @@ -33,6 +33,9 @@ init(HostType, _) -> [lserver, from_luser], <<"DELETE FROM smart_markers WHERE lserver=? AND from_luser=?">>), mongoose_rdbms:prepare(markers_remove_to, smart_markers, [to_jid], <<"DELETE FROM smart_markers WHERE to_jid=?">>), + mongoose_rdbms:prepare(markers_remove_to_for_user, smart_markers, + [lserver, from_luser, to_jid], + <<"DELETE FROM smart_markers WHERE lserver=? AND from_luser=? AND to_jid=?">>), ok. %%% @doc @@ -89,6 +92,11 @@ remove_user(HostType, #jid{luser = LU, lserver = LS}) -> remove_to(HostType, To) -> mongoose_rdbms:execute_successfully(HostType, markers_remove_to, [encode_jid(To)]). +-spec remove_to_for_user(mongooseim:host_type(), From :: jid:jid(), To :: jid:jid()) -> + mongoose_rdbms:query_result(). +remove_to_for_user(HostType, #jid{luser = LU, lserver = LS}, To) -> + mongoose_rdbms:execute_successfully(HostType, markers_remove_to_for_user, [LS, LU, encode_jid(To)]). + %%-------------------------------------------------------------------- %% local functions %%-------------------------------------------------------------------- From 66595bdc363522a38752e432c1be17e214759dd3 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Thu, 24 Feb 2022 11:26:54 +0100 Subject: [PATCH 5/5] Rename DB columns to keep naming consistent --- priv/mssql2012.sql | 4 ++-- priv/mysql.sql | 4 ++-- priv/pg.sql | 4 ++-- src/smart_markers/mod_smart_markers_rdbms.erl | 10 +++++----- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/priv/mssql2012.sql b/priv/mssql2012.sql index 9a530f4ab2c..b37984275c5 100644 --- a/priv/mssql2012.sql +++ b/priv/mssql2012.sql @@ -687,7 +687,7 @@ CREATE TABLE muc_registered( -- but here we are limited by index size (900 bytes) CREATE TABLE smart_markers ( lserver NVARCHAR(100) NOT NULL, - from_luser NVARCHAR(150) NOT NULL, + luser NVARCHAR(150) NOT NULL, to_jid NVARCHAR(250) NOT NULL, thread NVARCHAR(80) NOT NULL, -- chat marker types: @@ -699,7 +699,7 @@ CREATE TABLE smart_markers ( timestamp BIGINT NOT NULL, CONSTRAINT pk_smart_markers PRIMARY KEY CLUSTERED( lserver ASC, - from_luser ASC, + luser ASC, to_jid ASC, thread ASC, type ASC diff --git a/priv/mysql.sql b/priv/mysql.sql index fca85a29794..2b915f368ff 100644 --- a/priv/mysql.sql +++ b/priv/mysql.sql @@ -494,7 +494,7 @@ CREATE TABLE mongoose_cluster_id ( CREATE TABLE smart_markers ( lserver VARBINARY(255) NOT NULL, - from_luser VARBINARY(1023) NOT NULL, + luser VARBINARY(1023) NOT NULL, to_jid VARBINARY(1542) NOT NULL, thread VARBINARY(250) NOT NULL, -- 'R' - received @@ -503,7 +503,7 @@ CREATE TABLE smart_markers ( type ENUM('R', 'D', 'A') NOT NULL, msg_id VARBINARY(250) NOT NULL, timestamp BIGINT NOT NULL, - PRIMARY KEY(lserver, from_luser, to_jid, thread, type) + PRIMARY KEY(lserver, luser, to_jid, thread, type) ) CHARACTER SET utf8mb4; CREATE INDEX i_smart_markers USING BTREE ON smart_markers(to_jid, thread); diff --git a/priv/pg.sql b/priv/pg.sql index 3b43b2fd47d..c55619124f0 100644 --- a/priv/pg.sql +++ b/priv/pg.sql @@ -456,13 +456,13 @@ CREATE TYPE chat_marker_type AS ENUM('R', 'D', 'A'); CREATE TABLE smart_markers ( lserver VARCHAR(250) NOT NULL, - from_luser VARCHAR(250) NOT NULL, + luser VARCHAR(250) NOT NULL, to_jid VARCHAR(250) NOT NULL, thread VARCHAR(250) NOT NULL, type chat_marker_type NOT NULL, msg_id VARCHAR(250) NOT NULL, timestamp BIGINT NOT NULL, - PRIMARY KEY(lserver, from_luser, to_jid, thread, type) + PRIMARY KEY(lserver, luser, to_jid, thread, type) ); CREATE INDEX i_smart_markers ON smart_markers(to_jid, thread); diff --git a/src/smart_markers/mod_smart_markers_rdbms.erl b/src/smart_markers/mod_smart_markers_rdbms.erl index 7c3fde4cbc6..1c409255152 100644 --- a/src/smart_markers/mod_smart_markers_rdbms.erl +++ b/src/smart_markers/mod_smart_markers_rdbms.erl @@ -18,24 +18,24 @@ %%-------------------------------------------------------------------- -spec init(mongooseim:host_type(), gen_mod:module_opts()) -> ok. init(HostType, _) -> - KeyFields = [<<"lserver">>, <<"from_luser">>, <<"to_jid">>, <<"thread">>, <<"type">>], + KeyFields = [<<"lserver">>, <<"luser">>, <<"to_jid">>, <<"thread">>, <<"type">>], UpdateFields = [<<"msg_id">>, <<"timestamp">>], InsertFields = KeyFields ++ UpdateFields, rdbms_queries:prepare_upsert(HostType, smart_markers_upsert, smart_markers, InsertFields, UpdateFields, KeyFields), mongoose_rdbms:prepare(smart_markers_select, smart_markers, [to_jid, thread, timestamp], - <<"SELECT lserver, from_luser, type, msg_id, timestamp FROM smart_markers " + <<"SELECT lserver, luser, type, msg_id, timestamp FROM smart_markers " "WHERE to_jid = ? AND thread = ? AND timestamp >= ?">>), mongoose_rdbms:prepare(markers_remove_domain, smart_markers, [lserver], <<"DELETE FROM smart_markers WHERE lserver=?">>), mongoose_rdbms:prepare(markers_remove_user, smart_markers, - [lserver, from_luser], <<"DELETE FROM smart_markers WHERE lserver=? AND from_luser=?">>), + [lserver, luser], <<"DELETE FROM smart_markers WHERE lserver=? AND luser=?">>), mongoose_rdbms:prepare(markers_remove_to, smart_markers, [to_jid], <<"DELETE FROM smart_markers WHERE to_jid=?">>), mongoose_rdbms:prepare(markers_remove_to_for_user, smart_markers, - [lserver, from_luser, to_jid], - <<"DELETE FROM smart_markers WHERE lserver=? AND from_luser=? AND to_jid=?">>), + [lserver, luser, to_jid], + <<"DELETE FROM smart_markers WHERE lserver=? AND luser=? AND to_jid=?">>), ok. %%% @doc