Skip to content

[WIP] Allow Session triggers #4373

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

67 changes: 61 additions & 6 deletions spec/CloudCode.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,12 @@ describe('Cloud Code', () => {
it('test afterSave ran on created object and returned a promise', function(done) {
Parse.Cloud.afterSave('AfterSaveTest2', function(req) {
const obj = req.object;
if(!obj.existed())
if (!obj.existed())
{
const promise = new Parse.Promise();
setTimeout(function(){
setTimeout(function() {
obj.set('proof', obj.id);
obj.save().then(function(){
obj.save().then(function() {
promise.resolve();
});
}, 1000);
Expand All @@ -196,7 +196,7 @@ describe('Cloud Code', () => {
});

const obj = new Parse.Object('AfterSaveTest2');
obj.save().then(function(){
obj.save().then(function() {
const query = new Parse.Query('AfterSaveTest2');
query.equalTo('proof', obj.id);
query.find().then(function(results) {
Expand Down Expand Up @@ -1778,19 +1778,74 @@ describe('afterFind hooks', () => {
})
.then(() => done());
});
});

describe('Trigger Testing', () => {
it('should validate triggers correctly', () => {
expect(() => {
Parse.Cloud.beforeFind('_Session', () => {});
}).toThrow('beforeFind and afterFind triggers are not allowed for _Session class.');
expect(() => {
Parse.Cloud.afterFind('_Session', () => {});
}).toThrow('beforeFind and afterFind triggers are not allowed for _Session class.');
expect(() => {
Parse.Cloud.beforeSave('_Session', () => {});
}).toThrow('Triggers are not supported for _Session class.');
}).not.toThrow('Triggers are not supported for _Session class.');
expect(() => {
Parse.Cloud.afterSave('_Session', () => {});
}).toThrow('Triggers are not supported for _Session class.');
}).not.toThrow('Triggers are not supported for _Session class.');
expect(() => {
Parse.Cloud.beforeSave('_PushStatus', () => {});
}).toThrow('Only afterSave is allowed on _PushStatus');
expect(() => {
Parse.Cloud.afterSave('_PushStatus', () => {});
}).not.toThrow();
});

it('beforeSave _Session should not modify class', (done) => {
var hasCalled = false;
Parse.Cloud.beforeSave('_Session', (req, res) => {
req.object.set('foo', 'bing');
expect(() => {
req.object.set('createdWith', 'test')
}).toThrow();
expect(() => {
req.object.set('expiresAt', new Date())
}).toThrow();
expect(() => {
req.object.set('installationId', 'test')
}).toThrow();
expect(() => {
req.object.set('restricted', 'test')
}).toThrow();
expect(() => {
req.object.set('sessionToken', 'test')
}).toThrow();
expect(() => {
req.object.set('user', null)
}).toThrow();

hasCalled = true;
res.success();
});

var user = new Parse.User();
user.set("username", "zxcv");
user.set("email", "[email protected]");
user.set("password", "asdf");
user.signUp(null, {
success: function() {
Parse.Session.current().then((result) => {
expect(hasCalled).toBe(true);
expect(result).toBeDefined();
expect(result.get('foo')).toBeUndefined();
done();
});
},
error: function() {
fail('Failed to save user');
done();
}
});
});
});
15 changes: 10 additions & 5 deletions src/RestWrite.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ RestWrite.prototype.runBeforeTrigger = function() {
return Promise.resolve().then(() => {
return triggers.maybeRunTrigger(triggers.Types.beforeSave, this.auth, updatedObject, originalObject, this.config);
}).then((response) => {
if (response && response.object) {
if (this.className !== '_Session' && response && response.object) {
this.storage.fieldsChangedByTrigger = _.reduce(response.object, (result, value, key) => {
if (!_.isEqual(this.data[key], value)) {
result.push(key);
Expand Down Expand Up @@ -1161,19 +1161,24 @@ RestWrite.prototype.objectId = function() {
};

// Returns a copy of the data and delete bad keys (_auth_data, _hashed_password...)
RestWrite.prototype.sanitizedData = function() {
const data = Object.keys(this.data).reduce((data, key) => {
RestWrite.prototype.sanitizeData = function() {
Object.keys(this.data).reduce((data, key) => {
// Regexp comes from Parse.Object.prototype.validate
if (!(/^[A-Za-z][0-9A-Za-z_]*$/).test(key)) {
delete data[key];
}
return data;
}, deepcopy(this.data));
return Parse._decode(undefined, data);

return this.data;
}

// Returns an updated copy of the object
RestWrite.prototype.buildUpdatedObject = function (extraData) {
if (extraData.className == '_Session') {
return triggers.inflate(extraData, this.sanitizeData());
}

const updatedObject = triggers.inflate(extraData, this.originalData);
Object.keys(this.data).reduce(function (data, key) {
if (key.indexOf(".") > 0) {
Expand All @@ -1191,7 +1196,7 @@ RestWrite.prototype.buildUpdatedObject = function (extraData) {
return data;
}, deepcopy(this.data));

updatedObject.set(this.sanitizedData());
updatedObject.set(Parse._decode(undefined, this.sanitizeData()));
return updatedObject;
};

Expand Down
2 changes: 1 addition & 1 deletion src/Routers/PublicAPIRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class PublicAPIRouter extends PromiseRouter {
const appId = req.params.appId;
const config = Config.get(appId);

if(!config){
if(!config) {
this.invalidRequest();
}

Expand Down
9 changes: 4 additions & 5 deletions src/triggers.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,10 @@ const baseStore = function() {
};

function validateClassNameForTriggers(className, type) {
const restrictedClassNames = [ '_Session' ];
if (restrictedClassNames.indexOf(className) != -1) {
throw `Triggers are not supported for ${className} class.`;
if ((type == Types.beforeFind || type == Types.afterFind) && className === '_Session') {
throw 'beforeFind and afterFind triggers are not allowed for _Session class.';
}
if (type == Types.beforeSave && className === '_PushStatus') {
else if (type == Types.beforeSave && className === '_PushStatus') {
// _PushStatus uses undocumented nested key increment ops
// allowing beforeSave would mess up the objects big time
// TODO: Allow proper documented way of using nested increment ops
Expand Down Expand Up @@ -203,7 +202,7 @@ export function getResponseObject(request, resolve, reject) {
return {
success: function(response) {
if (request.triggerName === Types.afterFind) {
if(!response){
if(!response) {
response = request.objects;
}
response = response.map(object => {
Expand Down