./ct_report/coverage/mod_mam_cassandra_prefs.COVER.html

1 %%%-------------------------------------------------------------------
2 %%% @author Uvarov Michael <arcusfelis@gmail.com>
3 %%% @copyright (C) 2013, Uvarov Michael
4 %%% @doc A backend for storing MAM preferencies using Cassandra.
5 %%% @end
6 %%%-------------------------------------------------------------------
7 -module(mod_mam_cassandra_prefs).
8 -behaviour(mongoose_cassandra).
9
10 %% ----------------------------------------------------------------------
11 %% Exports
12
13 %% gen_mod handlers
14 -export([start/2, stop/1]).
15
16 %% MAM hook handlers
17 -behaviour(ejabberd_gen_mam_prefs).
18 -export([get_behaviour/5,
19 get_prefs/4,
20 set_prefs/7,
21 remove_archive/4]).
22
23 -export([prepared_queries/0]).
24
25 -ignore_xref([remove_archive/4, start/2, stop/1]).
26
27 -include("mongoose.hrl").
28 -include("jlib.hrl").
29 -include_lib("exml/include/exml.hrl").
30
31 -type host_type() :: mongooseim:host_type().
32
33 %% ----------------------------------------------------------------------
34 %% gen_mod callbacks
35 %% Starting and stopping functions for users' archives
36
37 -spec start(host_type(), _) -> ok.
38 start(HostType, _Opts) ->
39
:-(
ejabberd_hooks:add(hooks(HostType)).
40
41 -spec stop(host_type()) -> ok.
42 stop(HostType) ->
43
:-(
ejabberd_hooks:delete(hooks(HostType)).
44
45 %% ----------------------------------------------------------------------
46 %% Hooks
47
48 hooks(HostType) ->
49 case gen_mod:get_module_opt(HostType, ?MODULE, pm, false) of
50
:-(
true -> pm_hooks(HostType);
51
:-(
false -> []
52
:-(
end ++
53 case gen_mod:get_module_opt(HostType, ?MODULE, muc, false) of
54
:-(
true -> muc_hooks(HostType);
55
:-(
false -> []
56 end.
57
58 pm_hooks(HostType) ->
59
:-(
[{mam_get_behaviour, HostType, ?MODULE, get_behaviour, 50},
60 {mam_get_prefs, HostType, ?MODULE, get_prefs, 50},
61 {mam_set_prefs, HostType, ?MODULE, set_prefs, 50},
62 {mam_remove_archive, HostType, ?MODULE, remove_archive, 50}].
63
64 muc_hooks(HostType) ->
65
:-(
[{mam_muc_get_behaviour, HostType, ?MODULE, get_behaviour, 50},
66 {mam_muc_get_prefs, HostType, ?MODULE, get_prefs, 50},
67 {mam_muc_set_prefs, HostType, ?MODULE, set_prefs, 50},
68 {mam_muc_remove_archive, HostType, ?MODULE, remove_archive, 50}].
69
70 %% ----------------------------------------------------------------------
71
72 prepared_queries() ->
73
:-(
[
74 {set_prefs_ts_query,
75 "INSERT INTO mam_config(user_jid, remote_jid, behaviour) VALUES (?, ?, ?) USING TIMESTAMP ?"},
76 {get_prefs_query,
77 "SELECT remote_jid, behaviour FROM mam_config WHERE user_jid = ?"},
78 {get_behaviour_bare_query,
79 "SELECT remote_jid, behaviour FROM mam_config WHERE user_jid = ? AND remote_jid IN ('', ?)"},
80 {get_behaviour_full_query,
81 "SELECT remote_jid, behaviour FROM mam_config WHERE user_jid = ? AND remote_jid "
82 "IN ('', :start_remote_jid, :end_remote_jid)"},
83 {del_prefs_ts_query,
84 "DELETE FROM mam_config USING TIMESTAMP ? WHERE user_jid = ?"}
85 ].
86
87 %% ----------------------------------------------------------------------
88 %% Internal functions and callbacks
89
90 -spec get_behaviour(Default :: mod_mam:archive_behaviour(),
91 HostType :: host_type(), ArchiveID :: mod_mam:archive_id(),
92 LocJID :: jid:jid(), RemJID :: jid:jid()) -> any().
93 get_behaviour(DefaultBehaviour, HostType, _UserID, LocJID, RemJID) ->
94
:-(
BUserJID = mod_mam_utils:bare_jid(LocJID),
95
:-(
BRemBareJID = mod_mam_utils:bare_jid(RemJID),
96
:-(
BRemJID = mod_mam_utils:full_jid(RemJID),
97
:-(
case query_behaviour(HostType, LocJID, BUserJID, BRemJID, BRemBareJID) of
98 {ok, []} ->
99
:-(
DefaultBehaviour;
100 {ok, [_ | _] = Rows} ->
101 %% After sort <<>>, <<"a">>, <<"a/b">>
102
:-(
SortedRows = lists:sort(
103 fun(#{remote_jid := JID1, behaviour := B1},
104 #{remote_jid := JID2, behaviour := B2}) ->
105
:-(
{JID1, B1} < {JID2, B2}
106 end, Rows),
107
:-(
#{behaviour := Behaviour} = lists:last(SortedRows),
108
:-(
decode_behaviour(Behaviour)
109 end.
110
111
112 -spec set_prefs(Result :: any(), HostType :: host_type(),
113 ArchiveID :: mod_mam:archive_id(), ArchiveJID :: jid:jid(),
114 DefaultMode :: mod_mam:archive_behaviour(),
115 AlwaysJIDs :: [jid:literal_jid()],
116 NeverJIDs :: [jid:literal_jid()]) -> any().
117 set_prefs(_Result, HostType, _UserID, UserJID, DefaultMode, AlwaysJIDs, NeverJIDs) ->
118
:-(
try
119
:-(
set_prefs1(HostType, UserJID, DefaultMode, AlwaysJIDs, NeverJIDs)
120 catch Type:Error:StackTrace ->
121
:-(
?LOG_ERROR(#{what => mam_set_prefs_failed,
122 user_jid => UserJID, default_mode => DefaultMode,
123 always_jids => AlwaysJIDs, never_jids => NeverJIDs,
124
:-(
class => Type, reason => Error, stacktrace => StackTrace}),
125
:-(
{error, Error}
126 end.
127
128 set_prefs1(HostType, UserJID, DefaultMode, AlwaysJIDs, NeverJIDs) ->
129
:-(
BUserJID = mod_mam_utils:bare_jid(UserJID),
130 %% Force order of operations using timestamps
131 %% http://stackoverflow.com/questions/30317877/cassandra-batch-statement-execution-order
132
:-(
Now = mongoose_cassandra:now_timestamp(),
133
:-(
Next = Now + 1,
134
:-(
DelParams = #{'[timestamp]' => Now, user_jid => BUserJID},
135
:-(
MultiParams = [encode_row(BUserJID, <<>>, encode_behaviour(DefaultMode), Next)]
136
:-(
++ [encode_row(BUserJID, BinJID, <<"A">>, Next) || BinJID <- AlwaysJIDs]
137
:-(
++ [encode_row(BUserJID, BinJID, <<"N">>, Next) || BinJID <- NeverJIDs],
138
:-(
DelQuery = {del_prefs_ts_query, [DelParams]},
139
:-(
SetQuery = {set_prefs_ts_query, MultiParams},
140
:-(
Queries = [DelQuery, SetQuery],
141
:-(
Res = [mongoose_cassandra:cql_write(pool_name(HostType), UserJID, ?MODULE, Query, Params)
142
:-(
|| {Query, Params} <- Queries],
143
:-(
?LOG_DEBUG(#{what => mam_set_prefs, user_jid => UserJID, default_mode => DefaultMode,
144
:-(
always_jids => AlwaysJIDs, never_jids => NeverJIDs, result => Res}),
145
:-(
ok.
146
147 encode_row(BUserJID, BRemoteJID, Behaviour, Timestamp) ->
148
:-(
#{user_jid => BUserJID, remote_jid => BRemoteJID,
149 behaviour => Behaviour, '[timestamp]' => Timestamp}.
150
151
152 -spec get_prefs(mod_mam:preference(), _HostType :: host_type(),
153 ArchiveID :: mod_mam:archive_id(), ArchiveJID :: jid:jid())
154 -> mod_mam:preference().
155 get_prefs({GlobalDefaultMode, _, _}, HostType, _UserID, UserJID) ->
156
:-(
BUserJID = mod_mam_utils:bare_jid(UserJID),
157
:-(
Params = #{user_jid => BUserJID},
158
:-(
{ok, Rows} = mongoose_cassandra:cql_read(pool_name(HostType), UserJID, ?MODULE,
159 get_prefs_query, Params),
160
:-(
decode_prefs_rows(Rows, GlobalDefaultMode, [], []).
161
162
163 -spec remove_archive(mongoose_acc:t(), host_type(), mod_mam:archive_id(), jid:jid()) ->
164 mongoose_acc:t().
165 remove_archive(Acc, HostType, _UserID, UserJID) ->
166
:-(
remove_archive(HostType, UserJID),
167
:-(
Acc.
168
169 remove_archive(HostType, UserJID) ->
170
:-(
BUserJID = mod_mam_utils:bare_jid(UserJID),
171
:-(
Now = mongoose_cassandra:now_timestamp(),
172
:-(
Params = #{'[timestamp]' => Now, user_jid => BUserJID},
173
:-(
mongoose_cassandra:cql_write(pool_name(HostType), UserJID,
174 ?MODULE, del_prefs_ts_query, [Params]).
175
176 -spec query_behaviour(host_type(), UserJID :: jid:jid(), BUserJID :: binary() | string(),
177 BRemJID :: binary() | string(), BRemBareJID :: binary() | string()) -> any().
178 query_behaviour(HostType, UserJID, BUserJID, BRemJID, BRemBareJID)
179 when BRemJID == BRemBareJID ->
180
:-(
Params = #{user_jid => BUserJID, remote_jid => BRemBareJID},
181
:-(
mongoose_cassandra:cql_read(pool_name(HostType), UserJID, ?MODULE,
182 get_behaviour_bare_query, Params);
183 query_behaviour(HostType, UserJID, BUserJID, BRemJID, BRemBareJID) ->
184
:-(
Params = #{user_jid => BUserJID, start_remote_jid => BRemJID,
185 end_remote_jid => BRemBareJID},
186
:-(
mongoose_cassandra:cql_read(pool_name(HostType), UserJID, ?MODULE,
187 get_behaviour_full_query, Params).
188
189 %% ----------------------------------------------------------------------
190 %% Helpers
191
192 -spec encode_behaviour('always' | 'never' | 'roster') -> binary().
193
:-(
encode_behaviour(roster) -> <<"R">>;
194
:-(
encode_behaviour(always) -> <<"A">>;
195
:-(
encode_behaviour(never) -> <<"N">>.
196
197
198 -spec decode_behaviour(<<_:8>>) -> 'always' | 'never' | 'roster'.
199
:-(
decode_behaviour(<<"R">>) -> roster;
200
:-(
decode_behaviour(<<"A">>) -> always;
201
:-(
decode_behaviour(<<"N">>) -> never.
202
203 -spec decode_prefs_rows([[term()]], DefaultMode, AlwaysJIDs, NeverJIDs) ->
204 {DefaultMode, AlwaysJIDs, NeverJIDs} when
205 DefaultMode :: mod_mam:archive_behaviour(),
206 AlwaysJIDs :: [jid:literal_jid()],
207 NeverJIDs :: [jid:literal_jid()].
208 decode_prefs_rows([], DefaultMode, AlwaysJIDs, NeverJIDs) ->
209
:-(
{DefaultMode, AlwaysJIDs, NeverJIDs};
210
211 decode_prefs_rows([#{remote_jid := <<>>, behaviour := Behaviour} | Rows],
212 _DefaultMode, AlwaysJIDs, NeverJIDs) ->
213
:-(
decode_prefs_rows(Rows, decode_behaviour(Behaviour), AlwaysJIDs, NeverJIDs);
214 decode_prefs_rows([#{remote_jid := JID, behaviour := <<"A">>} | Rows],
215 DefaultMode, AlwaysJIDs, NeverJIDs) ->
216
:-(
decode_prefs_rows(Rows, DefaultMode, [JID | AlwaysJIDs], NeverJIDs);
217 decode_prefs_rows([#{remote_jid := JID, behaviour := <<"N">>} | Rows],
218 DefaultMode, AlwaysJIDs, NeverJIDs) ->
219
:-(
decode_prefs_rows(Rows, DefaultMode, AlwaysJIDs, [JID | NeverJIDs]).
220
221 %% ----------------------------------------------------------------------
222 %% Params getters
223
224 -spec pool_name(HostType :: host_type()) -> term().
225 pool_name(HostType) ->
226
:-(
gen_mod:get_module_opt(HostType, ?MODULE, pool_name, default).
Line Hits Source