./ct_report/coverage/mod_smart_markers_rdbms.COVER.html

1 %%%----------------------------------------------------------------------------
2 %%% @copyright (C) 2020, Erlang Solutions Ltd.
3 %%% @doc
4 %%% RDBMS backend for mod_smart_markers
5 %%% @end
6 %%%----------------------------------------------------------------------------
7 -module(mod_smart_markers_rdbms).
8 -author("denysgonchar").
9 -behavior(mod_smart_markers_backend).
10
11 -export([init/2, update_chat_marker/2, get_chat_markers/4]).
12
13 %%--------------------------------------------------------------------
14 %% API
15 %%--------------------------------------------------------------------
16 -spec init(mongooseim:host_type(), gen_mod:module_opts()) -> ok.
17 init(HostType, _) ->
18 1 KeyFields = [<<"from_jid">>, <<"to_jid">>, <<"thread">>, <<"type">>],
19 1 UpdateFields = [<<"msg_id">>, <<"timestamp">>],
20 1 InsertFields = KeyFields ++ UpdateFields,
21 1 QueryName = smart_markers_upsert,
22 1 rdbms_queries:prepare_upsert(HostType, QueryName, smart_markers,
23 InsertFields, UpdateFields, KeyFields),
24 1 mongoose_rdbms:prepare(smart_markers_select, smart_markers,
25 [to_jid, thread, timestamp],
26 <<"SELECT from_jid, to_jid, thread, type, msg_id, timestamp FROM smart_markers "
27 "WHERE to_jid = ? AND thread = ? AND timestamp >= ?">>),
28 1 ok.
29
30 %%% @doc
31 %%% 'from', 'to', 'thread' and 'type' keys of the ChatMarker map serve
32 %%% as a composite database key. If key is not available in the database,
33 %%% then chat marker must be added. Otherwise this function must update
34 %%% chat marker record for that composite key.
35 %%% @end
36 -spec update_chat_marker(mongooseim:host_type(),
37 mod_smart_markers:chat_marker()) -> ok.
38 update_chat_marker(HostType, ChatMarker) ->
39 10 do_update_chat_marker(HostType, ChatMarker).
40
41 %%% @doc
42 %%% This function must return the latest chat markers sent to the
43 %%% user/room (with or w/o thread) later than provided timestamp.
44 %%% @end
45 -spec get_chat_markers(HostType :: mongooseim:host_type(),
46 To :: jid:jid(),
47 Thread :: mod_smart_markers:maybe_thread(),
48 Timestamp :: integer()) -> [mod_smart_markers:chat_marker()].
49 get_chat_markers(HostType, To, Thread, TS) ->
50 4 do_get_chat_markers(HostType, To, Thread, TS).
51
52 %%--------------------------------------------------------------------
53 %% local functions
54 %%--------------------------------------------------------------------
55 -spec execute_select_chat_markers(HostType :: mongooseim:host_type(),
56 To :: binary(),
57 Thread :: binary(),
58 Timestamp :: integer()) ->
59 mongoose_rdbms:query_result().
60 execute_select_chat_markers(HostType, To, Thread, Timestamp) ->
61 4 mongoose_rdbms:execute_successfully(HostType, smart_markers_select,
62 [To, Thread, Timestamp]).
63
64 do_update_chat_marker(HostType, #{from := From, to := To, thread := Thread,
65 type := Type, timestamp := TS, id := Id}) ->
66 10 FromEncoded = encode_jid(From),
67 10 ToEncoded = encode_jid(To),
68 10 ThreadEncoded = encode_thread(Thread),
69 10 TypeEncoded = encode_type(Type),
70 10 KeyValues = [FromEncoded, ToEncoded, ThreadEncoded, TypeEncoded],
71 10 UpdateValues = [Id, TS],
72 10 InsertValues = KeyValues ++ UpdateValues,
73 10 Res = rdbms_queries:execute_upsert(HostType, smart_markers_upsert,
74 InsertValues, UpdateValues, KeyValues),
75 10 ok = check_upsert_result(Res).
76
77 do_get_chat_markers(HostType, To, Thread, TS) ->
78 4 {selected, ChatMarkers} = execute_select_chat_markers(HostType,
79 encode_jid(To),
80 encode_thread(Thread),
81 TS),
82 4 decode(ChatMarkers).
83
84 24 encode_jid(JID) -> jid:to_binary(jid:to_lus(JID)).
85
86 8 encode_thread(undefined) -> <<>>;
87 6 encode_thread(Thread) -> Thread.
88
89 8 encode_type(received) -> <<"R">>;
90 2 encode_type(displayed) -> <<"D">>;
91
:-(
encode_type(acknowledged) -> <<"A">>.
92
93 %% MySQL returns 1 when an upsert is an insert
94 %% and 2, when an upsert acts as update
95 10 check_upsert_result({updated, 1}) -> ok;
96
:-(
check_upsert_result({updated, 2}) -> ok;
97 check_upsert_result(Result) ->
98
:-(
{error, {bad_result, Result}}.
99
100 decode(ChatMarkersList) ->
101 4 [decode_record(ChatMarker) || ChatMarker <- ChatMarkersList].
102
103 decode_record({From, To, Thread, Type, Id, TS}) ->
104 6 #{from => decode_jid(From),
105 to => decode_jid(To),
106 thread => decode_thread(Thread),
107 type => decode_type(Type),
108 timestamp => decode_timestamp(TS),
109 id => Id}.
110
111 12 decode_jid(EncodedJID) -> jid:from_binary(EncodedJID).
112
113 4 decode_thread(<<>>) -> undefined;
114 2 decode_thread(Thread) -> Thread.
115
116 4 decode_type(<<"R">>) -> received;
117 2 decode_type(<<"D">>) -> displayed;
118
:-(
decode_type(<<"A">>) -> acknowledged.
119
120 decode_timestamp(EncodedTS) ->
121 6 mongoose_rdbms:result_to_integer(EncodedTS).
Line Hits Source