Merge pull request #673 from stephentuso/installation-handling-fix

Handle duplicate android device tokens correctly
This commit is contained in:
Fosco Marotto
2016-02-29 14:37:24 -08:00
2 changed files with 61 additions and 14 deletions

View File

@@ -446,6 +446,52 @@ describe('Installations', () => {
}); });
}); });
it('update android device token with duplicate device token', (done) => {
var installId1 = '11111111-abcd-abcd-abcd-123456789abc';
var installId2 = '22222222-abcd-abcd-abcd-123456789abc';
var t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
var input = {
'installationId': installId1,
'deviceToken': t,
'deviceType': 'android'
};
var firstObject;
var secondObject;
rest.create(config, auth.nobody(config), '_Installation', input)
.then(() => {
input = {
'installationId': installId2,
'deviceType': 'android'
};
return rest.create(config, auth.nobody(config), '_Installation', input);
}).then(() => {
return database.mongoFind('_Installation',
{installationId: installId1}, {});
}).then((results) => {
expect(results.length).toEqual(1);
firstObject = results[0];
return database.mongoFind('_Installation',
{installationId: installId2}, {});
}).then((results) => {
expect(results.length).toEqual(1);
secondObject = results[0];
// Update second installation to conflict with first installation
input = {
'objectId': secondObject._id,
'deviceToken': t
};
return rest.update(config, auth.nobody(config), '_Installation',
secondObject._id, input);
}).then(() => {
// The first object should have been deleted
return database.mongoFind('_Installation', {_id: firstObject._id}, {});
}).then((results) => {
expect(results.length).toEqual(0);
done();
}).catch((error) => { console.log(error); });
});
it('update ios device token with duplicate device token', (done) => { it('update ios device token with duplicate device token', (done) => {
var installId1 = '11111111-abcd-abcd-abcd-123456789abc'; var installId1 = '11111111-abcd-abcd-abcd-123456789abc';
var installId2 = '22222222-abcd-abcd-abcd-123456789abc'; var installId2 = '22222222-abcd-abcd-abcd-123456789abc';

View File

@@ -594,6 +594,9 @@ RestWrite.prototype.handleInstallation = function() {
var promise = Promise.resolve(); var promise = Promise.resolve();
var idMatch; // Will be a match on either objectId or installationId
var deviceTokenMatches = [];
if (this.query && this.query.objectId) { if (this.query && this.query.objectId) {
promise = promise.then(() => { promise = promise.then(() => {
return this.config.database.find('_Installation', { return this.config.database.find('_Installation', {
@@ -603,22 +606,22 @@ RestWrite.prototype.handleInstallation = function() {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
'Object not found for update.'); 'Object not found for update.');
} }
var existing = results[0]; idMatch = results[0];
if (this.data.installationId && existing.installationId && if (this.data.installationId && idMatch.installationId &&
this.data.installationId !== existing.installationId) { this.data.installationId !== idMatch.installationId) {
throw new Parse.Error(136, throw new Parse.Error(136,
'installationId may not be changed in this ' + 'installationId may not be changed in this ' +
'operation'); 'operation');
} }
if (this.data.deviceToken && existing.deviceToken && if (this.data.deviceToken && idMatch.deviceToken &&
this.data.deviceToken !== existing.deviceToken && this.data.deviceToken !== idMatch.deviceToken &&
!this.data.installationId && !existing.installationId) { !this.data.installationId && !idMatch.installationId) {
throw new Parse.Error(136, throw new Parse.Error(136,
'deviceToken may not be changed in this ' + 'deviceToken may not be changed in this ' +
'operation'); 'operation');
} }
if (this.data.deviceType && this.data.deviceType && if (this.data.deviceType && this.data.deviceType &&
this.data.deviceType !== existing.deviceType) { this.data.deviceType !== idMatch.deviceType) {
throw new Parse.Error(136, throw new Parse.Error(136,
'deviceType may not be changed in this ' + 'deviceType may not be changed in this ' +
'operation'); 'operation');
@@ -629,8 +632,6 @@ RestWrite.prototype.handleInstallation = function() {
} }
// Check if we already have installations for the installationId/deviceToken // Check if we already have installations for the installationId/deviceToken
var installationMatch;
var deviceTokenMatches = [];
promise = promise.then(() => { promise = promise.then(() => {
if (this.data.installationId) { if (this.data.installationId) {
return this.config.database.find('_Installation', { return this.config.database.find('_Installation', {
@@ -641,7 +642,7 @@ RestWrite.prototype.handleInstallation = function() {
}).then((results) => { }).then((results) => {
if (results && results.length) { if (results && results.length) {
// We only take the first match by installationId // We only take the first match by installationId
installationMatch = results[0]; idMatch = results[0];
} }
if (this.data.deviceToken) { if (this.data.deviceToken) {
return this.config.database.find( return this.config.database.find(
@@ -653,7 +654,7 @@ RestWrite.prototype.handleInstallation = function() {
if (results) { if (results) {
deviceTokenMatches = results; deviceTokenMatches = results;
} }
if (!installationMatch) { if (!idMatch) {
if (!deviceTokenMatches.length) { if (!deviceTokenMatches.length) {
return; return;
} else if (deviceTokenMatches.length == 1 && } else if (deviceTokenMatches.length == 1 &&
@@ -691,14 +692,14 @@ RestWrite.prototype.handleInstallation = function() {
// Exactly one device token match and it doesn't have an installation // Exactly one device token match and it doesn't have an installation
// ID. This is the one case where we want to merge with the existing // ID. This is the one case where we want to merge with the existing
// object. // object.
var delQuery = {objectId: installationMatch.objectId}; var delQuery = {objectId: idMatch.objectId};
return this.config.database.destroy('_Installation', delQuery) return this.config.database.destroy('_Installation', delQuery)
.then(() => { .then(() => {
return deviceTokenMatches[0]['objectId']; return deviceTokenMatches[0]['objectId'];
}); });
} else { } else {
if (this.data.deviceToken && if (this.data.deviceToken &&
installationMatch.deviceToken != this.data.deviceToken) { idMatch.deviceToken != this.data.deviceToken) {
// We're setting the device token on an existing installation, so // We're setting the device token on an existing installation, so
// we should try cleaning out old installations that match this // we should try cleaning out old installations that match this
// device token. // device token.
@@ -714,7 +715,7 @@ RestWrite.prototype.handleInstallation = function() {
this.config.database.destroy('_Installation', delQuery); this.config.database.destroy('_Installation', delQuery);
} }
// In non-merge scenarios, just return the installation match id // In non-merge scenarios, just return the installation match id
return installationMatch.objectId; return idMatch.objectId;
} }
} }
}).then((objId) => { }).then((objId) => {