Skip to content

Adding support for AfterFind #2968

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
Nov 12, 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
132 changes: 131 additions & 1 deletion spec/CloudCode.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1306,4 +1306,134 @@ describe('beforeFind hooks', () => {
done();
});
});
})
});

describe('afterFind hooks', () => {
it('should add afterFind trigger using get',(done) => {
Parse.Cloud.afterFind('MyObject', (req, res) => {
for(let i = 0 ; i < req.objects.length ; i++){
req.objects[i].set("secretField","###");
}
res.success(req.objects);
});
let obj = new Parse.Object('MyObject');
obj.set('secretField', 'SSID');
obj.save().then(function() {
let query = new Parse.Query('MyObject');
query.get(obj.id).then(function(result) {
expect(result.get('secretField')).toEqual('###');
done();
}, function(error) {
fail(error);
done();
});
}, function(error) {
fail(error);
done();
});
});

it('should add afterFind trigger using find',(done) => {
Parse.Cloud.afterFind('MyObject', (req, res) => {
for(let i = 0 ; i < req.objects.length ; i++){
req.objects[i].set("secretField","###");
}
res.success(req.objects);
});
let obj = new Parse.Object('MyObject');
obj.set('secretField', 'SSID');
obj.save().then(function() {
let query = new Parse.Query('MyObject');
query.equalTo('objectId',obj.id);
query.find().then(function(results) {
expect(results[0].get('secretField')).toEqual('###');
done();
}, function(error) {
fail(error);
done();
});
}, function(error) {
fail(error);
done();
});
});

it('should filter out results',(done) => {
Parse.Cloud.afterFind('MyObject', (req, res) => {
let filteredResults = [];
for(let i = 0 ; i < req.objects.length ; i++){
if(req.objects[i].get("secretField")==="SSID1") {
filteredResults.push(req.objects[i]);
}
}
res.success(filteredResults);
});
let obj0 = new Parse.Object('MyObject');
obj0.set('secretField', 'SSID1');
let obj1 = new Parse.Object('MyObject');
obj1.set('secretField', 'SSID2');
Parse.Object.saveAll([obj0, obj1]).then(function() {
let query = new Parse.Query('MyObject');
query.find().then(function(results) {
expect(results[0].get('secretField')).toEqual('SSID1');
expect(results.length).toEqual(1);
done();
}, function(error) {
fail(error);
done();
});
}, function(error) {
fail(error);
done();
});
});

it('should handle failures',(done) => {
Parse.Cloud.afterFind('MyObject', (req, res) => {
res.error(Parse.Error.SCRIPT_FAILED, "It should fail");
});
let obj = new Parse.Object('MyObject');
obj.set('secretField', 'SSID');
obj.save().then(function() {
let query = new Parse.Query('MyObject');
query.equalTo('objectId',obj.id);
query.find().then(function(results) {
fail("AfterFind should handle response failure correctly");
done();
}, function(error) {
done();
});
}, function(error) {
done();
});
});

it('should also work with promise',(done) => {
Parse.Cloud.afterFind('MyObject', (req, res) => {
let promise = new Parse.Promise();
setTimeout(function(){
for(let i = 0 ; i < req.objects.length ; i++){
req.objects[i].set("secretField","###");
}
promise.resolve(req.objects);
}, 1000);
return promise;
});
let obj = new Parse.Object('MyObject');
obj.set('secretField', 'SSID');
obj.save().then(function() {
let query = new Parse.Query('MyObject');
query.equalTo('objectId',obj.id);
query.find().then(function(results) {
expect(results[0].get('secretField')).toEqual('###');
done();
}, function(error) {
fail(error);
});
}, function(error) {
fail(error);
});
});

});

19 changes: 19 additions & 0 deletions src/RestQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

var SchemaController = require('./Controllers/SchemaController');
var Parse = require('parse/node').Parse;
const triggers = require('./triggers');

import { default as FilesController } from './Controllers/FilesController';

