Merge pull request #614 from ParsePlatform/nlutsenko.beforeSave.dirty

Fix dirtyKeys() and dirty(key:) on beforeSave when updating objects.
This commit is contained in:
Nikita Lutsenko
2016-02-23 22:35:57 -08:00
3 changed files with 159 additions and 105 deletions

View File

@@ -1,5 +1,6 @@
// A bunch of different tests are in here - it isn't very thematic. // A bunch of different tests are in here - it isn't very thematic.
// It would probably be better to refactor them into different files. // It would probably be better to refactor them into different files.
'use strict';
var DatabaseAdapter = require('../src/DatabaseAdapter'); var DatabaseAdapter = require('../src/DatabaseAdapter');
var request = require('request'); var request = require('request');
@@ -280,7 +281,7 @@ describe('miscellaneous', function() {
// We should have been able to fetch the object again // We should have been able to fetch the object again
fail(error); fail(error);
}); });
}) });
it('basic beforeDelete rejection via promise', function(done) { it('basic beforeDelete rejection via promise', function(done) {
var obj = new Parse.Object('BeforeDeleteFailWithPromise'); var obj = new Parse.Object('BeforeDeleteFailWithPromise');
@@ -383,11 +384,24 @@ describe('miscellaneous', function() {
}); });
}); });
it('test beforeSave get full object on create and update', function(done) { describe('beforeSave', () => {
var triggerTime = 0; beforeEach(done => {
// Make sure the required mock for all tests is unset.
delete Parse.Cloud.Triggers.beforeSave.GameScore;
done();
});
afterEach(done => {
// Make sure the required mock for all tests is unset.
delete Parse.Cloud.Triggers.beforeSave.GameScore;
done();
});
it('object is set on create and update', done => {
let triggerTime = 0;
// Register a mock beforeSave hook // Register a mock beforeSave hook
Parse.Cloud.beforeSave('GameScore', function(req, res) { Parse.Cloud.beforeSave('GameScore', (req, res) => {
var object = req.object; let object = req.object;
expect(object instanceof Parse.Object).toBeTruthy(); expect(object instanceof Parse.Object).toBeTruthy();
expect(object.get('fooAgain')).toEqual('barAgain'); expect(object.get('fooAgain')).toEqual('barAgain');
if (triggerTime == 0) { if (triggerTime == 0) {
@@ -410,18 +424,55 @@ describe('miscellaneous', function() {
res.success(); res.success();
}); });
var obj = new Parse.Object('GameScore'); let obj = new Parse.Object('GameScore');
obj.set('foo', 'bar'); obj.set('foo', 'bar');
obj.set('fooAgain', 'barAgain'); obj.set('fooAgain', 'barAgain');
obj.save().then(function() { obj.save().then(() => {
// We only update foo // We only update foo
obj.set('foo', 'baz'); obj.set('foo', 'baz');
return obj.save(); return obj.save();
}).then(function() { }).then(() => {
// Make sure the checking has been triggered
expect(triggerTime).toBe(2);
done();
}, error => {
fail(error);
done();
});
});
it('dirtyKeys are set on update', done => {
let triggerTime = 0;
// Register a mock beforeSave hook
Parse.Cloud.beforeSave('GameScore', (req, res) => {
var object = req.object;
expect(object instanceof Parse.Object).toBeTruthy();
expect(object.get('fooAgain')).toEqual('barAgain');
if (triggerTime == 0) {
// Create
expect(object.get('foo')).toEqual('bar');
} else if (triggerTime == 1) {
// Update
expect(object.dirtyKeys()).toEqual(['foo']);
expect(object.dirty('foo')).toBeTruthy();
expect(object.get('foo')).toEqual('baz');
} else {
res.error();
}
triggerTime++;
res.success();
});
let obj = new Parse.Object('GameScore');
obj.set('foo', 'bar');
obj.set('fooAgain', 'barAgain');
obj.save().then(() => {
// We only update foo
obj.set('foo', 'baz');
return obj.save();
}).then(() => {
// Make sure the checking has been triggered // Make sure the checking has been triggered
expect(triggerTime).toBe(2); expect(triggerTime).toBe(2);
// Clear mock beforeSave
delete Parse.Cloud.Triggers.beforeSave.GameScore;
done(); done();
}, function(error) { }, function(error) {
fail(error); fail(error);
@@ -429,6 +480,61 @@ describe('miscellaneous', function() {
}); });
}); });
it('original object is set on update', done => {
let triggerTime = 0;
// Register a mock beforeSave hook
Parse.Cloud.beforeSave('GameScore', (req, res) => {
let object = req.object;
expect(object instanceof Parse.Object).toBeTruthy();
expect(object.get('fooAgain')).toEqual('barAgain');
let originalObject = req.original;
if (triggerTime == 0) {
// No id/createdAt/updatedAt
expect(object.id).toBeUndefined();
expect(object.createdAt).toBeUndefined();
expect(object.updatedAt).toBeUndefined();
// Create
expect(object.get('foo')).toEqual('bar');
// Check the originalObject is undefined
expect(originalObject).toBeUndefined();
} else if (triggerTime == 1) {
// Update
expect(object.id).not.toBeUndefined();
expect(object.createdAt).not.toBeUndefined();
expect(object.updatedAt).not.toBeUndefined();
expect(object.get('foo')).toEqual('baz');
// Check the originalObject
expect(originalObject instanceof Parse.Object).toBeTruthy();
expect(originalObject.get('fooAgain')).toEqual('barAgain');
expect(originalObject.id).not.toBeUndefined();
expect(originalObject.createdAt).not.toBeUndefined();
expect(originalObject.updatedAt).not.toBeUndefined();
expect(originalObject.get('foo')).toEqual('bar');
} else {
res.error();
}
triggerTime++;
res.success();
});
let obj = new Parse.Object('GameScore');
obj.set('foo', 'bar');
obj.set('fooAgain', 'barAgain');
obj.save().then(() => {
// We only update foo
obj.set('foo', 'baz');
return obj.save();
}).then(() => {
// Make sure the checking has been triggered
expect(triggerTime).toBe(2);
done();
}, error => {
fail(error);
done();
});
});
});
it('test afterSave get full object on create and update', function(done) { it('test afterSave get full object on create and update', function(done) {
var triggerTime = 0; var triggerTime = 0;
// Register a mock beforeSave hook // Register a mock beforeSave hook
@@ -471,62 +577,6 @@ describe('miscellaneous', function() {
}); });
}); });
it('test beforeSave get original object on update', function(done) {
var triggerTime = 0;
// Register a mock beforeSave hook
Parse.Cloud.beforeSave('GameScore', function(req, res) {
var object = req.object;
expect(object instanceof Parse.Object).toBeTruthy();
expect(object.get('fooAgain')).toEqual('barAgain');
var originalObject = req.original;
if (triggerTime == 0) {
// No id/createdAt/updatedAt
expect(object.id).toBeUndefined();
expect(object.createdAt).toBeUndefined();
expect(object.updatedAt).toBeUndefined();
// Create
expect(object.get('foo')).toEqual('bar');
// Check the originalObject is undefined
expect(originalObject).toBeUndefined();
} else if (triggerTime == 1) {
// Update
expect(object.id).not.toBeUndefined();
expect(object.createdAt).not.toBeUndefined();
expect(object.updatedAt).not.toBeUndefined();
expect(object.get('foo')).toEqual('baz');
// Check the originalObject
expect(originalObject instanceof Parse.Object).toBeTruthy();
expect(originalObject.get('fooAgain')).toEqual('barAgain');
expect(originalObject.id).not.toBeUndefined();
expect(originalObject.createdAt).not.toBeUndefined();
expect(originalObject.updatedAt).not.toBeUndefined();
expect(originalObject.get('foo')).toEqual('bar');
} else {
res.error();
}
triggerTime++;
res.success();
});
var obj = new Parse.Object('GameScore');
obj.set('foo', 'bar');
obj.set('fooAgain', 'barAgain');
obj.save().then(function() {
// We only update foo
obj.set('foo', 'baz');
return obj.save();
}).then(function() {
// Make sure the checking has been triggered
expect(triggerTime).toBe(2);
// Clear mock beforeSave
delete Parse.Cloud.Triggers.beforeSave.GameScore;
done();
}, function(error) {
fail(error);
done();
});
});
it('test afterSave get original object on update', function(done) { it('test afterSave get original object on update', function(done) {
var triggerTime = 0; var triggerTime = 0;
// Register a mock beforeSave hook // Register a mock beforeSave hook

View File

@@ -7,7 +7,6 @@ var DatabaseAdapter = require('../src/DatabaseAdapter');
var express = require('express'); var express = require('express');
var facebook = require('../src/oauth/facebook'); var facebook = require('../src/oauth/facebook');
var ParseServer = require('../src/index').ParseServer; var ParseServer = require('../src/index').ParseServer;
var DatabaseAdapter = require('../src/DatabaseAdapter');
var databaseURI = process.env.DATABASE_URI; var databaseURI = process.env.DATABASE_URI;
var cloudMain = process.env.CLOUD_CODE_MAIN || '../spec/cloud/main.js'; var cloudMain = process.env.CLOUD_CODE_MAIN || '../spec/cloud/main.js';

View File

@@ -116,18 +116,23 @@ RestWrite.prototype.runBeforeTrigger = function() {
if (this.query && this.query.objectId) { if (this.query && this.query.objectId) {
extraData.objectId = this.query.objectId; extraData.objectId = this.query.objectId;
} }
// Build the inflated object, for a create write, originalData is empty
var inflatedObject = triggers.inflate(extraData, this.originalData);; let originalObject = null;
inflatedObject._finishFetch(this.data); let updatedObject = null;
// Build the original object, we only do this for a update write
var originalObject;
if (this.query && this.query.objectId) { if (this.query && this.query.objectId) {
// This is an update for existing object.
originalObject = triggers.inflate(extraData, this.originalData); originalObject = triggers.inflate(extraData, this.originalData);
updatedObject = triggers.inflate(extraData, this.originalData);
updatedObject.set(Parse._decode(undefined, this.data));
} else {
// This is create of an object, so no original object exists.
// TODO: (nlutsenko) Use the same flow as for creation, when _Session triggers support is removed.
updatedObject = triggers.inflate(extraData, this.data);
} }
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
return triggers.maybeRunTrigger( return triggers.maybeRunTrigger(
'beforeSave', this.auth, inflatedObject, originalObject); 'beforeSave', this.auth, updatedObject, originalObject);
}).then((response) => { }).then((response) => {
if (response && response.object) { if (response && response.object) {
this.data = response.object; this.data = response.object;