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 |
:-( |
KeyFields = [<<"from_jid">>, <<"to_jid">>, <<"thread">>, <<"type">>], |
19 |
:-( |
UpdateFields = [<<"msg_id">>, <<"timestamp">>], |
20 |
:-( |
InsertFields = KeyFields ++ UpdateFields, |
21 |
:-( |
QueryName = smart_markers_upsert, |
22 |
:-( |
rdbms_queries:prepare_upsert(HostType, QueryName, smart_markers, |
23 |
|
InsertFields, UpdateFields, KeyFields), |
24 |
:-( |
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 |
:-( |
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 |
:-( |
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 |
:-( |
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 |
:-( |
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 |
:-( |
FromEncoded = encode_jid(From), |
67 |
:-( |
ToEncoded = encode_jid(To), |
68 |
:-( |
ThreadEncoded = encode_thread(Thread), |
69 |
:-( |
TypeEncoded = encode_type(Type), |
70 |
:-( |
KeyValues = [FromEncoded, ToEncoded, ThreadEncoded, TypeEncoded], |
71 |
:-( |
UpdateValues = [Id, TS], |
72 |
:-( |
InsertValues = KeyValues ++ UpdateValues, |
73 |
:-( |
Res = rdbms_queries:execute_upsert(HostType, smart_markers_upsert, |
74 |
|
InsertValues, UpdateValues, KeyValues), |
75 |
:-( |
ok = check_upsert_result(Res). |
76 |
|
|
77 |
|
do_get_chat_markers(HostType, To, Thread, TS) -> |
78 |
:-( |
{selected, ChatMarkers} = execute_select_chat_markers(HostType, |
79 |
|
encode_jid(To), |
80 |
|
encode_thread(Thread), |
81 |
|
TS), |
82 |
:-( |
decode(ChatMarkers). |
83 |
|
|
84 |
:-( |
encode_jid(JID) -> jid:to_binary(jid:to_lus(JID)). |
85 |
|
|
86 |
:-( |
encode_thread(undefined) -> <<>>; |
87 |
:-( |
encode_thread(Thread) -> Thread. |
88 |
|
|
89 |
:-( |
encode_type(received) -> <<"R">>; |
90 |
:-( |
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 |
:-( |
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 |
:-( |
[decode_record(ChatMarker) || ChatMarker <- ChatMarkersList]. |
102 |
|
|
103 |
|
decode_record({From, To, Thread, Type, Id, TS}) -> |
104 |
:-( |
#{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 |
:-( |
decode_jid(EncodedJID) -> jid:from_binary(EncodedJID). |
112 |
|
|
113 |
:-( |
decode_thread(<<>>) -> undefined; |
114 |
:-( |
decode_thread(Thread) -> Thread. |
115 |
|
|
116 |
:-( |
decode_type(<<"R">>) -> received; |
117 |
:-( |
decode_type(<<"D">>) -> displayed; |
118 |
:-( |
decode_type(<<"A">>) -> acknowledged. |
119 |
|
|
120 |
|
decode_timestamp(EncodedTS) -> |
121 |
:-( |
mongoose_rdbms:result_to_integer(EncodedTS). |