Allow multiple push services to be enabled and also allow disabling

This commit is contained in:
JC Brand 2018-06-07 16:25:59 +02:00
parent 5954cd8f29
commit a09333f82c
4 changed files with 252 additions and 118 deletions

78
dist/converse.js vendored
View File

@ -73353,36 +73353,64 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
__ = _converse.__; __ = _converse.__;
_converse.api.settings.update({ _converse.api.settings.update({
'push_service': undefined, 'push_services': []
'push_service_node': undefined,
'push_service_secret': undefined
}); });
function enablePush() { function disablePushService(push_service) {
if (_converse.session.get('push_enabled')) { if (!push_service.jid) {
return; return;
} }
if (_converse.push_service && _converse.push_service_node) { Promise.all([_converse.api.disco.supports(Strophe.NS.PUSH, _converse.bare_jid)]).then(result => {
_converse.api.disco.getIdentity('pubsub', 'push', _converse.push_service).then(identity => { if (!result[0].length && !result[1].length) {
if (!identity) { return _converse.log(`Not disabling push service "${push_service.jid}", no disco support from your server.`, Strophe.LogLevel.WARN);
return _converse.log(`Not enabling push the service "${_converse.push_service}", it doesn't have the right disco identtiy.`, Strophe.LogLevel.WARN);
} }
return Promise.all([_converse.api.disco.supports(Strophe.NS.PUSH, _converse.push_service), _converse.api.disco.supports(Strophe.NS.PUSH, _converse.bare_jid)]).then(result => { const stanza = $iq({
'type': 'set'
}).c('disable', {
'xmlns': Strophe.NS.PUSH,
'jid': push_service.jid
});
if (push_service.node) {
stanza.attrs({
'node': push_service.node
});
}
_converse.api.sendIQ(stanza).then(() => _converse.session.set('push_enabled', true)).catch(e => {
_converse.log(`Could not enable push service for ${push_service.jid}`, Strophe.LogLevel.ERROR);
_converse.log(e, Strophe.LogLevel.ERROR);
});
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}
function enablePushService(push_service) {
if (!push_service.jid || !push_service.node) {
return;
}
_converse.api.disco.getIdentity('pubsub', 'push', push_service.jid).then(identity => {
if (!identity) {
return _converse.log(`Not enabling push the service "${push_service.jid}", it doesn't have the right disco identtiy.`, Strophe.LogLevel.WARN);
}
return Promise.all([_converse.api.disco.supports(Strophe.NS.PUSH, push_service.jid), _converse.api.disco.supports(Strophe.NS.PUSH, _converse.bare_jid)]).then(result => {
if (!result[0].length && !result[1].length) { if (!result[0].length && !result[1].length) {
return _converse.log(`Not enabling push service "${_converse.push_service}", no disco support`, Strophe.LogLevel.WARN); return _converse.log(`Not enabling push service "${push_service.jid}", no disco support from your server.`, Strophe.LogLevel.WARN);
} }
const stanza = $iq({ const stanza = $iq({
'type': 'set' 'type': 'set'
}).c('enable', { }).c('enable', {
'xmlns': Strophe.NS.PUSH, 'xmlns': Strophe.NS.PUSH,
'jid': _converse.push_service, 'jid': push_service.jid,
'node': _converse.push_service_node 'node': push_service.node
}); });
if (_converse.push_service_secret) { if (push_service.secret) {
stanza.c('x', { stanza.c('x', {
'xmlns': Strophe.NS.XFORM, 'xmlns': Strophe.NS.XFORM,
'type': 'submit' 'type': 'submit'
@ -73390,17 +73418,35 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
'var': 'FORM_TYPE' 'var': 'FORM_TYPE'
}).c('value').t(`${Strophe.NS.PUBSUB}#publish-options`).up().up().c('field', { }).c('value').t(`${Strophe.NS.PUBSUB}#publish-options`).up().up().c('field', {
'var': 'secret' 'var': 'secret'
}).c('value').t(_converse.push_service_secret); }).c('value').t(push_service.secret);
} }
_converse.api.sendIQ(stanza).then(() => _converse.session.set('push_enabled', true)).catch(e => { _converse.api.sendIQ(stanza).then(() => _converse.session.set('push_enabled', true)).catch(e => {
_converse.log(`Could not enable push service for ${_converse.push_service}`, Strophe.LogLevel.ERROR); _converse.log(`Could not enable push service for ${push_service.jid}`, Strophe.LogLevel.ERROR);
_converse.log(e, Strophe.LogLevel.ERROR); _converse.log(e, Strophe.LogLevel.ERROR);
}); });
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
} }
function enablePush() {
if (_converse.session.get('push_enabled')) {
// XXX: this code is still a bit naive. We set push_enabled
// to true as soon as the first push service has been set.
//
// When enabling or disabling multiple push services,
// we won't wait until we have confirmation that all have been set.
return;
}
const enabled_services = _.reject(_converse.push_services, 'disable');
_.each(enabled_services, enablePushService);
const disabled_services = _.filter(_converse.push_services, 'disable');
_.each(disabled_services, disablePushService);
} }
_converse.api.listen.on('statusInitialized', enablePush); _converse.api.listen.on('statusInitialized', enablePush);

View File

@ -1068,38 +1068,38 @@ providers_link
The hyperlink on the registration form which points to a directory of public The hyperlink on the registration form which points to a directory of public
XMPP servers. XMPP servers.
push_service push_services
------------ -------------
* Default: ``undefined`` * Default: ``[]``
This option allows you to specify a URI for the push notifications service This option lets you enable or disable so-called push notification "App Servers"
(called an "App Server" by `XEP-0357 <https://xmpp.org/extensions/xep-0357.html>`_). (as per `XEP-0357 <https://xmpp.org/extensions/xep-0357.html>`_).
If provided, together with a `push_service_node`_, then Converse will instruct For each "App Server" an object needs to be passed in. When enabling, you need
the user's XMPP server to send push notificatiosn to that URI. to specify ``jid`` and ``node`` values. You can also provide a
``secret``, if required by your App Server.
push_service_node When disabling, you need to specify at least a ``jid`` and set ``disabled`` to
----------------- ``true``. This will disable notifications to all pubsub nodes on that "App
Server". If you want to disable only a particular node, then specify a ``node``
value as well.
* Default: ``undefined`` For example:
This is the PubSub node of the push notifications service (aka "App Server") specified with the
`push_service`_ setting.
Push notifications will be sent to this node. If this value is not set, then .. code-block:: javascript
push notifications won't be sent out.
push_service_secret converse.initialize({
------------------- 'push_services': [{
'jid': 'push-4@client.example',
* Default: ``undefined`` 'node': 'yxs32uqsflafdk3iuqo',
'disable': true
Some push notification services (aka "App Servers") require a secret token to }, {
be used when sending out notifications. 'jid': 'push-5@client.example',
'node': 'yxs32uqsflafdk3iuqo',
This setting enables you to provide such a secret to Converse which will }]
forward it to your XMPP server to be included in push notifications. });
root root
---- ----

View File

@ -8,22 +8,22 @@
describe("XEP-0357 Push Notifications", function () { describe("XEP-0357 Push Notifications", function () {
it("can be enabled by specifying a push_service and push_service_node", it("can be enabled",
mock.initConverseWithPromises(null, mock.initConverseWithPromises(null,
['rosterGroupsFetched'], { ['rosterGroupsFetched'], {
'push_service': 'push-5@client.example', 'push_services': [{
'push_service_node': 'yxs32uqsflafdk3iuqo' 'jid': 'push-5@client.example',
'node': 'yxs32uqsflafdk3iuqo'
}]
}, function (done, _converse) { }, function (done, _converse) {
const IQ_stanzas = _converse.connection.IQ_stanzas; const IQ_stanzas = _converse.connection.IQ_stanzas;
let stanza; let stanza;
expect(_converse.push_service).toBe('push-5@client.example');
expect(_converse.push_service_node).toBe('yxs32uqsflafdk3iuqo');
expect(_converse.session.get('push_enabled')).toBeFalsy(); expect(_converse.session.get('push_enabled')).toBeFalsy();
test_utils.waitUntilDiscoConfirmed( test_utils.waitUntilDiscoConfirmed(
_converse, _converse.push_service, _converse, _converse.push_services[0].jid,
[{'category': 'pubsub', 'type':'push'}], [{'category': 'pubsub', 'type':'push'}],
['urn:xmpp:push:0'], [], 'info') ['urn:xmpp:push:0'], [], 'info')
.then(() => test_utils.waitUntilDiscoConfirmed( .then(() => test_utils.waitUntilDiscoConfirmed(
@ -58,25 +58,70 @@
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
})); }));
it("can be disabled",
it("can require a secret token to be included",
mock.initConverseWithPromises(null, mock.initConverseWithPromises(null,
['rosterGroupsFetched'], { ['rosterGroupsFetched'], {
'push_service': 'push-5@client.example', 'push_services': [{
'push_service_node': 'yxs32uqsflafdk3iuqo', 'jid': 'push-5@client.example',
'push_service_secret': 'eruio234vzxc2kla-91' 'node': 'yxs32uqsflafdk3iuqo',
'disable': true
}]
}, function (done, _converse) { }, function (done, _converse) {
const IQ_stanzas = _converse.connection.IQ_stanzas; const IQ_stanzas = _converse.connection.IQ_stanzas;
let stanza; let stanza;
expect(_converse.push_service).toBe('push-5@client.example');
expect(_converse.push_service_node).toBe('yxs32uqsflafdk3iuqo');
expect(_converse.push_service_secret).toBe('eruio234vzxc2kla-91');
expect(_converse.session.get('push_enabled')).toBeFalsy(); expect(_converse.session.get('push_enabled')).toBeFalsy();
test_utils.waitUntilDiscoConfirmed( test_utils.waitUntilDiscoConfirmed(
_converse, _converse.push_service, _converse,
_converse.bare_jid,
[{'category': 'account', 'type':'registered'}],
['urn:xmpp:push:0'], [], 'info')
.then(() => {
return test_utils.waitUntil(() => {
const node = _.filter(IQ_stanzas, function (iq) {
return iq.nodeTree.querySelector('iq[type="set"] disable[xmlns="urn:xmpp:push:0"]');
}).pop();
if (node) {
stanza = node.nodeTree;
return true;
}
})
}).then(() => {
expect(stanza.outerHTML).toEqual(
`<iq type="set" xmlns="jabber:client" id="${stanza.getAttribute('id')}">`+
'<disable xmlns="urn:xmpp:push:0" jid="push-5@client.example" node="yxs32uqsflafdk3iuqo"/>'+
'</iq>'
)
_converse.connection._dataRecv(test_utils.createRequest($iq({
'to': _converse.connection.jid,
'type': 'result',
'id': stanza.getAttribute('id')
})));
return test_utils.waitUntil(() => _converse.session.get('push_enabled'))
}).then(() => {
done();
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}));
it("can require a secret token to be included",
mock.initConverseWithPromises(null,
['rosterGroupsFetched'], {
'push_services': [{
'jid': 'push-5@client.example',
'node': 'yxs32uqsflafdk3iuqo',
'secret': 'eruio234vzxc2kla-91'
}]
}, function (done, _converse) {
const IQ_stanzas = _converse.connection.IQ_stanzas;
let stanza;
expect(_converse.session.get('push_enabled')).toBeFalsy();
test_utils.waitUntilDiscoConfirmed(
_converse, _converse.push_services[0].jid,
[{'category': 'pubsub', 'type':'push'}], [{'category': 'pubsub', 'type':'push'}],
['urn:xmpp:push:0'], [], 'info') ['urn:xmpp:push:0'], [], 'info')
.then(() => test_utils.waitUntilDiscoConfirmed( .then(() => test_utils.waitUntilDiscoConfirmed(

View File

@ -27,56 +27,99 @@
{ __ } = _converse; { __ } = _converse;
_converse.api.settings.update({ _converse.api.settings.update({
'push_service': undefined, 'push_services': [],
'push_service_node': undefined,
'push_service_secret': undefined
}); });
function enablePush() { function disablePushService (push_service) {
if (_converse.session.get('push_enabled')) { if (!push_service.jid) {
return; return;
} }
if (_converse.push_service && _converse.push_service_node) { Promise.all([
_converse.api.disco.getIdentity('pubsub', 'push', _converse.push_service)
.then((identity) => {
if (!identity) {
return _converse.log(
`Not enabling push the service "${_converse.push_service}", it doesn't have the right disco identtiy.`,
Strophe.LogLevel.WARN
);
}
return Promise.all([
_converse.api.disco.supports(Strophe.NS.PUSH, _converse.push_service),
_converse.api.disco.supports(Strophe.NS.PUSH, _converse.bare_jid) _converse.api.disco.supports(Strophe.NS.PUSH, _converse.bare_jid)
]).then((result) => { ]).then((result) => {
if (!result[0].length && !result[1].length) { if (!result[0].length && !result[1].length) {
return _converse.log( return _converse.log(
`Not enabling push service "${_converse.push_service}", no disco support`, `Not disabling push service "${push_service.jid}", no disco support from your server.`,
Strophe.LogLevel.WARN
);
}
const stanza = $iq({'type': 'set'})
.c('disable', {
'xmlns': Strophe.NS.PUSH,
'jid': push_service.jid,
});
if (push_service.node) {
stanza.attrs({'node': push_service.node});
}
_converse.api.sendIQ(stanza)
.then(() => _converse.session.set('push_enabled', true))
.catch((e) => {
_converse.log(`Could not enable push service for ${push_service.jid}`, Strophe.LogLevel.ERROR);
_converse.log(e, Strophe.LogLevel.ERROR);
});
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}
function enablePushService (push_service) {
if (!push_service.jid || !push_service.node) {
return;
}
_converse.api.disco.getIdentity('pubsub', 'push', push_service.jid)
.then((identity) => {
if (!identity) {
return _converse.log(
`Not enabling push the service "${push_service.jid}", it doesn't have the right disco identtiy.`,
Strophe.LogLevel.WARN
);
}
return Promise.all([
_converse.api.disco.supports(Strophe.NS.PUSH, push_service.jid),
_converse.api.disco.supports(Strophe.NS.PUSH, _converse.bare_jid)
]).then((result) => {
if (!result[0].length && !result[1].length) {
return _converse.log(
`Not enabling push service "${push_service.jid}", no disco support from your server.`,
Strophe.LogLevel.WARN Strophe.LogLevel.WARN
); );
} }
const stanza = $iq({'type': 'set'}) const stanza = $iq({'type': 'set'})
.c('enable', { .c('enable', {
'xmlns': Strophe.NS.PUSH, 'xmlns': Strophe.NS.PUSH,
'jid': _converse.push_service, 'jid': push_service.jid,
'node': _converse.push_service_node 'node': push_service.node
}); });
if (_converse.push_service_secret) { if (push_service.secret) {
stanza.c('x', {'xmlns': Strophe.NS.XFORM, 'type': 'submit'}) stanza.c('x', {'xmlns': Strophe.NS.XFORM, 'type': 'submit'})
.c('field', {'var': 'FORM_TYPE'}) .c('field', {'var': 'FORM_TYPE'})
.c('value').t(`${Strophe.NS.PUBSUB}#publish-options`).up().up() .c('value').t(`${Strophe.NS.PUBSUB}#publish-options`).up().up()
.c('field', {'var': 'secret'}) .c('field', {'var': 'secret'})
.c('value').t(_converse.push_service_secret); .c('value').t(push_service.secret);
} }
_converse.api.sendIQ(stanza) _converse.api.sendIQ(stanza)
.then(() => _converse.session.set('push_enabled', true)) .then(() => _converse.session.set('push_enabled', true))
.catch((e) => { .catch((e) => {
_converse.log(`Could not enable push service for ${_converse.push_service}`, Strophe.LogLevel.ERROR); _converse.log(`Could not enable push service for ${push_service.jid}`, Strophe.LogLevel.ERROR);
_converse.log(e, Strophe.LogLevel.ERROR); _converse.log(e, Strophe.LogLevel.ERROR);
}); });
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
} }
function enablePush () {
if (_converse.session.get('push_enabled')) {
// XXX: this code is still a bit naive. We set push_enabled
// to true as soon as the first push service has been set.
//
// When enabling or disabling multiple push services,
// we won't wait until we have confirmation that all have been set.
return;
}
const enabled_services = _.reject(_converse.push_services, 'disable');
_.each(enabled_services, enablePushService);
const disabled_services = _.filter(_converse.push_services, 'disable');
_.each(disabled_services, disablePushService);
} }
_converse.api.listen.on('statusInitialized', enablePush); _converse.api.listen.on('statusInitialized', enablePush);
} }