Skip to content

Handle duplicate android device tokens correctly #673

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

Merged
merged 4 commits into from
Feb 29, 2016
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
46 changes: 46 additions & 0 deletions spec/ParseInstallation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,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) => {
var installId1 = '11111111-abcd-abcd-abcd-123456789abc';
var installId2 = '22222222-abcd-abcd-abcd-123456789abc';
Expand Down
29 changes: 15 additions & 14 deletions src/RestWrite.js
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,9 @@ RestWrite.prototype.handleInstallation = function() {

var promise = Promise.resolve();

var idMatch; // Will be a match on either objectId or installationId
var deviceTokenMatches = [];

if (this.query && this.query.objectId) {
promise = promise.then(() => {
return this.config.database.find('_Installation', {
Expand All @@ -601,22 +604,22 @@ RestWrite.prototype.handleInstallation = function() {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
'Object not found for update.');
}
var existing = results[0];
if (this.data.installationId && existing.installationId &&
this.data.installationId !== existing.installationId) {
idMatch = results[0];
if (this.data.installationId && idMatch.installationId &&
this.data.installationId !== idMatch.installationId) {
throw new Parse.Error(136,
'installationId may not be changed in this ' +
'operation');
}
if (this.data.deviceToken && existing.deviceToken &&
this.data.deviceToken !== existing.deviceToken &&
!this.data.installationId && !existing.installationId) {
if (this.data.deviceToken && idMatch.deviceToken &&
this.data.deviceToken !== idMatch.deviceToken &&
!this.data.installationId && !idMatch.installationId) {
throw new Parse.Error(136,
'deviceToken may not be changed in this ' +
'operation');
}
if (this.data.deviceType && this.data.deviceType &&
this.data.deviceType !== existing.deviceType) {
this.data.deviceType !== idMatch.deviceType) {
throw new Parse.Error(136,
'deviceType may not be changed in this ' +
'operation');
Expand All @@ -627,8 +630,6 @@ RestWrite.prototype.handleInstallation = function() {
}

// Check if we already have installations for the installationId/deviceToken
var installationMatch;
var deviceTokenMatches = [];
promise = promise.then(() => {
if (this.data.installationId) {
return this.config.database.find('_Installation', {
Expand All @@ -639,7 +640,7 @@ RestWrite.prototype.handleInstallation = function() {
}).then((results) => {
if (results && results.length) {
// We only take the first match by installationId
installationMatch = results[0];
idMatch = results[0];
}
if (this.data.deviceToken) {
return this.config.database.find(
Expand All @@ -651,7 +652,7 @@ RestWrite.prototype.handleInstallation = function() {
if (results) {
deviceTokenMatches = results;
}
if (!installationMatch) {
if (!idMatch) {
if (!deviceTokenMatches.length) {
return;
} else if (deviceTokenMatches.length == 1 &&
Expand Down Expand Up @@ -689,14 +690,14 @@ RestWrite.prototype.handleInstallation = function() {
// 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
// object.
var delQuery = {objectId: installationMatch.objectId};
var delQuery = {objectId: idMatch.objectId};
return this.config.database.destroy('_Installation', delQuery)
.then(() => {
return deviceTokenMatches[0]['objectId'];
});
} else {
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 should try cleaning out old installations that match this
// device token.
Expand All @@ -712,7 +713,7 @@ RestWrite.prototype.handleInstallation = function() {
this.config.database.destroy('_Installation', delQuery);
}
// In non-merge scenarios, just return the installation match id
return installationMatch.objectId;
return idMatch.objectId;
}
}
}).then((objId) => {
Expand Down