Expand Down Expand Up @@ -122,6 +123,8 @@ RestQuery.prototype.execute = function(executeOptions) {
return this.runCount();
}).then(() => {
return this.handleInclude();
}).then(() => {
return this.runAfterFindTrigger();
}).then(() => {
return this.response;
});
Expand Down Expand Up @@ -468,6 +471,22 @@ RestQuery.prototype.handleInclude = function() {
return pathResponse;
};

//Returns a promise of a processed set of results
RestQuery.prototype.runAfterFindTrigger = function() {
if (!this.response) {
return;
}
// Avoid doing any setup for triggers if there is no 'afterFind' trigger for this class.
const hasAfterFindHook = triggers.triggerExists(this.className, triggers.Types.afterFind, this.config.applicationId);
if (!hasAfterFindHook) {
return Promise.resolve();
}
// Run afterFind trigger and set the new results
return triggers.maybeRunAfterFindTrigger(triggers.Types.afterFind, this.auth, this.className,this.response.results, this.config).then((results) => {
this.response.results = results;
});
};

// Adds included values to the response.
// Path is a list of field names.
// Returns a promise for an augmented response.
Expand Down
5 changes: 5 additions & 0 deletions src/cloud-code/Parse.Cloud.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ ParseCloud.beforeFind = function(parseClass, handler) {
triggers.addTrigger(triggers.Types.beforeFind, className, handler, Parse.applicationId);
};

ParseCloud.afterFind = function(parseClass, handler) {
const className = getClassName(parseClass);
triggers.addTrigger(triggers.Types.afterFind, className, handler, Parse.applicationId);
};

ParseCloud._removeAllHooks = () => {
triggers._unregisterAll();
}
Expand Down
48 changes: 47 additions & 1 deletion src/triggers.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export const Types = {
afterSave: 'afterSave',
beforeDelete: 'beforeDelete',
afterDelete: 'afterDelete',
beforeFind: 'beforeFind'
beforeFind: 'beforeFind',
afterFind: 'afterFind'
};

const baseStore = function() {
Expand Down Expand Up @@ -185,6 +186,15 @@ export function getRequestQueryObject(triggerType, auth, query, config) {
export function getResponseObject(request, resolve, reject) {
return {
success: function(response) {
if (request.triggerName === Types.afterFind) {
if(!response){
response = request.objects;
}
response = response.map(object => {
return object.toJSON();
});
return resolve(response);
}
// Use the JSON response
if (response && !request.object.equals(response)
&& request.triggerName === Types.beforeSave) {
Expand Down Expand Up @@ -240,6 +250,42 @@ function logTriggerErrorBeforeHook(triggerType, className, input, auth, error) {
});
}

export function maybeRunAfterFindTrigger(triggerType, auth, className, objects, config) {
return new Promise((resolve, reject) => {
const trigger = getTrigger(className, triggerType, config.applicationId);
if (!trigger) {
return resolve();
}
const request = getRequestObject(triggerType, auth, null, null, config);
const response = getResponseObject(request,
object => {
resolve(object);
},
error => {
reject(error);
});
logTriggerSuccessBeforeHook(triggerType, className, 'AfterFind', JSON.stringify(objects), auth);
request.objects = objects.map(object => {
//setting the class name to transform into parse object
object.className=className;
return Parse.Object.fromJSON(object);
});
const triggerPromise = trigger(request, response);
if (triggerPromise && typeof triggerPromise.then === "function") {
return triggerPromise.then(promiseResults => {
if(promiseResults) {
resolve(promiseResults);
}else{
return reject(new Parse.Error(Parse.Error.SCRIPT_FAILED, "AfterFind expect results to be returned in the promise"));
}
});
}
}).then((results) => {
logTriggerAfterHook(triggerType, className, JSON.stringify(results), auth);
return results;
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the logLine here by replacing with:

}).then((results) => {
  logTriggerAfterHook(triggerType, className, JSON.stringify(results), auth);
  return results;
});

}

export function maybeRunQueryTrigger(triggerType, className, restWhere, restOptions, config, auth) {
let trigger = getTrigger(className, triggerType, config.applicationId);
if (!trigger) {
Expand Down