./ct_report/coverage/mod_smart_markers_rdbms.COVER.html

1 %%%----------------------------------------------------------------------------
2 %%% @doc
3 %%% RDBMS backend for mod_smart_markers
4 %%% @end
5 %%% @copyright (C) 2020-2022, Erlang Solutions Ltd.
6 %%%----------------------------------------------------------------------------
7 -module(mod_smart_markers_rdbms).
8 -author("denysgonchar").
9 -behavior(mod_smart_markers_backend).
10
11 -include("jlib.hrl").
12 -include("mongoose_logger.hrl").
13
14 -export([init/2, update_chat_marker/2, get_chat_markers/4]).
15 -export([get_conv_chat_marker/6]).
16 -export([remove_domain/2, remove_user/2, remove_to/2, remove_to_for_user/3]).
17 -export([encode_jid/1, encode_thread/1, encode_type/1, verify/2]).
18
19 %%--------------------------------------------------------------------
20 %% API
21 %%--------------------------------------------------------------------
22 -spec init(mongooseim:host_type(), gen_mod:module_opts()) -> ok.
23 init(HostType, _) ->
24
:-(
KeyFields = [<<"lserver">>, <<"luser">>, <<"to_jid">>, <<"thread">>, <<"type">>],
25
:-(
UpdateFields = [<<"msg_id">>, <<"timestamp">>],
26
:-(
InsertFields = KeyFields ++ UpdateFields,
27
:-(
rdbms_queries:prepare_upsert(HostType, smart_markers_upsert, smart_markers,
28 InsertFields, UpdateFields, KeyFields, <<"timestamp">>),
29
:-(
mongoose_rdbms:prepare(smart_markers_select_conv, smart_markers,
30 [lserver, luser, to_jid, thread, timestamp],
31 <<"SELECT lserver, luser, to_jid, thread, type, msg_id, timestamp FROM smart_markers "
32 "WHERE lserver = ? AND luser = ? AND to_jid = ? AND thread = ? AND timestamp >= ?">>),
33
:-(
mongoose_rdbms:prepare(smart_markers_select, smart_markers,
34 [to_jid, thread, timestamp],
35 <<"SELECT lserver, luser, type, msg_id, timestamp FROM smart_markers "
36 "WHERE to_jid = ? AND thread = ? AND timestamp >= ?">>),
37
:-(
mongoose_rdbms:prepare(markers_remove_domain, smart_markers,
38 [lserver], <<"DELETE FROM smart_markers WHERE lserver=?">>),
39
:-(
mongoose_rdbms:prepare(markers_remove_user, smart_markers,
40 [lserver, luser], <<"DELETE FROM smart_markers WHERE lserver=? AND luser=?">>),
41
:-(
mongoose_rdbms:prepare(markers_remove_to, smart_markers,
42 [to_jid], <<"DELETE FROM smart_markers WHERE to_jid=?">>),
43
:-(
mongoose_rdbms:prepare(markers_remove_to_for_user, smart_markers,
44 [lserver, luser, to_jid],
45 <<"DELETE FROM smart_markers WHERE lserver=? AND luser=? AND to_jid=?">>),
46
:-(
ok.
47
48 %%% @doc
49 %%% 'from', 'to', 'thread' and 'type' keys of the ChatMarker map serve
50 %%% as a composite database key. If key is not available in the database,
51 %%% then chat marker must be added. Otherwise this function must update
52 %%% chat marker record for that composite key.
53 %%% @end
54 -spec update_chat_marker(mongooseim:host_type(),
55 mod_smart_markers:chat_marker()) -> ok.
56 update_chat_marker(HostType, #{from := #jid{luser = LU, lserver = LS},
57 to := To, thread := Thread,
58 type := Type, timestamp := TS, id := Id} = Marker) ->
59
:-(
ToEncoded = encode_jid(To),
60
:-(
ThreadEncoded = encode_thread(Thread),
61
:-(
TypeEncoded = encode_type(Type),
62
:-(
KeyValues = [LS, LU, ToEncoded, ThreadEncoded, TypeEncoded],
63
:-(
UpdateValues = [Id, TS],
64
:-(
InsertValues = KeyValues ++ UpdateValues,
65
:-(
Res = rdbms_queries:execute_upsert(HostType, smart_markers_upsert,
66 InsertValues, UpdateValues, KeyValues),
67
:-(
verify(Res, Marker).
68
69 -spec get_conv_chat_marker(HostType :: mongooseim:host_type(),
70 From :: jid:jid(),
71 To :: jid:jid(),
72 Thread :: mod_smart_markers:maybe_thread(),
73 Timestamp :: integer(),
74 Private :: boolean()) -> [mod_smart_markers:chat_marker()].
75 get_conv_chat_marker(HostType, From, To = #jid{lserver = ToLServer}, Thread, TS, Private) ->
76 % If To is a room, we'll want to check just the room
77
:-(
case mongoose_domain_api:get_subdomain_host_type(ToLServer) of
78 {error, not_found} ->
79
:-(
one2one_get_conv_chat_marker(HostType, From, To, Thread, TS, Private);
80 {ok, _} ->
81
:-(
groupchat_get_conv_chat_marker(HostType, From, To, Thread, TS, Private)
82 end.
83
84 one2one_get_conv_chat_marker(HostType,
85 From = #jid{luser = FromLUser, lserver = FromLServer},
86 To = #jid{luser = ToLUser, lserver = ToLServer},
87 Thread, TS, Private) ->
88
:-(
{selected, ChatMarkersFrom} = mongoose_rdbms:execute_successfully(
89 HostType, smart_markers_select_conv,
90 [FromLServer, FromLUser, encode_jid(To), encode_thread(Thread), TS]),
91
:-(
ChatMarkers = case Private of
92
:-(
true -> ChatMarkersFrom;
93 false ->
94
:-(
{selected, ChatMarkersTo} = mongoose_rdbms:execute_successfully(
95 HostType, smart_markers_select_conv,
96 [ToLServer, ToLUser, encode_jid(From), encode_thread(Thread), TS]),
97
:-(
ChatMarkersFrom ++ ChatMarkersTo
98 end,
99
:-(
[ decode_chat_marker(Tuple) || Tuple <- ChatMarkers].
100
101 groupchat_get_conv_chat_marker(HostType, _From, To, Thread, TS, false) ->
102
:-(
get_chat_markers(HostType, To, Thread, TS);
103 groupchat_get_conv_chat_marker(HostType, #jid{luser = FromLUser, lserver = FromLServer}, To, Thread, TS, true) ->
104
:-(
{selected, ChatMarkers} = mongoose_rdbms:execute_successfully(
105 HostType, smart_markers_select_conv,
106 [FromLServer, FromLUser, encode_jid(To), encode_thread(Thread), TS]),
107
:-(
[ decode_chat_marker(Tuple) || Tuple <- ChatMarkers].
108
109 %%% @doc
110 %%% This function must return the latest chat markers sent to the
111 %%% user/room (with or w/o thread) later than provided timestamp.
112 %%% @end
113 -spec get_chat_markers(HostType :: mongooseim:host_type(),
114 To :: jid:jid(),
115 Thread :: mod_smart_markers:maybe_thread(),
116 Timestamp :: integer()) -> [mod_smart_markers:chat_marker()].
117 get_chat_markers(HostType, To, Thread, TS) ->
118
:-(
{selected, ChatMarkers} = mongoose_rdbms:execute_successfully(
119 HostType, smart_markers_select,
120 [encode_jid(To), encode_thread(Thread), TS]),
121
:-(
[ #{from => jid:make_noprep(CLUser, CLServer, <<>>),
122 to => To,
123 thread => Thread,
124 type => decode_type(CType),
125 timestamp => decode_timestamp(CTS),
126 id => CMsgId}
127
:-(
|| {CLServer, CLUser, CType, CMsgId, CTS} <- ChatMarkers].
128
129
130 -spec remove_domain(mongooseim:host_type(), jid:lserver()) -> mongoose_rdbms:query_result().
131 remove_domain(HostType, Domain) ->
132
:-(
mongoose_rdbms:execute_successfully(HostType, markers_remove_domain, [Domain]).
133
134 -spec remove_user(mongooseim:host_type(), jid:jid()) -> mongoose_rdbms:query_result().
135 remove_user(HostType, #jid{luser = LU, lserver = LS}) ->
136
:-(
mongoose_rdbms:execute_successfully(HostType, markers_remove_user, [LS, LU]).
137
138 -spec remove_to(mongooseim:host_type(), jid:jid()) -> mongoose_rdbms:query_result().
139 remove_to(HostType, To) ->
140
:-(
mongoose_rdbms:execute_successfully(HostType, markers_remove_to, [encode_jid(To)]).
141
142 -spec remove_to_for_user(mongooseim:host_type(), From :: jid:jid(), To :: jid:jid()) ->
143 mongoose_rdbms:query_result().
144 remove_to_for_user(HostType, #jid{luser = LU, lserver = LS}, To) ->
145
:-(
mongoose_rdbms:execute_successfully(HostType, markers_remove_to_for_user, [LS, LU, encode_jid(To)]).
146
147 -spec verify(term(), mod_smart_markers:chat_marker()) -> ok.
148 verify(Answer, Marker) ->
149
:-(
case check_upsert_result(Answer) of
150 {error, Reason} ->
151
:-(
?LOG_WARNING(#{what => smart_marker_insert_failed, reason => Reason,
152
:-(
marker => Marker});
153
:-(
_ -> ok
154 end.
155
156 %%--------------------------------------------------------------------
157 %% local functions
158 %%--------------------------------------------------------------------
159
:-(
encode_jid(JID) -> jid:to_bare_binary(JID).
160
161
:-(
encode_thread(undefined) -> <<>>;
162
:-(
encode_thread(Thread) -> Thread.
163
164
:-(
encode_type(received) -> <<"R">>;
165
:-(
encode_type(displayed) -> <<"D">>;
166
:-(
encode_type(acknowledged) -> <<"A">>.
167
168 %% MySQL returns 1 when an upsert is an insert
169 %% and 2, when an upsert acts as update
170
:-(
check_upsert_result({updated, 0}) -> ok;
171
:-(
check_upsert_result({updated, 1}) -> ok;
172
:-(
check_upsert_result({updated, 2}) -> ok;
173 check_upsert_result(Result) ->
174
:-(
{error, {bad_result, Result}}.
175
176 decode_chat_marker({LS, LU, ToJid, MsgThread, Type, MsgId, MsgTS}) ->
177
:-(
#{from => jid:make_noprep(LU, LS, <<>>),
178 to => decode_jid(ToJid),
179 thread => decode_thread(MsgThread),
180 type => decode_type(Type),
181 timestamp => decode_timestamp(MsgTS),
182 id => MsgId}.
183
184
:-(
decode_jid(EncodedJID) -> jid:from_binary(EncodedJID).
185
186
:-(
decode_thread(<<>>) -> undefined;
187
:-(
decode_thread(Thread) -> Thread.
188
189
:-(
decode_type(<<"R">>) -> received;
190
:-(
decode_type(<<"D">>) -> displayed;
191
:-(
decode_type(<<"A">>) -> acknowledged.
192
193 decode_timestamp(EncodedTS) ->
194
:-(
mongoose_rdbms:result_to_integer(EncodedTS).
Line Hits Source