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