Skip to content

Makes sure we plumb auth.installationId when updating installations #2739

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 1 commit into from
Sep 24, 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
44 changes: 39 additions & 5 deletions spec/ParseInstallation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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) => {
Expand All @@ -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) => {
Expand All @@ -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) => {
Expand All @@ -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) => {
Expand Down Expand Up @@ -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
Expand Down
38 changes: 21 additions & 17 deletions src/RestWrite.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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');
Expand All @@ -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
Expand All @@ -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) {
Expand Down