Skip to content

Commit 3837d33

Browse files
committed
Merge pull request #1066 from ParsePlatform/flovilmart.returnsFullModificationsForREST
Improvements in REST calls
2 parents 7b3448e + e270964 commit 3837d33

File tree

4 files changed

+109
-14
lines changed

4 files changed

+109
-14
lines changed

spec/ParseAPI.spec.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,19 @@ describe('miscellaneous', function() {
224224
});
225225
});
226226

227+
it('test beforeSave returns value on create and update', (done) => {
228+
var obj = new Parse.Object('BeforeSaveChanged');
229+
obj.set('foo', 'bing');
230+
obj.save().then(() => {
231+
expect(obj.get('foo')).toEqual('baz');
232+
obj.set('foo', 'bar');
233+
return obj.save().then(() => {
234+
expect(obj.get('foo')).toEqual('baz');
235+
done();
236+
})
237+
})
238+
});
239+
227240
it('test afterSave ran and created an object', function(done) {
228241
var obj = new Parse.Object('AfterSaveTest');
229242
obj.save();
@@ -383,6 +396,13 @@ describe('miscellaneous', function() {
383396
});
384397
});
385398

399+
it('should properly create an object in before save', (done) => {
400+
Parse.Cloud.run('createBeforeSaveChangedObject').then((res) => {
401+
expect(res.get('foo')).toEqual('baz');
402+
done();
403+
});
404+
})
405+
386406
it('test rest_create_app', function(done) {
387407
var appId;
388408
Parse._request('POST', 'rest_create_app').then((res) => {
@@ -868,6 +888,50 @@ describe('miscellaneous', function() {
868888
});
869889
});
870890

891+
it('should return the updated fields on PUT', (done) => {
892+
let obj = new Parse.Object('GameScore');
893+
obj.save({a:'hello', c: 1, d: ['1'], e:['1'], f:['1','2']}).then(( ) => {
894+
var headers = {
895+
'Content-Type': 'application/json',
896+
'X-Parse-Application-Id': 'test',
897+
'X-Parse-REST-API-Key': 'rest',
898+
'X-Parse-Installation-Id': 'yolo'
899+
};
900+
request.put({
901+
headers: headers,
902+
url: 'http://localhost:8378/1/classes/GameScore/'+obj.id,
903+
body: JSON.stringify({
904+
a: 'b',
905+
c: {"__op":"Increment","amount":2},
906+
d: {"__op":"Add", objects: ['2']},
907+
e: {"__op":"AddUnique", objects: ['1', '2']},
908+
f: {"__op":"Remove", objects: ['2']},
909+
selfThing: {"__type":"Pointer","className":"GameScore","objectId":obj.id},
910+
})
911+
}, (error, response, body) => {
912+
body = JSON.parse(body);
913+
expect(body.a).toBeUndefined();
914+
expect(body.c).toEqual(3); // 2+1
915+
expect(body.d.length).toBe(2);
916+
expect(body.d.indexOf('1') > -1).toBe(true);
917+
expect(body.d.indexOf('2') > -1).toBe(true);
918+
expect(body.e.length).toBe(2);
919+
expect(body.e.indexOf('1') > -1).toBe(true);
920+
expect(body.e.indexOf('2') > -1).toBe(true);
921+
expect(body.f.length).toBe(1);
922+
expect(body.f.indexOf('1') > -1).toBe(true);
923+
// return nothing on other self
924+
expect(body.selfThing).toBeUndefined();
925+
// updatedAt is always set
926+
expect(body.updatedAt).not.toBeUndefined();
927+
done();
928+
});
929+
}).fail((err) => {
930+
fail('Should not fail');
931+
done();
932+
})
933+
})
934+
871935
it('test cloud function error handling', (done) => {
872936
// Register a function which will fail
873937
Parse.Cloud.define('willFail', (req, res) => {

spec/cloud/main.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,10 @@ Parse.Cloud.define('echoKeys', function(req, res){
108108
javascriptKey: Parse.javascriptKey
109109
})
110110
});
111+
112+
Parse.Cloud.define('createBeforeSaveChangedObject', function(req, res){
113+
var obj = new Parse.Object('BeforeSaveChanged');
114+
obj.save().then(() => {
115+
res.success(obj);
116+
})
117+
})

src/Controllers/DatabaseController.js

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ DatabaseController.prototype.untransformObject = function(
139139
// one of the provided strings must provide the caller with
140140
// write permissions.
141141
DatabaseController.prototype.update = function(className, query, update, options) {
142+
143+
const originalUpdate = update;
142144
// Make a copy of the object, so we don't mutate the incoming data.
143145
update = deepcopy(update);
144146

@@ -177,18 +179,27 @@ DatabaseController.prototype.update = function(className, query, update, options
177179
return Promise.reject(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
178180
'Object not found.'));
179181
}
180-
181-
let response = {};
182-
let inc = mongoUpdate['$inc'];
183-
if (inc) {
184-
Object.keys(inc).forEach(key => {
185-
response[key] = result[key];
186-
});
187-
}
188-
return response;
182+
return sanitizeDatabaseResult(originalUpdate, result);
189183
});
190184
};
191185

186+
function sanitizeDatabaseResult(originalObject, result) {
187+
let response = {};
188+
if (!result) {
189+
return Promise.resolve(response);
190+
}
191+
Object.keys(originalObject).forEach(key => {
192+
let keyUpdate = originalObject[key];
193+
// determine if that was an op
194+
if (keyUpdate && typeof keyUpdate === 'object' && keyUpdate.__op
195+
&& ['Add', 'AddUnique', 'Remove', 'Increment'].indexOf(keyUpdate.__op) > -1) {
196+
// only valid ops that produce an actionable result
197+
response[key] = result[key];
198+
}
199+
});
200+
return Promise.resolve(response);
201+
}
202+
192203
// Processes relation-updating operations from a REST-format update.
193204
// Returns a promise that resolves successfully when these are
194205
// processed.
@@ -313,6 +324,7 @@ DatabaseController.prototype.destroy = function(className, query, options = {})
313324
// Returns a promise that resolves successfully iff the object saved.
314325
DatabaseController.prototype.create = function(className, object, options) {
315326
// Make a copy of the object, so we don't mutate the incoming data.
327+
let originalObject = object;
316328
object = deepcopy(object);
317329

318330
var schema;
@@ -333,6 +345,9 @@ DatabaseController.prototype.create = function(className, object, options) {
333345
.then(coll => {
334346
var mongoObject = transform.transformCreate(schema, className, object);
335347
return coll.insertOne(mongoObject);
348+
})
349+
.then(result => {
350+
return sanitizeDatabaseResult(originalObject, result.ops[0]);
336351
});
337352
};
338353

@@ -474,7 +489,7 @@ DatabaseController.prototype.reduceRelationKeys = function(className, query) {
474489

475490
DatabaseController.prototype.addInObjectIdsIds = function(ids, query) {
476491
if (typeof query.objectId == 'string') {
477-
// Add equality op as we are sure
492+
// Add equality op as we are sure
478493
// we had a constraint on that one
479494
query.objectId = {'$eq': query.objectId};
480495
}

src/RestWrite.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,12 @@ RestWrite.prototype.runDatabaseOperation = function() {
712712
return this.config.database.update(
713713
this.className, this.query, this.data, this.runOptions).then((resp) => {
714714
resp.updatedAt = this.updatedAt;
715+
if (this.storage['changedByTrigger']) {
716+
resp = Object.keys(this.data).reduce((memo, key) => {
717+
memo[key] = resp[key] || this.data[key];
718+
return memo;
719+
}, resp);
720+
}
715721
this.response = {
716722
response: resp
717723
};
@@ -726,13 +732,16 @@ RestWrite.prototype.runDatabaseOperation = function() {
726732
}
727733
// Run a create
728734
return this.config.database.create(this.className, this.data, this.runOptions)
729-
.then(() => {
730-
var resp = {
735+
.then((resp) => {
736+
Object.assign(resp, {
731737
objectId: this.data.objectId,
732738
createdAt: this.data.createdAt
733-
};
739+
});
734740
if (this.storage['changedByTrigger']) {
735-
Object.assign(resp, this.data);
741+
resp = Object.keys(this.data).reduce((memo, key) => {
742+
memo[key] = resp[key] || this.data[key];
743+
return memo;
744+
}, resp);
736745
}
737746
if (this.storage['token']) {
738747
resp.sessionToken = this.storage['token'];

0 commit comments

Comments
 (0)