From c18f6593d916f6ff09585a37dd9abcb2a5c0dbee Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Mon, 19 Sep 2016 09:51:40 -0400 Subject: [PATCH] Makes sure we plumb auth.installationId when updating installations --- spec/ParseInstallation.spec.js | 44 ++++++++++++++++++++++++++++++---- src/RestWrite.js | 38 ++++++++++++++++------------- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/spec/ParseInstallation.spec.js b/spec/ParseInstallation.spec.js index f7d648eed8..3796501ea7 100644 --- a/spec/ParseInstallation.spec.js +++ b/spec/ParseInstallation.spec.js @@ -37,7 +37,7 @@ describe('Installations', () => { expect(obj.installationId).toEqual(installId); expect(obj.deviceType).toEqual(device); done(); - }).catch((error) => { console.log(error); }); + }).catch((error) => { console.log(error); jfail(error); done(); }); }); it('creates an ios installation with ids', (done) => { @@ -55,7 +55,7 @@ describe('Installations', () => { expect(obj.deviceToken).toEqual(t); expect(obj.deviceType).toEqual(device); done(); - }).catch((error) => { console.log(error); }); + }).catch((error) => { console.log(error); jfail(error); done(); }); }); it('creates an embedded installation with ids', (done) => { @@ -73,7 +73,7 @@ describe('Installations', () => { expect(obj.installationId).toEqual(installId); expect(obj.deviceType).toEqual(device); done(); - }).catch((error) => { console.log(error); }); + }).catch((error) => { console.log(error); jfail(error); done(); }); }); it('creates an android installation with all fields', (done) => { @@ -96,7 +96,7 @@ describe('Installations', () => { expect(obj.channels[0]).toEqual('foo'); expect(obj.channels[1]).toEqual('bar'); done(); - }).catch((error) => { console.log(error); }); + }).catch((error) => { console.log(error); jfail(error); done(); }); }); it('creates an ios installation with all fields', (done) => { @@ -119,7 +119,7 @@ describe('Installations', () => { expect(obj.channels[0]).toEqual('foo'); expect(obj.channels[1]).toEqual('bar'); done(); - }).catch((error) => { console.log(error); }); + }).catch((error) => { console.log(error); jfail(error); done(); }); }); it('should properly fail queying installations', (done) => { @@ -872,6 +872,40 @@ describe('Installations', () => { }); }); + it('allows you to update installation from header (#2090)', done => { + let installId = '12345678-abcd-abcd-abcd-123456789abc'; + let device = 'android'; + let input = { + 'installationId': installId, + 'deviceType': device + }; + rest.create(config, auth.nobody(config), '_Installation', input) + .then(createResult => { + let headers = { + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Installation-Id': installId + }; + request.post({ + headers: headers, + url: 'http://localhost:8378/1/classes/_Installation', + json: true, + body: { + date: new Date() + } + }, (error, response, body) => { + expect(response.statusCode).toBe(200); + expect(body.updatedAt).not.toBeUndefined(); + done(); + }); + }) + .catch(error => { + console.log(error); + fail('failed'); + done(); + }); + }); + // TODO: Look at additional tests from installation_collection_test.go:882 // TODO: Do we need to support _tombstone disabling of installations? // TODO: Test deletion, badge increments diff --git a/src/RestWrite.js b/src/RestWrite.js index 6443a67e45..1fd291ef01 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -561,31 +561,30 @@ RestWrite.prototype.handleInstallation = function() { return; } - if (!this.query && !this.data.deviceToken && !this.data.installationId) { + if (!this.query && !this.data.deviceToken && !this.data.installationId && !this.auth.installationId) { throw new Parse.Error(135, 'at least one ID field (deviceToken, installationId) ' + 'must be specified in this operation'); } - if (!this.query && !this.data.deviceType) { - throw new Parse.Error(135, - 'deviceType must be specified in this operation'); - } - // If the device token is 64 characters long, we assume it is for iOS // and lowercase it. if (this.data.deviceToken && this.data.deviceToken.length == 64) { this.data.deviceToken = this.data.deviceToken.toLowerCase(); } - // TODO: We may need installationId from headers, plumb through Auth? - // per installation_handler.go - // We lowercase the installationId if present if (this.data.installationId) { this.data.installationId = this.data.installationId.toLowerCase(); } + // If data.installationId is not set, we can lookup in the auth + let installationId = this.data.installationId || this.auth.installationId; + + if (installationId) { + installationId = installationId.toLowerCase(); + } + var promise = Promise.resolve(); var idMatch; // Will be a match on either objectId or installationId @@ -600,9 +599,9 @@ RestWrite.prototype.handleInstallation = function() { objectId: this.query.objectId }); } - if (this.data.installationId) { + if (installationId) { orQueries.push({ - 'installationId': this.data.installationId + 'installationId': installationId }); } if (this.data.deviceToken) { @@ -622,7 +621,7 @@ RestWrite.prototype.handleInstallation = function() { if (this.query && this.query.objectId && result.objectId == this.query.objectId) { objectIdMatch = result; } - if (result.installationId == this.data.installationId) { + if (result.installationId == installationId) { installationIdMatch = result; } if (result.deviceToken == this.data.deviceToken) { @@ -636,8 +635,8 @@ RestWrite.prototype.handleInstallation = function() { throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found for update.'); } - if (this.data.installationId && objectIdMatch.installationId && - this.data.installationId !== objectIdMatch.installationId) { + if (installationId && objectIdMatch.installationId && + installationId !== objectIdMatch.installationId) { throw new Parse.Error(136, 'installationId may not be changed in this ' + 'operation'); @@ -661,16 +660,21 @@ RestWrite.prototype.handleInstallation = function() { idMatch = objectIdMatch; } - if (this.data.installationId && installationIdMatch) { + if (installationId && installationIdMatch) { idMatch = installationIdMatch; } + // need to specify deviceType only if it's new + if (!this.query && !this.data.deviceType && !idMatch) { + throw new Parse.Error(135, + 'deviceType must be specified in this operation'); + } }).then(() => { if (!idMatch) { if (!deviceTokenMatches.length) { return; } else if (deviceTokenMatches.length == 1 && - (!deviceTokenMatches[0]['installationId'] || !this.data.installationId) + (!deviceTokenMatches[0]['installationId'] || !installationId) ) { // Single match on device token but none on installationId, and either // the passed object or the match is missing an installationId, so we @@ -689,7 +693,7 @@ RestWrite.prototype.handleInstallation = function() { var delQuery = { 'deviceToken': this.data.deviceToken, 'installationId': { - '$ne': this.data.installationId + '$ne': installationId } }; if (this.data.appIdentifier) {