Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MongoosePush API v3 mock #2549

Merged
merged 7 commits into from
Dec 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions big_tests/tests/mongoose_push_mock.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
-export([stop/0]).
-export([port/0]).
-export([init/2]).
-export([subscribe/1]).
-export([subscribe/2]).
-export([wait_for_push_request/1]).

start(Config) ->
Expand All @@ -13,7 +13,10 @@ start(Config) ->
CertPath = path_helper:canonicalize_path(filename:join(CertDir, "cert.pem")),
KeyPath = path_helper:canonicalize_path(filename:join(CertDir, "key.pem")),

Dispatch = cowboy_router:compile([{'_', [{<<"/v2/notification/:token">>, ?MODULE, #{}}]}]),
Dispatch = cowboy_router:compile([{'_', [{<<"/v2/notification/:token">>, ?MODULE, #{}},
{<<"/v3/notification/:token">>, ?MODULE, #{}}
]}
]),
{ok, Pid} = cowboy:start_tls(mongoose_push_https_mock,
[{certfile, CertPath}, {keyfile, KeyPath}],
#{env => #{dispatch => Dispatch}}),
Expand All @@ -26,22 +29,22 @@ port() ->
stop() ->
cowboy:stop_listener(mongoose_push_https_mock).

subscribe(Token) ->
ets:insert(mongoose_push_mock_subscribers, {Token, self()}).
subscribe(Token, Response) ->
ets:insert(mongoose_push_mock_subscribers, {Token, self(), Response}).

wait_for_push_request(Token) ->
receive
{push_request, Token, Body} ->
Body
{push_request, Token, Body, Resp} ->
{jiffy:decode(Body, [return_maps]), Resp}
after 10000 ->
ct:fail("timeout_waiting_for_push_request")
end.

init(Req, State) ->
Token = cowboy_req:binding(token, Req),
{ok, Body, Req2} = cowboy_req:read_body(Req),
[{_, Subscriber}] = ets:lookup(mongoose_push_mock_subscribers, Token),
Subscriber ! {push_request, Token, Body},
Req3 = cowboy_req:reply(204, #{}, <<>>, Req2),
[{_, Subscriber, {RespStatus, RespBody} = Resp}] = ets:lookup(mongoose_push_mock_subscribers, Token),
Subscriber ! {push_request, Token, Body, Resp},
Req3 = cowboy_req:reply(RespStatus, #{}, RespBody, Req2),
{ok, Req3, State}.

110 changes: 80 additions & 30 deletions big_tests/tests/push_integration_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ all() ->
{group, pm_msg_notifications},
{group, muclight_msg_notifications},
{group, pm_notifications_with_inbox},
{group, groupchat_notifications_with_inbox}
{group, groupchat_notifications_with_inbox},
{group, failure_cases_v3},
{group, failure_cases_v2}
].

groups() ->
Expand Down Expand Up @@ -74,10 +76,16 @@ groups() ->
inbox_msg_unread_count_fcm,
inbox_msg_reset_unread_count_apns,
inbox_msg_reset_unread_count_fcm
]}
]},
{failure_cases_v3, [parallel], failure_cases()},
{failure_cases_v2, [parallel], failure_cases()}
],
G.

failure_cases() ->
[no_push_notification_for_expired_device,
no_push_notification_for_internal_mongoose_push_error].

%% ct_helper:repeat_all_until_all_ok(G).

suite() ->
Expand All @@ -94,7 +102,7 @@ init_per_suite(Config0) ->

%% Start modules
Config = dynamic_modules:save_modules(domain(), Config0),
dynamic_modules:ensure_modules(domain(), required_modules()),
dynamic_modules:ensure_modules(domain(), required_modules("v3")),

PoolOpts = [{strategy, available_worker}, {workers, 20}],
HTTPOpts = [{server, "https://localhost:" ++ integer_to_list(Port)}],
Expand Down Expand Up @@ -144,7 +152,7 @@ pm_msg_notify_on_apns(Config, EnableOpts) ->
Config, [{bob, 1}, {alice, 1}],
fun(Bob, Alice) ->
{SenderJID, DeviceToken} = pm_conversation(Alice, Bob, <<"apns">>, EnableOpts),
Notification = wait_for_push_request(DeviceToken),
{Notification, _} = wait_for_push_request(DeviceToken),

assert_push_notification(Notification, <<"apns">>, EnableOpts, SenderJID, [])

Expand Down Expand Up @@ -195,7 +203,7 @@ pm_msg_notify_on_fcm(Config, EnableOpts) ->
Config, [{bob, 1}, {alice, 1}],
fun(Bob, Alice) ->
{SenderJID, DeviceToken} = pm_conversation(Alice, Bob, <<"fcm">>, EnableOpts),
Notification = wait_for_push_request(DeviceToken),
{Notification, _} = wait_for_push_request(DeviceToken),

assert_push_notification(Notification, <<"fcm">>, EnableOpts, SenderJID)

Expand Down Expand Up @@ -313,13 +321,13 @@ muclight_inbox_msg_unread_count(Config, Service, EnableOpts) ->

SenderJID = muclight_conversation(Alice, RoomJID, <<"First!">>),
escalus:wait_for_stanza(Alice),
Notification = wait_for_push_request(KateToken),
{Notification, _} = wait_for_push_request(KateToken),
assert_push_notification(Notification, Service, EnableOpts, SenderJID,
[{body, <<"First!">>}, {unread_count, 1}, {badge, 1}]),

muclight_conversation(Alice, RoomJID, <<"Second!">>),
escalus:wait_for_stanza(Alice),
Notification2 = wait_for_push_request(KateToken),
{Notification2, _} = wait_for_push_request(KateToken),
assert_push_notification(Notification2, Service, EnableOpts, SenderJID,
[{body, <<"Second!">>}, {unread_count, 2}, {badge, 1}]),

Expand All @@ -344,7 +352,7 @@ send_private_message(Sender, Recipient, Body) ->
Id.

check_notification(DeviceToken, ExpectedCount) ->
Notification = wait_for_push_request(DeviceToken),
{Notification, _} = wait_for_push_request(DeviceToken),
Data = maps:get(<<"data">>, Notification, undefined),
?assertMatch(#{<<"message-count">> := ExpectedCount}, Data).

Expand All @@ -366,7 +374,7 @@ muclight_msg_notify_on_apns(Config, EnableOpts) ->
DeviceToken = enable_push_for_user(Bob, <<"apns">>, EnableOpts),

SenderJID = muclight_conversation(Alice, RoomJID, <<"Heyah!">>),
Notification = wait_for_push_request(DeviceToken),
{Notification, _} = wait_for_push_request(DeviceToken),
assert_push_notification(Notification, <<"apns">>, EnableOpts, SenderJID,
[{body, <<"Heyah!">>}, {unread_count, 1}, {badge, 1}])
end).
Expand All @@ -380,7 +388,7 @@ muclight_msg_notify_on_fcm(Config, EnableOpts) ->
DeviceToken = enable_push_for_user(Bob, <<"fcm">>, EnableOpts),

SenderJID = muclight_conversation(Alice, RoomJID, <<"Heyah!">>),
Notification = wait_for_push_request(DeviceToken),
{Notification, _} = wait_for_push_request(DeviceToken),
assert_push_notification(Notification, <<"fcm">>,
EnableOpts, SenderJID, [{body, <<"Heyah!">>}, {unread_count, 1}, {badge, 1}])
end).
Expand All @@ -404,7 +412,7 @@ muclight_aff_change(Config, Service, EnableOpts) ->
{Room, Body, M1} = when_muc_light_message_is_sent(Alice, Room, <<"First!">>, <<"M1">>),
then_muc_light_message_is_received_by([Alice], {Room, Body, M1}),

Notification = wait_for_push_request(KateToken),
{Notification, _} = wait_for_push_request(KateToken),
assert_push_notification(Notification, Service, EnableOpts, SenderJID,
[{body, <<"First!">>}, {unread_count, 1}, {badge, 1}]),

Expand All @@ -415,7 +423,7 @@ muclight_aff_change(Config, Service, EnableOpts) ->
{_, B2, M2} = when_muc_light_message_is_sent(Alice, Room, <<"Second!">>, <<"M2">>),
then_muc_light_message_is_received_by([Alice, Bob], {Room, B2, M2}),

Notification2 = wait_for_push_request(KateToken),
{Notification2, _} = wait_for_push_request(KateToken),
assert_push_notification(Notification2, Service, EnableOpts, SenderJID,
[{body, <<"Second!">>}, {unread_count, 2}, {badge, 1}])

Expand Down Expand Up @@ -448,10 +456,44 @@ muclight_aff_change_fcm(Config) ->

muclight_aff_change_apns(Config) ->
muclight_aff_change(Config, <<"apns">>, [{<<"silent">>, <<"true">>}]).

no_push_notification_for_expired_device(Config) ->
escalus:fresh_story(
Config, [{bob, 1}, {alice, 1}],
fun(Bob, Alice) ->
Response = mongoose_push_unregistered_device_resp(Config),
DeviceToken = enable_push_for_user(Bob, <<"fcm">>, [], Response),
escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)),
{_, Response} = wait_for_push_request(DeviceToken)

end).

mongoose_push_unregistered_device_resp(Config) ->
case ?config(api_v, Config) of
"v3" ->
{410, jiffy:encode(#{<<"reason">> => <<"unregistered">>})};
"v2" ->
{500, jiffy:encode(#{<<"details">> => <<"probably_unregistered">>})}
end.

no_push_notification_for_internal_mongoose_push_error(Config) ->
escalus:fresh_story(
Config, [{bob, 1}, {alice, 1}],
fun(Bob, Alice) ->
Response = {503, jiffy:encode(#{<<"reason">> => <<"unspecified">>})},
DeviceToken = enable_push_for_user(Bob, <<"fcm">>, [], Response),
escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)),
{_, Response} = wait_for_push_request(DeviceToken)

end).


%%--------------------------------------------------------------------
%% Test helpers
%%--------------------------------------------------------------------



muclight_conversation(Sender, RoomJID, Msg) ->
Bare = bare_jid(Sender),
SenderJID = <<RoomJID/binary, "/", Bare/binary>>,
Expand All @@ -466,6 +508,9 @@ pm_conversation(Alice, Bob, Service, EnableOpts) ->
{AliceJID, DeviceToken}.

enable_push_for_user(User, Service, EnableOpts) ->
enable_push_for_user(User, Service, EnableOpts, {200, <<"OK">>}).

enable_push_for_user(User, Service, EnableOpts, MockResponse) ->
PubsubJID = node_addr(),
Node = {_, NodeName} = pubsub_node(),

Expand All @@ -479,14 +524,13 @@ enable_push_for_user(User, Service, EnableOpts) ->
[{<<"service">>, Service},
{<<"device_id">>, DeviceToken}] ++ EnableOpts)),
escalus:assert(is_iq_result, escalus:wait_for_stanza(User)),
mongoose_push_mock:subscribe(DeviceToken),
mongoose_push_mock:subscribe(DeviceToken, MockResponse),
become_unavailable(User),
DeviceToken.


wait_for_push_request(DeviceToken) ->
Body = mongoose_push_mock:wait_for_push_request(DeviceToken),
jiffy:decode(Body, [return_maps]).
mongoose_push_mock:wait_for_push_request(DeviceToken).


%% ----------------------------------
Expand Down Expand Up @@ -554,29 +598,35 @@ h2_req(Conn, Method, Path, Body) ->
end.

init_modules(G, Config) ->
Modules = required_modules(G),
MongoosePushAPI = mongoose_push_api_for_group(G),
Modules = required_modules_for_group(G, MongoosePushAPI),
C = dynamic_modules:save_modules(domain(), Config),
dynamic_modules:ensure_modules(domain(), Modules),
C.


required_modules(pm_notifications_with_inbox) ->
[{mod_inbox, inbox_opts()}|required_modules()];
required_modules(groupchat_notifications_with_inbox)->
[{mod_inbox, inbox_opts()}, {mod_muc_light, muc_light_opts()}|required_modules()];
required_modules(muclight_msg_notifications) ->
[{mod_muc_light, muc_light_opts()}|required_modules()];
required_modules(_) ->
required_modules().

required_modules() ->
[{api_v, MongoosePushAPI} | C].

mongoose_push_api_for_group(failure_cases_v2) ->
"v2";
mongoose_push_api_for_group(_) ->
"v3".

required_modules_for_group(pm_notifications_with_inbox, API) ->
[{mod_inbox, inbox_opts()} | required_modules(API)];
required_modules_for_group(groupchat_notifications_with_inbox, API)->
[{mod_inbox, inbox_opts()}, {mod_muc_light, muc_light_opts()} |
required_modules(API)];
required_modules_for_group(muclight_msg_notifications, API) ->
[{mod_muc_light, muc_light_opts()} | required_modules(API)];
required_modules_for_group(_, API) ->
required_modules(API).

required_modules(API) ->
[
{mod_pubsub, [{plugins, [<<"dag">>, <<"push">>]},
{backend, mongoose_helper:mnesia_or_rdbms_backend()},
{nodetree, <<"dag">>},
{host, "pubsub.@HOST@"}]},
{mod_push_service_mongoosepush, [{pool_name, mongoose_push_http},
{api_version, "v2"}]},
{api_version, API}]},
{mod_push, [{backend, mongoose_helper:mnesia_or_rdbms_backend()}]}
].

Expand Down
7 changes: 5 additions & 2 deletions doc/migrations/3.5.0_3.5.0plus.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Push notifications

In this version, push notifications work with MongoosePush 2.0.0 and it's API v3 by default.

## Different MUC Light room schema definition

We have introduced a change that enforces defining fields with default values.
Expand Down Expand Up @@ -68,7 +72,7 @@ The current method makes it impossible to make the same mistake, as it disallows
{"priority", 10, priority, integer},
{"owners-height", 180.0, owners_height, float}
]}

