Skip to content

Commit 04491fc

Browse files
committed
Merge pull request #673 from stephentuso/installation-handling-fix
Handle duplicate android device tokens correctly
2 parents 46da50f + 02b56de commit 04491fc

File tree

2 files changed

+61
-14
lines changed

2 files changed

+61
-14
lines changed

spec/ParseInstallation.spec.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,52 @@ describe('Installations', () => {
446446
});
447447
});
448448

449+
it('update android device token with duplicate device token', (done) => {
450+
var installId1 = '11111111-abcd-abcd-abcd-123456789abc';
451+
var installId2 = '22222222-abcd-abcd-abcd-123456789abc';
452+
var t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
453+
var input = {
454+
'installationId': installId1,
455+
'deviceToken': t,
456+
'deviceType': 'android'
457+
};
458+
var firstObject;
459+
var secondObject;
460+
rest.create(config, auth.nobody(config), '_Installation', input)
461+
.then(() => {
462+
input = {
463+
'installationId': installId2,
464+
'deviceType': 'android'
465+
};
466+
return rest.create(config, auth.nobody(config), '_Installation', input);
467+
}).then(() => {
468+
return database.mongoFind('_Installation',
469+
{installationId: installId1}, {});
470+
}).then((results) => {
471+
expect(results.length).toEqual(1);
472+
firstObject = results[0];
473+
return database.mongoFind('_Installation',
474+
{installationId: installId2}, {});
475+
}).then((results) => {
476+
expect(results.length).toEqual(1);
477+
secondObject = results[0];
478+
// Update second installation to conflict with first installation
479+
input = {
480+
'objectId': secondObject._id,
481+
'deviceToken': t
482+
};
483+
return rest.update(config, auth.nobody(config), '_Installation',
484+
secondObject._id, input);
485+
}).then(() => {
486+
// The first object should have been deleted
487+
return database.mongoFind('_Installation', {_id: firstObject._id}, {});
488+
}).then((results) => {
489+
expect(results.length).toEqual(0);
490+
done();
491+
}).catch((error) => { console.log(error); });
492+
});
493+
494+
449495
it('update ios device token with duplicate device token', (done) => {
450496
var installId1 = '11111111-abcd-abcd-abcd-123456789abc';
451497
var installId2 = '22222222-abcd-abcd-abcd-123456789abc';

src/RestWrite.js

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,9 @@ RestWrite.prototype.handleInstallation = function() {
594594

595595
var promise = Promise.resolve();
596596

597+
var idMatch; // Will be a match on either objectId or installationId
598+
var deviceTokenMatches = [];
599+
597600
if (this.query && this.query.objectId) {
598601
promise = promise.then(() => {
599602
return this.config.database.find('_Installation', {
@@ -603,22 +606,22 @@ RestWrite.prototype.handleInstallation = function() {
603606
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
604607
'Object not found for update.');
605608
}
606-
var existing = results[0];
607-
if (this.data.installationId && existing.installationId &&
608-
this.data.installationId !== existing.installationId) {
609+
idMatch = results[0];
610+
if (this.data.installationId && idMatch.installationId &&
611+
this.data.installationId !== idMatch.installationId) {
609612
throw new Parse.Error(136,
610613
'installationId may not be changed in this ' +
611614
'operation');
612615
}
613-
if (this.data.deviceToken && existing.deviceToken &&
614-
this.data.deviceToken !== existing.deviceToken &&
615-
!this.data.installationId && !existing.installationId) {
616+
if (this.data.deviceToken && idMatch.deviceToken &&
617+
this.data.deviceToken !== idMatch.deviceToken &&
618+
!this.data.installationId && !idMatch.installationId) {
616619
throw new Parse.Error(136,
617620
'deviceToken may not be changed in this ' +
618621
'operation');
619622
}
620623
if (this.data.deviceType && this.data.deviceType &&
621-
this.data.deviceType !== existing.deviceType) {
624+
this.data.deviceType !== idMatch.deviceType) {
622625
throw new Parse.Error(136,
623626
'deviceType may not be changed in this ' +
624627
'operation');
@@ -629,8 +632,6 @@ RestWrite.prototype.handleInstallation = function() {
629632
}
630633

631634
// Check if we already have installations for the installationId/deviceToken
632-
var installationMatch;
633-
var deviceTokenMatches = [];
634635
promise = promise.then(() => {
635636
if (this.data.installationId) {
636637
return this.config.database.find('_Installation', {
@@ -641,7 +642,7 @@ RestWrite.prototype.handleInstallation = function() {
641642
}).then((results) => {
642643
if (results && results.length) {
643644
// We only take the first match by installationId
644-
installationMatch = results[0];
645+
idMatch = results[0];
645646
}
646647
if (this.data.deviceToken) {
647648
return this.config.database.find(
@@ -653,7 +654,7 @@ RestWrite.prototype.handleInstallation = function() {
653654
if (results) {
654655
deviceTokenMatches = results;
655656
}
656-
if (!installationMatch) {
657+
if (!idMatch) {
657658
if (!deviceTokenMatches.length) {
658659
return;
659660
} else if (deviceTokenMatches.length == 1 &&
@@ -691,14 +692,14 @@ RestWrite.prototype.handleInstallation = function() {
691692
// Exactly one device token match and it doesn't have an installation
692693
// ID. This is the one case where we want to merge with the existing
693694
// object.
694-
var delQuery = {objectId: installationMatch.objectId};
695+
var delQuery = {objectId: idMatch.objectId};
695696
return this.config.database.destroy('_Installation', delQuery)
696697
.then(() => {
697698
return deviceTokenMatches[0]['objectId'];
698699
});
699700
} else {
700701
if (this.data.deviceToken &&
701-
installationMatch.deviceToken != this.data.deviceToken) {
702+
idMatch.deviceToken != this.data.deviceToken) {
702703
// We're setting the device token on an existing installation, so
703704
// we should try cleaning out old installations that match this
704705
// device token.
@@ -714,7 +715,7 @@ RestWrite.prototype.handleInstallation = function() {
714715
this.config.database.destroy('_Installation', delQuery);
715716
}
716717
// In non-merge scenarios, just return the installation match id
717-
return installationMatch.objectId;
718+
return idMatch.objectId;
718719
}
719720
}
720721
}).then((objId) => {

0 commit comments

Comments
 (0)