Skip to content
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

Improvements in REST calls #1066

Merged
merged 3 commits into from
Mar 17, 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
64 changes: 64 additions & 0 deletions spec/ParseAPI.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,19 @@ describe('miscellaneous', function() {
});
});

it('test beforeSave returns value on create and update', (done) => {
var obj = new Parse.Object('BeforeSaveChanged');
obj.set('foo', 'bing');
obj.save().then(() => {
expect(obj.get('foo')).toEqual('baz');
obj.set('foo', 'bar');
return obj.save().then(() => {
expect(obj.get('foo')).toEqual('baz');
done();
})
})
});

it('test afterSave ran and created an object', function(done) {
var obj = new Parse.Object('AfterSaveTest');
obj.save();
Expand Down Expand Up @@ -383,6 +396,13 @@ describe('miscellaneous', function() {
});
});

it('should properly create an object in before save', (done) => {
Parse.Cloud.run('createBeforeSaveChangedObject').then((res) => {
expect(res.get('foo')).toEqual('baz');
done();
});
})

it('test rest_create_app', function(done) {
var appId;
Parse._request('POST', 'rest_create_app').then((res) => {
Expand Down Expand Up @@ -868,6 +888,50 @@ describe('miscellaneous', function() {
});
});

it('should return the updated fields on PUT', (done) => {
let obj = new Parse.Object('GameScore');
obj.save({a:'hello', c: 1, d: ['1'], e:['1'], f:['1','2']}).then(( ) => {
var headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
'X-Parse-Installation-Id': 'yolo'
};
request.put({
headers: headers,
url: 'http://localhost:8378/1/classes/GameScore/'+obj.id,
body: JSON.stringify({
a: 'b',
c: {"__op":"Increment","amount":2},
d: {"__op":"Add", objects: ['2']},
e: {"__op":"AddUnique", objects: ['1', '2']},
f: {"__op":"Remove", objects: ['2']},
selfThing: {"__type":"Pointer","className":"GameScore","objectId":obj.id},
})
}, (error, response, body) => {
body = JSON.parse(body);
expect(body.a).toBeUndefined();
expect(body.c).toEqual(3); // 2+1
expect(body.d.length).toBe(2);
expect(body.d.indexOf('1') > -1).toBe(true);
expect(body.d.indexOf('2') > -1).toBe(true);
expect(body.e.length).toBe(2);
expect(body.e.indexOf('1') > -1).toBe(true);
expect(body.e.indexOf('2') > -1).toBe(true);
expect(body.f.length).toBe(1);
expect(body.f.indexOf('1') > -1).toBe(true);
// return nothing on other self
expect(body.selfThing).toBeUndefined();
// updatedAt is always set
expect(body.updatedAt).not.toBeUndefined();
done();
});
}).fail((err) => {
fail('Should not fail');
done();
})
})

it('test cloud function error handling', (done) => {
// Register a function which will fail
Parse.Cloud.define('willFail', (req, res) => {
Expand Down
7 changes: 7 additions & 0 deletions spec/cloud/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,10 @@ Parse.Cloud.define('echoKeys', function(req, res){
javascriptKey: Parse.javascriptKey
})
});

Parse.Cloud.define('createBeforeSaveChangedObject', function(req, res){
var obj = new Parse.Object('BeforeSaveChanged');
obj.save().then(() => {
res.success(obj);
})
})
35 changes: 25 additions & 10 deletions src/Controllers/DatabaseController.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ DatabaseController.prototype.untransformObject = function(
// one of the provided strings must provide the caller with
// write permissions.
DatabaseController.prototype.update = function(className, query, update, options) {

const originalUpdate = update;
// Make a copy of the object, so we don't mutate the incoming data.
update = deepcopy(update);

Expand Down Expand Up @@ -177,18 +179,27 @@ DatabaseController.prototype.update = function(className, query, update, options
return Promise.reject(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
'Object not found.'));
}

let response = {};
let inc = mongoUpdate['$inc'];
if (inc) {
Object.keys(inc).forEach(key => {
response[key] = result[key];
});
}
return response;
return sanitizeDatabaseResult(originalUpdate, result);
});
};

function sanitizeDatabaseResult(originalObject, result) {
let response = {};
if (!result) {
return Promise.resolve(response);
}
Object.keys(originalObject).forEach(key => {
let keyUpdate = originalObject[key];
// determine if that was an op
if (keyUpdate && typeof keyUpdate === 'object' && keyUpdate.__op
&& ['Add', 'AddUnique', 'Remove', 'Increment'].indexOf(keyUpdate.__op) > -1) {
// only valid ops that produce an actionable result
response[key] = result[key];
}
});
return Promise.resolve(response);
}

// Processes relation-updating operations from a REST-format update.
// Returns a promise that resolves successfully when these are
// processed.
Expand Down Expand Up @@ -313,6 +324,7 @@ DatabaseController.prototype.destroy = function(className, query, options = {})
// Returns a promise that resolves successfully iff the object saved.
DatabaseController.prototype.create = function(className, object, options) {
// Make a copy of the object, so we don't mutate the incoming data.
let originalObject = object;
object = deepcopy(object);

var schema;
Expand All @@ -333,6 +345,9 @@ DatabaseController.prototype.create = function(className, object, options) {
.then(coll => {
var mongoObject = transform.transformCreate(schema, className, object);
return coll.insertOne(mongoObject);
})
.then(result => {
return sanitizeDatabaseResult(originalObject, result.ops[0]);
});
};

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

DatabaseController.prototype.addInObjectIdsIds = function(ids, query) {
if (typeof query.objectId == 'string') {
// Add equality op as we are sure
// Add equality op as we are sure
// we had a constraint on that one
query.objectId = {'$eq': query.objectId};
}
Expand Down
17 changes: 13 additions & 4 deletions src/RestWrite.js
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,12 @@ RestWrite.prototype.runDatabaseOperation = function() {
return this.config.database.update(
this.className, this.query, this.data, this.runOptions).then((resp) => {
resp.updatedAt = this.updatedAt;
if (this.storage['changedByTrigger']) {
resp = Object.keys(this.data).reduce((memo, key) => {
memo[key] = resp[key] || this.data[key];
return memo;
}, resp);
}
this.response = {
response: resp
};
Expand All @@ -725,13 +731,16 @@ RestWrite.prototype.runDatabaseOperation = function() {
}
// Run a create
return this.config.database.create(this.className, this.data, this.runOptions)
.then(() => {
var resp = {
.then((resp) => {
Object.assign(resp, {
objectId: this.data.objectId,
createdAt: this.data.createdAt
};
});
if (this.storage['changedByTrigger']) {
Object.assign(resp, this.data);
resp = Object.keys(this.data).reduce((memo, key) => {
memo[key] = resp[key] || this.data[key];
return memo;
}, resp);
}
if (this.storage['token']) {
resp.sessionToken = this.storage['token'];
Expand Down