./ct_report/coverage/mod_mam_meta.COVER.html

1 %%==============================================================================
2 %% Copyright 2016 Erlang Solutions Ltd.
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15 %%==============================================================================
16
17 -module(mod_mam_meta).
18 -behaviour(gen_mod).
19 -behaviour(mongoose_module_metrics).
20
21 -type deps() :: #{module() => proplists:proplist()}.
22
23 -export([start/2, stop/1, config_spec/0, supported_features/0, deps/2]).
24
25 -export([config_metrics/1]).
26
27 -include("mongoose_config_spec.hrl").
28
29 %%--------------------------------------------------------------------
30 %% API
31 %%--------------------------------------------------------------------
32
33 -spec supported_features() -> [atom()].
34 supported_features() ->
35
:-(
[dynamic_domains].
36
37 -spec start(Host :: jid:server(), Opts :: list()) -> any().
38 start(_Host, _Opts) ->
39
:-(
ok.
40
41
42 -spec stop(Host :: jid:server()) -> any().
43 stop(_Host) ->
44
:-(
ok.
45
46 -spec config_spec() -> mongoose_config_spec:config_section().
47 config_spec() ->
48 160 Items = config_items(),
49 160 #section{
50 items = Items#{<<"pm">> => #section{items = maps:merge(Items, pm_config_items())},
51 <<"muc">> => #section{items = maps:merge(Items, muc_config_items())},
52 <<"riak">> => riak_config_spec()}
53 }.
54
55 config_items() ->
56 160 #{%% General options
57 <<"backend">> => #option{type = atom,
58 validate = {enum, [rdbms, riak, cassandra, elasticsearch]}},
59 <<"no_stanzaid_element">> => #option{type = boolean},
60 <<"is_archivable_message">> => #option{type = atom,
61 validate = module},
62 <<"send_message">> => #option{type = atom,
63 validate = module},
64 <<"archive_chat_markers">> => #option{type = boolean},
65 <<"message_retraction">> => #option{type = boolean},
66
67 %% Common backend options
68 <<"user_prefs_store">> => #option{type = atom,
69 validate = {enum, [rdbms, cassandra, mnesia]}},
70 <<"full_text_search">> => #option{type = boolean},
71
72 %% RDBMS-specific options
73 <<"cache_users">> => #option{type = boolean},
74 <<"cache">> => mongoose_user_cache:config_spec(),
75 <<"rdbms_message_format">> => #option{type = atom,
76 validate = {enum, [simple, internal]}},
77 <<"async_writer">> => mod_mam_rdbms_arch_async:config_spec(),
78
79 %% Low-level options
80 <<"default_result_limit">> => #option{type = integer,
81 validate = non_negative},
82 <<"max_result_limit">> => #option{type = integer,
83 validate = non_negative},
84 <<"db_jid_format">> => #option{type = atom,
85 validate = module},
86 <<"db_message_format">> => #option{type = atom,
87 validate = module},
88 <<"simple">> => #option{type = boolean},
89 <<"extra_fin_element">> => #option{type = atom,
90 validate = module},
91 <<"extra_lookup_params">> => #option{type = atom,
92 validate = module}
93 }.
94
95 pm_config_items() ->
96 160 #{<<"archive_groupchats">> => #option{type = boolean},
97 <<"same_mam_id_for_peers">> => #option{type = boolean}}.
98
99 muc_config_items() ->
100 160 #{<<"host">> => #option{type = string,
101 validate = subdomain_template,
102 process = fun mongoose_subdomain_utils:make_subdomain_pattern/1}}.
103
104 riak_config_spec() ->
105 160 #section{
106 items = #{<<"search_index">> => #option{type = binary,
107 validate = non_empty},
108 <<"bucket_type">> => #option{type = binary,
109 validate = non_empty}},
110 wrap = none
111 }.
112
113 -spec deps(_Host :: jid:server(), Opts :: proplists:proplist()) ->
114 gen_mod:deps_list().
115 deps(_Host, Opts0) ->
116
:-(
Opts = normalize(Opts0),
117
118
:-(
DepsWithPm = handle_nested_opts(pm, Opts, false, #{}),
119
:-(
DepsWithPmAndMuc = handle_nested_opts(muc, Opts, false, DepsWithPm),
120
121
:-(
[{Dep, Args, hard} || {Dep, Args} <- maps:to_list(DepsWithPmAndMuc)].
122
123 %%--------------------------------------------------------------------
124 %% Helpers
125 %%--------------------------------------------------------------------
126
127 -spec handle_nested_opts(Key :: atom(), RootOpts :: proplists:proplist(),
128 Default :: term(), deps()) -> deps().
129 handle_nested_opts(Key, RootOpts, Default, Deps) ->
130
:-(
case proplists:get_value(Key, RootOpts, Default) of
131
:-(
false -> Deps;
132 Opts0 ->
133
:-(
Opts = normalize(Opts0),
134
:-(
FullOpts = lists:ukeymerge(1, Opts, RootOpts),
135
:-(
parse_opts(Key, FullOpts, Deps)
136 end.
137
138 -spec parse_opts(Type :: pm | muc, Opts :: proplists:proplist(), deps()) -> deps().
139 parse_opts(Type, Opts, Deps) ->
140 %% Opts are merged root options with options inside pm or muc section
141
:-(
CoreMod = mam_type_to_core_mod(Type),
142
:-(
CoreModOpts = filter_opts(Opts, valid_core_mod_opts(CoreMod)),
143
:-(
WithCoreDeps = add_dep(CoreMod, CoreModOpts, Deps),
144
:-(
Backend = proplists:get_value(backend, Opts, rdbms),
145
:-(
parse_backend_opts(Backend, Type, Opts, WithCoreDeps).
146
147 -spec mam_type_to_core_mod(atom()) -> module().
148
:-(
mam_type_to_core_mod(pm) -> mod_mam;
149
:-(
mam_type_to_core_mod(muc) -> mod_mam_muc.
150
151 filter_opts(Opts, ValidOpts) ->
152
:-(
lists:filtermap(
153 fun(Key) ->
154
:-(
case proplists:lookup(Key, Opts) of
155
:-(
none -> false;
156
:-(
Opt -> {true, Opt}
157 end
158 end, ValidOpts).
159
160 %% Get a list of options to pass into the two modules.
161 %% They don't required to be defined in pm or muc sections,
162 %% the root section is enough.
163 -spec valid_core_mod_opts(module()) -> [atom()].
164 valid_core_mod_opts(mod_mam) ->
165
:-(
[no_stanzaid_element, archive_groupchats, same_mam_id_for_peers] ++ common_opts();
166 valid_core_mod_opts(mod_mam_muc) ->
167
:-(
[host] ++ common_opts().
168
169 common_opts() ->
170
:-(
[async_writer,
171 is_archivable_message,
172 send_message,
173 archive_chat_markers,
174 extra_fin_element,
175 extra_lookup_params,
176 full_text_search,
177 message_retraction,
178 default_result_limit,
179 max_result_limit].
180
181 -spec parse_backend_opts(rdbms | cassandra | riak | elasticsearch, Type :: pm | muc,
182 Opts :: proplists:proplist(), deps()) -> deps().
183 parse_backend_opts(cassandra, Type, Opts, Deps0) ->
184
:-(
ModArch =
185 case Type of
186
:-(
pm -> mod_mam_cassandra_arch;
187
:-(
muc -> mod_mam_muc_cassandra_arch
188 end,
189
190
:-(
Opts1 = filter_opts(Opts, [db_message_format, pool_name, simple]),
191
:-(
Deps = add_dep(ModArch, Opts1, Deps0),
192
193
:-(
case proplists:get_value(user_prefs_store, Opts, false) of
194
:-(
cassandra -> add_dep(mod_mam_cassandra_prefs, [Type], Deps);
195
:-(
mnesia -> add_dep(mod_mam_mnesia_prefs, [Type], Deps);
196
:-(
_ -> Deps
197 end;
198
199 parse_backend_opts(riak, Type, Opts, Deps0) ->
200
:-(
Opts1 = filter_opts(Opts, [db_message_format, search_index, bucket_type]),
201
:-(
Deps = add_dep(mod_mam_riak_timed_arch_yz, [Type | Opts1], Deps0),
202
203
:-(
case proplists:get_value(user_prefs_store, Opts, false) of
204
:-(
mnesia -> add_dep(mod_mam_mnesia_prefs, [Type], Deps);
205
:-(
_ -> Deps
206 end;
207
208 parse_backend_opts(rdbms, Type, Opts0, Deps0) ->
209
:-(
Opts1 = add_rdbms_async_opts(Opts0),
210
:-(
Opts = add_rdbms_cache_opts(Opts1),
211
212
:-(
{ModRDBMSArch, ModAsyncWriter} =
213 case Type of
214
:-(
pm -> {mod_mam_rdbms_arch, mod_mam_rdbms_arch_async};
215
:-(
muc -> {mod_mam_muc_rdbms_arch, mod_mam_rdbms_arch_async}
216 end,
217
218
:-(
Deps1 = add_dep(ModRDBMSArch, [Type], Deps0),
219
:-(
Deps = add_dep(mod_mam_rdbms_user, user_db_types(Type), Deps1),
220
221
:-(
lists:foldl(
222 pa:bind(fun parse_rdbms_opt/5, Type, ModRDBMSArch, ModAsyncWriter),
223 Deps, Opts);
224
225 parse_backend_opts(elasticsearch, Type, Opts, Deps0) ->
226
:-(
ModArch =
227 case Type of
228
:-(
pm -> mod_mam_elasticsearch_arch;
229
:-(
muc -> mod_mam_muc_elasticsearch_arch
230 end,
231
232
:-(
Deps = add_dep(ModArch, Deps0),
233
234
:-(
case proplists:get_value(user_prefs_store, Opts, false) of
235
:-(
mnesia -> add_dep(mod_mam_mnesia_prefs, [Type], Deps);
236
:-(
_ -> Deps
237 end.
238
239 % muc backend requires both pm and muc user DB to populate sender_id column
240 -spec user_db_types(pm | muc) -> [pm | muc].
241
:-(
user_db_types(pm) -> [pm];
242
:-(
user_db_types(muc) -> [pm, muc].
243
244 -spec normalize(proplists:proplist()) -> [{atom(), term()}].
245 normalize(Opts) ->
246
:-(
lists:ukeysort(1, proplists:unfold(Opts)).
247
248
249 -spec add_dep(Dep :: module(), deps()) -> deps().
250 add_dep(Dep, Deps) ->
251
:-(
add_dep(Dep, [], Deps).
252
253
254 -spec add_dep(Dep :: module(), Args :: proplists:proplist(), deps()) -> deps().
255 add_dep(Dep, Args, Deps) ->
256
:-(
PrevArgs = maps:get(Dep, Deps, []),
257
:-(
NewArgs = lists:usort(Args ++ PrevArgs),
258
:-(
maps:put(Dep, NewArgs, Deps).
259
260
261 -spec add_rdbms_async_opts(proplists:proplist()) -> proplists:proplist().
262 add_rdbms_async_opts(Opts) ->
263
:-(
case lists:keyfind(async_writer, 1, Opts) of
264 {async_writer, AsyncOpts} ->
265
:-(
case lists:keyfind(enabled, 1, AsyncOpts) of
266
:-(
{enabled, false} -> lists:keydelete(async_writer, 1, Opts);
267
:-(
_ -> Opts
268 end;
269 false ->
270
:-(
[{async_writer, []} | Opts]
271 end.
272
273
274 add_rdbms_cache_opts(Opts) ->
275
:-(
case {lists:keyfind(cache_users, 1, Opts), lists:keyfind(cache, 1, Opts)} of
276 {{cache_users, false}, _} ->
277
:-(
lists:keydelete(cache, 1, Opts);
278 {{cache_users, true}, false} ->
279
:-(
[{cache, []} | Opts];
280 {false, false} ->
281
:-(
[{cache, []} | Opts];
282 {false, {cache, _}} ->
283
:-(
Opts
284 end.
285
286 -spec parse_rdbms_opt(Type :: pm | muc, module(), module(),
287 Option :: {module(), term()}, deps()) -> deps().
288 parse_rdbms_opt(Type, ModRDBMSArch, ModAsyncWriter, Option, Deps) ->
289
:-(
case Option of
290 {cache, CacheOpts} ->
291
:-(
Deps1 = case gen_mod:get_opt(module, CacheOpts, internal) of
292
:-(
internal -> Deps;
293
:-(
mod_cache_users -> add_dep(mod_cache_users, Deps)
294 end,
295
:-(
add_dep(mod_mam_cache_user, [Type | CacheOpts], Deps1);
296 {user_prefs_store, rdbms} ->
297
:-(
add_dep(mod_mam_rdbms_prefs, [Type], Deps);
298 {user_prefs_store, mnesia} ->
299
:-(
add_dep(mod_mam_mnesia_prefs, [Type], Deps);
300 {rdbms_message_format, simple} ->
301
:-(
add_dep(ModRDBMSArch, rdbms_simple_opts(), Deps);
302 {async_writer, Opts} ->
303
:-(
DepsWithNoWriter = add_dep(ModRDBMSArch, [no_writer], Deps),
304
:-(
add_dep(ModAsyncWriter, [{Type, Opts}], DepsWithNoWriter);
305
:-(
_ -> Deps
306 end.
307
308 -spec rdbms_simple_opts() -> list().
309
:-(
rdbms_simple_opts() -> [{db_jid_format, mam_jid_rfc}, {db_message_format, mam_message_xml}].
310
311 config_metrics(Host) ->
312
:-(
OptsToReport = [{backend, rdbms}], %list of tuples {option, defualt_value}
313
:-(
mongoose_module_metrics:opts_for_module(Host, ?MODULE, OptsToReport).
Line Hits Source