./ct_report/coverage/node_push.COVER.html

1 %%%-------------------------------------------------------------------
2 %%% @author Rafal Slota
3 %%% @copyright (C) 2017 Erlang Solutions Ltd.
4 %%% This software is released under the Apache License, Version 2.0
5 %%% cited in 'LICENSE.txt'.
6 %%% @end
7 %%%-------------------------------------------------------------------
8 %%% @doc
9 %%% Node implementation that proxies all published items to `push_notification' hook.
10 %%% @end
11 %%%-------------------------------------------------------------------
12 -module(node_push).
13 -author('rafal.slota@erlang-solutions.com').
14 -behaviour(gen_pubsub_node).
15
16 -include("mongoose.hrl").
17 -include("jlib.hrl").
18 -include("pubsub.hrl").
19
20 -export([based_on/0, init/3, terminate/2, options/0, features/0,
21 publish_item/9, node_to_path/1, should_delete_when_owner_removed/0]).
22
23
:-(
based_on() -> node_flat.
24
25 init(Host, ServerHost, Opts) ->
26
:-(
node_flat:init(Host, ServerHost, Opts),
27
:-(
ok.
28
29 terminate(Host, ServerHost) ->
30
:-(
node_flat:terminate(Host, ServerHost),
31
:-(
ok.
32
33 options() ->
34
:-(
[{deliver_payloads, true},
35 {notify_config, false},
36 {notify_delete, false},
37 {notify_retract, false},
38 {purge_offline, false},
39 {persist_items, false},
40 {max_items, 1},
41 {subscribe, true},
42 {access_model, whitelist},
43 {roster_groups_allowed, []},
44 {publish_model, open},
45 {notification_type, headline},
46 {max_payload_size, ?MAX_PAYLOAD_SIZE},
47 {send_last_published_item, on_sub_and_presence},
48 {deliver_notifications, true},
49 {presence_based_delivery, true}].
50
51 features() ->
52
:-(
[
53 <<"create-nodes">>,
54 <<"delete-nodes">>,
55 <<"modify-affiliations">>,
56 <<"publish">>,
57 <<"publish-options">>,
58 <<"publish-only-affiliation">>,
59 <<"purge-nodes">>,
60 <<"retrieve-affiliations">>
61 ].
62
63 publish_item(ServerHost, Nidx, Publisher, Model, _MaxItems, _ItemId, _ItemPublisher, Payload,
64 PublishOptions) ->
65
:-(
{ok, Affiliation} = mod_pubsub_db_backend:get_affiliation(Nidx, jid:to_lower(Publisher)),
66
:-(
ElPayload = [El || #xmlel{} = El <- Payload],
67
68
:-(
case is_allowed_to_publish(Model, Affiliation) of
69 true ->
70
:-(
do_publish_item(ServerHost, PublishOptions, ElPayload);
71 false ->
72
:-(
{error, mongoose_xmpp_errors:forbidden()}
73 end.
74
75 do_publish_item(ServerHost, PublishOptions,
76 [#xmlel{name = <<"notification">>} | _] = Notifications) ->
77
:-(
case catch parse_form(PublishOptions) of
78 #{<<"device_id">> := _, <<"service">> := _} = OptionMap ->
79
:-(
NotificationRawForms = [exml_query:subelement(El, <<"x">>) || El <- Notifications],
80
:-(
NotificationForms = [parse_form(Form) || Form <- NotificationRawForms],
81
:-(
Result = mongoose_hooks:push_notifications(ServerHost, ok,
82 NotificationForms, OptionMap),
83
:-(
handle_push_hook_result(Result);
84 _ ->
85
:-(
{error, mod_pubsub:extended_error(mongoose_xmpp_errors:conflict(), <<"precondition-not-met">>)}
86 end;
87 do_publish_item(_ServerHost, _PublishOptions, _Payload) ->
88
:-(
{error, mongoose_xmpp_errors:bad_request()}.
89
90 handle_push_hook_result(ok) ->
91
:-(
{result, default};
92 handle_push_hook_result({error, device_not_registered}) ->
93
:-(
{error, mod_pubsub:extended_error(mongoose_xmpp_errors:not_acceptable_cancel(), <<"device-not-registered">>)};
94 handle_push_hook_result({error, _}) ->
95
:-(
{error, mod_pubsub:extended_error(mongoose_xmpp_errors:bad_request(), <<"faild-to-submit-push-notification">>)}.
96
97 node_to_path(Node) ->
98
:-(
node_flat:node_to_path(Node).
99
100
:-(
should_delete_when_owner_removed() -> true.
101
102 %%%
103 %%% Internal
104 %%%
105
106 is_allowed_to_publish(PublishModel, Affiliation) ->
107 (PublishModel == open)
108
:-(
or (PublishModel == publishers)
109 and ((Affiliation == owner)
110 or (Affiliation == publisher)
111 or (Affiliation == publish_only)).
112
113
114 -spec parse_form(undefined | exml:element()) -> invalid_form | #{atom() => binary()}.
115 parse_form(undefined) ->
116
:-(
#{};
117 parse_form(Form) ->
118
:-(
IsForm = ?NS_XDATA == exml_query:attr(Form, <<"xmlns">>),
119
:-(
IsSubmit = <<"submit">> == exml_query:attr(Form, <<"type">>, <<"submit">>),
120
121
:-(
FieldsXML = exml_query:subelements(Form, <<"field">>),
122
:-(
Fields = [{exml_query:attr(Field, <<"var">>),
123
:-(
exml_query:path(Field, [{element, <<"value">>}, cdata])} || Field <- FieldsXML],
124
:-(
{_, CustomFields} = lists:partition(
125 fun({Name, _}) ->
126
:-(
Name == <<"FORM_TYPE">>
127 end, Fields),
128
129
:-(
case IsForm andalso IsSubmit of
130 true ->
131
:-(
maps:from_list(CustomFields);
132 false ->
133
:-(
invalid_form
134 end.
Line Hits Source