```

### What has changed? - for developers
Expand All @@ -82,4 +86,3 @@ For more information, please check the specs for types and functions in the afor

* The default room config is still the same, i.e. `roomname` (default: `"Untitled"`) and `subject` (empty string).
* The room config representation in databases (both Mnesia and RDBMS) is the same; no need for migration.

4 changes: 2 additions & 2 deletions doc/modules/mod_push_service_mongoosepush.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ It must be defined in [outgoing_pools setting](../advanced-configuration/outgoin
### Options

* **pool_name** (atom, required) - name of the pool to use (as defined in `outgoing_pools`)
* **api_version** (string, default: `v2`) - REST API version to be used.
* **api_version** (string, default: `v3`) - REST API version to be used.
* **max_http_connections** (integer, default: 100) - the maximum amount of concurrent http connections

### Example configuration
Expand All @@ -39,6 +39,6 @@ It must be defined in [outgoing_pools setting](../advanced-configuration/outgoin

{mod_push_service_mongoosepush, [
{pool_name, mongoose_push_http}
{api_version, "v2"}
{api_version, "v3"}
]}.
```
6 changes: 3 additions & 3 deletions doc/user-guide/Push-notifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The following list shows those components as defined in

* _XMPP Server_ in MongooseIM is enabled by module [mod_event_pusher_push][]
* _App Server_ in MongooseIM is enabled by adding a `push` node type to [mod_pubsub][]'s configuration
* _XMPP Push Service_ is implemented as a [MongoosePush][] application
* _XMPP Push Service_ is implemented as a [MongoosePush][] application, the recommended version is 2.0.0 or above

All these entities have to be enabled and properly configured in order to use push notifications.
So let's get to it, shall we?
Expand Down Expand Up @@ -97,15 +97,15 @@ Then add `mod_push_service_mongoosepush` to the `modules` section in the config

{mod_push_service_mongoosepush, [
{pool_name, mongoose_push_http},
{api_version, "v2"}]},
{api_version, "v3"}]},

(...)
```

First, we create the HTTP pool for communicating with [MongoosePush][].
Here, we assume that [MongoosePush][] will be available on the localhost on port 8443 which is the default one.
Next we enable [mod_push_service_mongoosepush][].
First option is the name of the HTTP pool to use and the second one is the version of [MongoosePush][]'s API (currently only "_v2_" is supported).
First option is the name of the HTTP pool to use and the second one is the version of [MongoosePush][]'s API ("_v2_" or "_v3_" are supported).

And that's it, we've just completed the entire MongooseIM configuration.
All we need to do now is to set up [MongoosePush][].
Expand Down
Loading