Commit aae7e111 authored by JC Brand's avatar JC Brand

Fix roster caching and versioning issue.

- Always try to first get local cache before requesting the roster.
- Rename `roster_fetched` with `roster_cached`
parent 015f1c6f
...@@ -44,6 +44,13 @@ ...@@ -44,6 +44,13 @@
`<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+ `<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`); `<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
iq = IQ_stanzas[IQ_stanzas.length-1];
expect(Strophe.serialize(iq)).toBe(
`<iq id="${iq.getAttribute('id')}" type="get" xmlns="jabber:client"><query xmlns="jabber:iq:roster"/></iq>`);
await test_utils.waitForRoster(_converse, 'current', 1);
iq = IQ_stanzas.pop();
iq = IQ_stanzas.pop(); iq = IQ_stanzas.pop();
expect(Strophe.serialize(iq)).toBe( expect(Strophe.serialize(iq)).toBe(
`<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="montague.lit" type="get" xmlns="jabber:client">`+ `<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="montague.lit" type="get" xmlns="jabber:client">`+
...@@ -54,19 +61,13 @@ ...@@ -54,19 +61,13 @@
`<iq from="romeo@montague.lit" id="${disco_iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+ `<iq from="romeo@montague.lit" id="${disco_iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
`<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub></iq>`); `<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub></iq>`);
iq = IQ_stanzas[IQ_stanzas.length-1];
expect(Strophe.serialize(iq)).toBe(
`<iq id="${iq.getAttribute('id')}" type="get" xmlns="jabber:client"><query xmlns="jabber:iq:roster"/></iq>`);
await test_utils.waitForRoster(_converse, 'current', 1);
expect(sent_stanzas.filter(s => (s.nodeName === 'r')).length).toBe(2); expect(sent_stanzas.filter(s => (s.nodeName === 'r')).length).toBe(2);
expect(_converse.session.get('unacked_stanzas').length).toBe(5); expect(_converse.session.get('unacked_stanzas').length).toBe(5);
// test handling of acks // test handling of acks
let ack = u.toStanza(`<a xmlns="urn:xmpp:sm:3" h="1"/>`); let ack = u.toStanza(`<a xmlns="urn:xmpp:sm:3" h="2"/>`);
_converse.connection._dataRecv(test_utils.createRequest(ack)); _converse.connection._dataRecv(test_utils.createRequest(ack));
expect(_converse.session.get('unacked_stanzas').length).toBe(4); expect(_converse.session.get('unacked_stanzas').length).toBe(3);
// test handling of ack requests // test handling of ack requests
let r = u.toStanza(`<r xmlns="urn:xmpp:sm:3"/>`); let r = u.toStanza(`<r xmlns="urn:xmpp:sm:3"/>`);
...@@ -90,9 +91,9 @@ ...@@ -90,9 +91,9 @@
.c('feature', {'var': 'http://jabber.org/protocol/disco#items'}); .c('feature', {'var': 'http://jabber.org/protocol/disco#items'});
_converse.connection._dataRecv(test_utils.createRequest(disco_result)); _converse.connection._dataRecv(test_utils.createRequest(disco_result));
ack = u.toStanza(`<a xmlns="urn:xmpp:sm:3" h="2"/>`); ack = u.toStanza(`<a xmlns="urn:xmpp:sm:3" h="3"/>`);
_converse.connection._dataRecv(test_utils.createRequest(ack)); _converse.connection._dataRecv(test_utils.createRequest(ack));
expect(_converse.session.get('unacked_stanzas').length).toBe(3); expect(_converse.session.get('unacked_stanzas').length).toBe(2);
r = u.toStanza(`<r xmlns="urn:xmpp:sm:3"/>`); r = u.toStanza(`<r xmlns="urn:xmpp:sm:3"/>`);
_converse.connection._dataRecv(test_utils.createRequest(r)); _converse.connection._dataRecv(test_utils.createRequest(r));
...@@ -104,6 +105,7 @@ ...@@ -104,6 +105,7 @@
IQ_stanzas = _converse.connection.IQ_stanzas; IQ_stanzas = _converse.connection.IQ_stanzas;
_converse.api.connection.reconnect(); _converse.api.connection.reconnect();
stanza = await u.waitUntil(() => sent_stanzas.filter(s => (s.tagName === 'resume')).pop()); stanza = await u.waitUntil(() => sent_stanzas.filter(s => (s.tagName === 'resume')).pop());
expect(Strophe.serialize(stanza)).toEqual('<resume h="2" previd="some-long-sm-id" xmlns="urn:xmpp:sm:3"/>'); expect(Strophe.serialize(stanza)).toEqual('<resume h="2" previd="some-long-sm-id" xmlns="urn:xmpp:sm:3"/>');
result = u.toStanza(`<resumed xmlns="urn:xmpp:sm:3" h="another-sequence-number" previd="some-long-sm-id"/>`); result = u.toStanza(`<resumed xmlns="urn:xmpp:sm:3" h="another-sequence-number" previd="some-long-sm-id"/>`);
...@@ -113,7 +115,7 @@ ...@@ -113,7 +115,7 @@
expect(sent_stanzas.filter(s => (s.tagName === 'enable')).length).toBe(1); expect(sent_stanzas.filter(s => (s.tagName === 'enable')).length).toBe(1);
expect(_converse.session.get('smacks_enabled')).toBe(true); expect(_converse.session.get('smacks_enabled')).toBe(true);
await u.waitUntil(() => IQ_stanzas.length === 2); await u.waitUntil(() => IQ_stanzas.length === 1);
// Test that unacked stanzas get resent out // Test that unacked stanzas get resent out
iq = IQ_stanzas.pop(); iq = IQ_stanzas.pop();
...@@ -121,8 +123,6 @@ ...@@ -121,8 +123,6 @@
`<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+ `<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`); `<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
// We don't fetch the roster again because it's cached.
expect(_converse.session.get('roster_fetched')).toBeTruthy();
expect(IQ_stanzas.filter(iq => sizzle('query[xmlns="jabber:iq:roster"]', iq).pop()).length).toBe(0); expect(IQ_stanzas.filter(iq => sizzle('query[xmlns="jabber:iq:roster"]', iq).pop()).length).toBe(0);
await _converse.api.waitUntil('statusInitialized'); await _converse.api.waitUntil('statusInitialized');
...@@ -163,20 +163,23 @@ ...@@ -163,20 +163,23 @@
`</failed>`); `</failed>`);
_converse.connection._dataRecv(test_utils.createRequest(result)); _converse.connection._dataRecv(test_utils.createRequest(result));
_converse.connection.IQ_stanzas = [];
const IQ_stanzas = _converse.connection.IQ_stanzas;
// Session data gets reset // Session data gets reset
expect(_converse.session.get('smacks_enabled')).toBe(false); expect(_converse.session.get('smacks_enabled')).toBe(false);
expect(_converse.session.get('num_stanzas_handled')).toBe(0); expect(_converse.session.get('num_stanzas_handled')).toBe(0);
expect(_converse.session.get('num_stanzas_handled_by_server')).toBe(0); expect(_converse.session.get('num_stanzas_handled_by_server')).toBe(0);
expect(_converse.session.get('num_stanzas_since_last_ack')).toBe(0); expect(_converse.session.get('num_stanzas_since_last_ack')).toBe(0);
expect(_converse.session.get('unacked_stanzas').length).toBe(0); expect(_converse.session.get('unacked_stanzas').length).toBe(0);
expect(_converse.session.get('roster_fetched')).toBeFalsy(); expect(_converse.session.get('roster_cached')).toBeFalsy();
await u.waitUntil(() => sent_stanzas.filter(s => (s.tagName === 'enable')).length === 2); await u.waitUntil(() => sent_stanzas.filter(s => (s.tagName === 'enable')).length === 2);
stanza = sent_stanzas.filter(s => (s.tagName === 'enable')).pop(); stanza = sent_stanzas.filter(s => (s.tagName === 'enable')).pop();
expect(Strophe.serialize(stanza)).toEqual('<enable resume="true" xmlns="urn:xmpp:sm:3"/>'); expect(Strophe.serialize(stanza)).toEqual('<enable resume="true" xmlns="urn:xmpp:sm:3"/>');
result = u.toStanza(`<enabled xmlns="urn:xmpp:sm:3" id="another-long-sm-id" resume="true"/>`); result = u.toStanza(`<enabled xmlns="urn:xmpp:sm:3" id="another-long-sm-id" resume="true"/>`);
_converse.connection._dataRecv(test_utils.createRequest(result)); _converse.connection._dataRecv(test_utils.createRequest(result));
expect(_converse.session.get('smacks_enabled')).toBe(true); expect(_converse.session.get('smacks_enabled')).toBe(true);
......
...@@ -103,39 +103,24 @@ converse.plugins.add('converse-roster', { ...@@ -103,39 +103,24 @@ converse.plugins.add('converse-roster', {
_converse.populateRoster = async function (ignore_cache=false) { _converse.populateRoster = async function (ignore_cache=false) {
if (ignore_cache) { if (ignore_cache) {
_converse.send_initial_presence = true; _converse.send_initial_presence = true;
try { }
await _converse.roster.fetchFromServer(); try {
/** await _converse.rostergroups.fetchRosterGroups();
* Triggered once roster contacts have been fetched. Used by the /**
* `converse-rosterview.js` plugin to know when it can start to show the roster. * Triggered once roster groups have been fetched. Used by the
* @event _converse#rosterContactsFetched * `converse-rosterview.js` plugin to know when it can start alphabetically
* @example _converse.api.listen.on('rosterContactsFetched', () => { ... }); * position roster groups.
*/ * @event _converse#rosterGroupsFetched
_converse.api.trigger('rosterContactsFetched'); * @example _converse.api.listen.on('rosterGroupsFetched', () => { ... });
} catch (reason) { * @example _converse.api.waitUntil('rosterGroupsFetched').then(() => { ... });
_converse.log(reason, Strophe.LogLevel.ERROR); */
} finally { _converse.api.trigger('rosterGroupsFetched');
_converse.sendInitialPresence(); await _converse.roster.fetchRosterContacts();
} _converse.api.trigger('rosterContactsFetched');
} else { } catch (reason) {
try { _converse.log(reason, Strophe.LogLevel.ERROR);
await _converse.rostergroups.fetchRosterGroups(); } finally {
/** _converse.sendInitialPresence();
* Triggered once roster groups have been fetched. Used by the
* `converse-rosterview.js` plugin to know when it can start alphabetically
* position roster groups.
* @event _converse#rosterGroupsFetched
* @example _converse.api.listen.on('rosterGroupsFetched', () => { ... });
* @example _converse.api.waitUntil('rosterGroupsFetched').then(() => { ... });
*/
_converse.api.trigger('rosterGroupsFetched');
await _converse.roster.fetchRosterContacts();
_converse.api.trigger('rosterContactsFetched');
} catch (reason) {
_converse.log(reason, Strophe.LogLevel.ERROR);
} finally {
_converse.sendInitialPresence();
}
} }
}; };
...@@ -438,33 +423,34 @@ converse.plugins.add('converse-roster', { ...@@ -438,33 +423,34 @@ converse.plugins.add('converse-roster', {
* @returns {promise} Promise which resolves once the contacts have been fetched. * @returns {promise} Promise which resolves once the contacts have been fetched.
*/ */
async fetchRosterContacts () { async fetchRosterContacts () {
if (_converse.session.get('roster_fetched')) { const result = await new Promise((resolve, reject) => {
const result = await new Promise((resolve, reject) => { this.fetch({
this.fetch({ 'add': true,
'add': true, 'silent': true,
'silent': true, 'success': resolve,
'success': resolve, 'error': (c, e) => reject(e)
'error': (c, e) => reject(e)
});
}); });
if (u.isErrorObject(result)) { });
_converse.log(result, Strophe.LogLevel.ERROR); if (u.isErrorObject(result)) {
_converse.session.set('roster_fetched', false) _converse.log(result, Strophe.LogLevel.ERROR);
} else { // Force a full roster refresh
/** _converse.session.set('roster_cached', false)
* The contacts roster has been retrieved from the local cache (`sessionStorage`). this.data.save('version', undefined);
* @event _converse#cachedRoster
* @type { _converse.RosterContacts }
* @example _converse.api.listen.on('cachedRoster', (items) => { ... });
* @example _converse.api.waitUntil('cachedRoster').then(items => { ... });
*/
_converse.api.trigger('cachedRoster', result);
return;
}
} }
_converse.send_initial_presence = true; if (_converse.session.get('roster_cached')) {
return _converse.roster.fetchFromServer(); /**
* The contacts roster has been retrieved from the local cache (`sessionStorage`).
* @event _converse#cachedRoster
* @type { _converse.RosterContacts }
* @example _converse.api.listen.on('cachedRoster', (items) => { ... });
* @example _converse.api.waitUntil('cachedRoster').then(items => { ... });
*/
_converse.api.trigger('cachedRoster', result);
} else {
_converse.send_initial_presence = true;
return _converse.roster.fetchFromServer();
}
}, },
subscribeToSuggestedItems (msg) { subscribeToSuggestedItems (msg) {
...@@ -623,7 +609,7 @@ converse.plugins.add('converse-roster', { ...@@ -623,7 +609,7 @@ converse.plugins.add('converse-roster', {
}, },
rosterVersioningSupported () { rosterVersioningSupported () {
return !!(_converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver') && this.data.get('version')); return _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver') && this.data.get('version');
}, },
/** /**
...@@ -653,7 +639,7 @@ converse.plugins.add('converse-roster', { ...@@ -653,7 +639,7 @@ converse.plugins.add('converse-roster', {
_converse.log(iq, Strophe.LogLevel.ERROR); _converse.log(iq, Strophe.LogLevel.ERROR);
return _converse.log("Error while trying to fetch roster from the server", Strophe.LogLevel.ERROR); return _converse.log("Error while trying to fetch roster from the server", Strophe.LogLevel.ERROR);
} }
_converse.session.save('roster_fetched', true); _converse.session.save('roster_cached', true);
/** /**
* When the roster has been received from the XMPP server. * When the roster has been received from the XMPP server.
* See also the `cachedRoster` event further up, which gets called instead of * See also the `cachedRoster` event further up, which gets called instead of
...@@ -848,6 +834,11 @@ converse.plugins.add('converse-roster', { ...@@ -848,6 +834,11 @@ converse.plugins.add('converse-roster', {
}); });
/**
* @class
* @namespace _converse.RosterGroups
* @memberOf _converse
*/
_converse.RosterGroups = _converse.Collection.extend({ _converse.RosterGroups = _converse.Collection.extend({
model: _converse.RosterGroup, model: _converse.RosterGroup,
...@@ -868,12 +859,13 @@ converse.plugins.add('converse-roster', { ...@@ -868,12 +859,13 @@ converse.plugins.add('converse-roster', {
} }
}, },
/**
* Fetches all the roster groups from sessionStorage.
* @private
* @method _converse.RosterGroups#fetchRosterGroups
* @returns { Promise } - A promise which resolves once the groups have been fetched.
*/
fetchRosterGroups () { fetchRosterGroups () {
/* Fetches all the roster groups from sessionStorage.
*
* Returns a promise which resolves once the groups have been
* returned.
*/
return new Promise(success => { return new Promise(success => {
this.fetch({ this.fetch({
success, success,
...@@ -936,7 +928,7 @@ converse.plugins.add('converse-roster', { ...@@ -936,7 +928,7 @@ converse.plugins.add('converse-roster', {
} }
} }
_converse.api.listen.on('streamResumptionFailed', () => _converse.session.set('roster_fetched', false)); _converse.api.listen.on('streamResumptionFailed', () => _converse.session.set('roster_cached', false));
_converse.api.listen.on('clearSession', () => { _converse.api.listen.on('clearSession', () => {
clearPresences(); clearPresences();
...@@ -996,7 +988,7 @@ converse.plugins.add('converse-roster', { ...@@ -996,7 +988,7 @@ converse.plugins.add('converse-roster', {
} }
_converse.roster.onConnected(); _converse.roster.onConnected();
_converse.registerPresenceHandler(); _converse.registerPresenceHandler();
_converse.populateRoster(reconnecting); _converse.populateRoster(!_converse.connection.restored);
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment