Skip to content

Commit 7f88531

Browse files
michalwskifenek
authored andcommitted
MAM refactoring - part 2 (#1425)
* [MAM] move action related functions to mam_iq * [MAM] reduce code duplication in mod_mam and mod_mam_muc Unify message forwarding * [MAM] export function to build MAM reply * [MAM] allow to configure call back module for extra lookup params * [MAM] use exml_query instead of xml * address PR comments * Export helpers function from mod_mam_muc Export a function which handles lookup_messages/2 result and reponds properly according to MAM version in incoming IQ.
1 parent db33a75 commit 7f88531

File tree

6 files changed

+273
-313
lines changed

6 files changed

+273
-313
lines changed

doc/modules/mod_mam.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ For now `odbc` backend has very limited support for this feature, while `cassand
3131
* **archive_chat_markers** (boolean, default: `false`) - If set to true, XEP-0333 chat markers will be archived. See more details [here](#archiving-chat-markers)
3232
* **pm** (list | `false`, default: `[]`) - Override options for archivization of one-to-one messages. If the value of this option is `false`, one-to-one message archive is disabled.
3333
* **muc** (list | `false`, default: `false`) - Override options for archivization of group chat messages. If the value of this option is `false`, group chat message archive is disabled.
34+
* **extra_lookup_params** (atom, default: `undefined`) - a module implementing `mam_iq` behaviour.
35+
If this option has value other then undefined, function `extra_lookup_params/2` from this module will be called when building MAM lookup parameters.
36+
This can be used to extend currently supported MAM query fields by a custom field or fields.
37+
This field(s) can be added to lookup params later passed to MAM backend.
3438

3539
**backend**, **add_archived_element**, **no_stanzaid_element** and **is_archivable_message** will be applied to both `pm` and `muc` (if they are enabled), unless overriden explicitly (see example below).
3640

@@ -70,9 +74,9 @@ These options will only have effect when the `odbc` backend is used:
7074
#### Common backend options
7175

7276
* **user_prefs_store** (atom, default: `false`) - Leaving this option as `false` will prevent users from setting their archiving preferences. It will also increase performance. Other possible values are:
73-
* `odbc` (ODBC backend only) - User archiving preferences saved in ODBC. Slow and not recommended, but might be used to simplify things and keep everything in ODBC.
74-
* `cassandra` (Cassandra backend only) - User archiving preferences are saved in Cassandra.
75-
* `mnesia` (recommended) - User archiving preferences saved in Mnesia and accessed without transactions. Recommended in most deployments, could be overloaded with lots of users updating their preferences at once. There's a small risk of an inconsistent (in a rather harmless way) state of the preferences table.
77+
* `odbc` (ODBC backend only) - User archiving preferences saved in ODBC. Slow and not recommended, but might be used for simplicity (keeping everything in ODBC).
78+
* `cassandra` (Cassandra backend only) - User archiving preferences are saved in Cassandra.
79+
* `mnesia` (recommended) - User archiving preferences saved in Mnesia and accessed without transactions. Recommended in most deployments, could be overloaded with lots of users updating their preferences at once. There's a small risk of an inconsistent (in a rather harmless way) state of the preferences table.
7680
* **full_text_search** (boolean, default: `true`) - Enables full text search in message archive (see *Full Text Search* paragraph). Please note that the full text search is currently only implemented for `odbc` and `riak` backends. Also, full text search works only for messages archived while this option is enabled.
7781

7882
#### <a id="is_archivable_message"></a>`is_archivable_message/3` callback

src/mam_iq.erl

Lines changed: 110 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
-module(mam_iq).
22

3+
-export([action/1]).
4+
-export([action_type/1]).
5+
36
-export([fix_rsm/1]).
47
-export([elem_to_start_microseconds/1]).
58
-export([elem_to_end_microseconds/1]).
69
-export([elem_to_with_jid/1]).
710
-export([elem_to_limit/1]).
8-
-export([query_to_lookup_params/3]).
11+
-export([query_to_lookup_params/4]).
912

1013
-export([form_to_start_microseconds/1]).
1114
-export([form_to_end_microseconds/1]).
1215
-export([form_to_with_jid/1]).
13-
-export([form_to_lookup_params/3]).
16+
-export([form_to_lookup_params/4]).
1417

1518
-export([lookup_params_with_archive_details/3]).
1619

@@ -21,6 +24,60 @@
2124

2225
-include("jlib.hrl").
2326

27+
-type action() :: 'mam_get_prefs'
28+
| 'mam_lookup_messages'
29+
| 'mam_purge_multiple_messages'
30+
| 'mam_purge_single_message'
31+
| 'mam_set_prefs'
32+
| 'mam_set_message_form'
33+
| 'mam_get_message_form'.
34+
35+
-export_type([action/0]).
36+
37+
-callback extra_lookup_params(iq(), map()) -> map().
38+
39+
-spec action(ejabberd:iq()) -> action().
40+
action(IQ = #iq{xmlns = ?NS_MAM}) ->
41+
action_v02(IQ);
42+
action(IQ = #iq{xmlns = ?NS_MAM_03}) ->
43+
action_v03(IQ);
44+
action(IQ = #iq{xmlns = ?NS_MAM_04}) ->
45+
action_v03(IQ);
46+
action(IQ = #iq{xmlns = ?NS_MAM_06}) ->
47+
action_v03(IQ).
48+
49+
action_v02(#iq{type = Action, sub_el = SubEl = #xmlel{name = Category}}) ->
50+
case {Action, Category} of
51+
{set, <<"prefs">>} -> mam_set_prefs;
52+
{get, <<"prefs">>} -> mam_get_prefs;
53+
{get, <<"query">>} -> mam_lookup_messages;
54+
{set, <<"purge">>} ->
55+
case exml_query:attr(SubEl, <<"id">>, <<>>) of
56+
<<>> -> mam_purge_multiple_messages;
57+
_ -> mam_purge_single_message
58+
end
59+
end.
60+
61+
action_v03(#iq{type = Action, sub_el = #xmlel{name = Category}}) ->
62+
case {Action, Category} of
63+
{set, <<"prefs">>} -> mam_set_prefs;
64+
{get, <<"prefs">>} -> mam_get_prefs;
65+
{get, <<"query">>} -> mam_get_message_form;
66+
{set, <<"query">>} ->
67+
mam_set_message_form
68+
%% Purge is NOT official extention, it is not implemented for XEP-0313 v0.3.
69+
%% Use v0.2 namespace if you really want it.
70+
end.
71+
72+
-spec action_type(action()) -> 'get' | 'set'.
73+
action_type(mam_get_prefs) -> get;
74+
action_type(mam_set_prefs) -> set;
75+
action_type(mam_lookup_messages) -> get;
76+
action_type(mam_set_message_form) -> get;
77+
action_type(mam_get_message_form) -> get;
78+
action_type(mam_purge_single_message) -> set;
79+
action_type(mam_purge_multiple_messages) -> set.
80+
2481
%% @doc Convert id into internal format.
2582
-spec fix_rsm('none' | jlib:rsm_in()) -> 'undefined' | jlib:rsm_in().
2683
fix_rsm(none) ->
@@ -82,48 +139,54 @@ maybe_jid(<<>>) ->
82139
maybe_jid(JID) when is_binary(JID) ->
83140
jid:from_binary(JID).
84141

85-
query_to_lookup_params(QueryEl, MaxResultLimit, DefaultResultLimit) ->
142+
-spec query_to_lookup_params(iq(), integer(), integer(), undefined | module()) -> map().
143+
query_to_lookup_params(#iq{sub_el = QueryEl} = IQ, MaxResultLimit, DefaultResultLimit, Module) ->
86144
Params0 = common_lookup_params(QueryEl, MaxResultLimit, DefaultResultLimit),
87-
Params0#{
88-
%% Filtering by date.
89-
%% Start :: integer() | undefined
90-
start_ts => mam_iq:elem_to_start_microseconds(QueryEl),
91-
end_ts => mam_iq:elem_to_end_microseconds(QueryEl),
92-
%% Filtering by contact.
93-
search_text => undefined,
94-
with_jid => elem_to_with_jid(QueryEl),
95-
borders => mod_mam_utils:borders_decode(QueryEl),
96-
is_simple => mod_mam_utils:decode_optimizations(QueryEl)}.
97-
98-
form_to_lookup_params(QueryEl, MaxResultLimit, DefaultResultLimit) ->
145+
Params = Params0#{
146+
%% Filtering by date.
147+
%% Start :: integer() | undefined
148+
start_ts => mam_iq:elem_to_start_microseconds(QueryEl),
149+
end_ts => mam_iq:elem_to_end_microseconds(QueryEl),
150+
%% Filtering by contact.
151+
search_text => undefined,
152+
with_jid => elem_to_with_jid(QueryEl),
153+
borders => mod_mam_utils:borders_decode(QueryEl),
154+
is_simple => mod_mam_utils:decode_optimizations(QueryEl)},
155+
156+
maybe_add_extra_lookup_params(Module, Params, IQ).
157+
158+
159+
-spec form_to_lookup_params(iq(), integer(), integer(), undefined | module()) -> map().
160+
form_to_lookup_params(#iq{sub_el = QueryEl} = IQ, MaxResultLimit, DefaultResultLimit, Module) ->
99161
Params0 = common_lookup_params(QueryEl, MaxResultLimit, DefaultResultLimit),
100-
Params0#{
101-
%% Filtering by date.
102-
%% Start :: integer() | undefined
103-
start_ts => mam_iq:form_to_start_microseconds(QueryEl),
104-
end_ts => mam_iq:form_to_end_microseconds(QueryEl),
105-
%% Filtering by contact.
106-
with_jid => mam_iq:form_to_with_jid(QueryEl),
107-
%% Filtering by text
108-
search_text => mod_mam_utils:form_to_text(QueryEl),
109-
110-
borders => mod_mam_utils:form_borders_decode(QueryEl),
111-
%% Whether or not the client query included a <set/> element,
112-
%% the server MAY simply return its limited results.
113-
%% So, disable 'policy-violation'.
114-
limit_passed => true,
115-
%% `is_simple' can contain three values:
116-
%% - true - do not count records (useful during pagination, when we already
117-
%% know how many messages we have from a previous query);
118-
%% - false - count messages (slow, according XEP-0313);
119-
%% - opt_count - count messages (same as false, fast for small result sets)
120-
%%
121-
%% The difference between false and opt_count is that with IsSimple=false we count
122-
%% messages first and then extract a messages on a page (if count is not zero).
123-
%% If IsSimple=opt_count we extract a page and then calculate messages (if required).
124-
%% `opt_count' can be passed inside an IQ.
125-
%% Same for mod_mam_muc.
126-
is_simple => mod_mam_utils:form_decode_optimizations(QueryEl)}.
162+
Params = Params0#{
163+
%% Filtering by date.
164+
%% Start :: integer() | undefined
165+
start_ts => mam_iq:form_to_start_microseconds(QueryEl),
166+
end_ts => mam_iq:form_to_end_microseconds(QueryEl),
167+
%% Filtering by contact.
168+
with_jid => mam_iq:form_to_with_jid(QueryEl),
169+
%% Filtering by text
170+
search_text => mod_mam_utils:form_to_text(QueryEl),
171+
172+
borders => mod_mam_utils:form_borders_decode(QueryEl),
173+
%% Whether or not the client query included a <set/> element,
174+
%% the server MAY simply return its limited results.
175+
%% So, disable 'policy-violation'.
176+
limit_passed => true,
177+
%% `is_simple' can contain three values:
178+
%% - true - do not count records (useful during pagination, when we already
179+
%% know how many messages we have from a previous query);
180+
%% - false - count messages (slow, according XEP-0313);
181+
%% - opt_count - count messages (same as false, fast for small result sets)
182+
%%
183+
%% The difference between false and opt_count is that with IsSimple=false we count
184+
%% messages first and then extract a messages on a page (if count is not zero).
185+
%% If IsSimple=opt_count we extract a page and then calculate messages (if required).
186+
%% `opt_count' can be passed inside an IQ.
187+
%% Same for mod_mam_muc.
188+
is_simple => mod_mam_utils:form_decode_optimizations(QueryEl)},
189+
maybe_add_extra_lookup_params(Module, Params, IQ).
127190

128191
common_lookup_params(QueryEl, MaxResultLimit, DefaultResultLimit) ->
129192
Limit = mam_iq:elem_to_limit(QueryEl),
@@ -138,3 +201,8 @@ lookup_params_with_archive_details(Params, ArcID, ArcJID) ->
138201
Params#{archive_id => ArcID,
139202
owner_jid => ArcJID}.
140203

204+
maybe_add_extra_lookup_params(undefined, Params, _) ->
205+
Params;
206+
maybe_add_extra_lookup_params(Module, Params, IQ) ->
207+
Module:extra_lookup_params(IQ, Params).
208+

0 commit comments

Comments
 (0)