Pass context in beforeDelete, afterDelete, beforeFind and Parse.Cloud.run. (#6666)

* add context for following hooks.
1. beforeDelete
2. afterDelete
3. beforeFind
4. Cloud Function

* revert un-necessary code change.

* fix: failing test cases.

* fix: failing test cases.

* fix: failing test cases.

* fix: failing test cases.

* fix: failing test cases.

* fix: failing test cases.

* fix: failing test cases.

* review changes

* revert changes

* revert changes

* review changes

* lint changes

* review changes
This commit is contained in:
yog27ray
2020-07-11 02:17:27 +05:30
committed by GitHub
parent 4437ea73ba
commit 34614e0f78
19 changed files with 130 additions and 43 deletions

View File

@@ -2968,4 +2968,58 @@ describe('afterLogin hook', () => {
obj.set("obj2", obj2); obj.set("obj2", obj2);
await obj.save(null, { context: { a: 'a' } }); await obj.save(null, { context: { a: 'a' } });
}); });
it('should have access to context as saveAll argument', async () => {
Parse.Cloud.beforeSave('TestObject', (req) => {
expect(req.context.a).toEqual('a');
});
Parse.Cloud.afterSave('TestObject', (req) => {
expect(req.context.a).toEqual('a');
});
const obj1 = new TestObject();
const obj2 = new TestObject();
await Parse.Object.saveAll([obj1, obj2], { context: { a: 'a' }});
});
it('should have access to context as destroyAll argument', async () => {
Parse.Cloud.beforeDelete('TestObject', (req) => {
expect(req.context.a).toEqual('a');
});
Parse.Cloud.afterDelete('TestObject', (req) => {
expect(req.context.a).toEqual('a');
});
const obj1 = new TestObject();
const obj2 = new TestObject();
await Parse.Object.saveAll([obj1, obj2]);
await Parse.Object.destroyAll([obj1, obj2], { context: { a: 'a' } });
});
it('should have access to context as destroy a object', async () => {
Parse.Cloud.beforeDelete('TestObject', (req) => {
expect(req.context.a).toEqual('a');
});
Parse.Cloud.afterDelete('TestObject', (req) => {
expect(req.context.a).toEqual('a');
});
const obj = new TestObject();
await obj.save();
await obj.destroy({ context: { a: 'a' } });
});
it('should have access to context in beforeFind hook', async () => {
Parse.Cloud.beforeFind('TestObject', (req) => {
expect(req.context.a).toEqual('a');
});
const query = new Parse.Query('TestObject');
return query.find({ context: { a: 'a' } });
});
it('should have access to context when cloud function is called.', async () => {
Parse.Cloud.define('contextTest', async (req) => {
expect(req.context.a).toEqual('a');
return {};
});
await Parse.Cloud.run('contextTest', {}, { context: { a: 'a' } });
});
}); });

View File

@@ -5,7 +5,7 @@ const createObject = async (className, fields, config, auth, info) => {
fields = {}; fields = {};
} }
return (await rest.create(config, auth, className, fields, info.clientSDK)) return (await rest.create(config, auth, className, fields, info.clientSDK, info.context))
.response; .response;
}; };
@@ -27,12 +27,13 @@ const updateObject = async (
className, className,
{ objectId }, { objectId },
fields, fields,
info.clientSDK info.clientSDK,
info.context
)).response; )).response;
}; };
const deleteObject = async (className, objectId, config, auth, info) => { const deleteObject = async (className, objectId, config, auth, info) => {
await rest.del(config, auth, className, objectId, info.clientSDK); await rest.del(config, auth, className, objectId, info.context);
return true; return true;
}; };

View File

@@ -74,7 +74,8 @@ const getObject = async (
className, className,
objectId, objectId,
options, options,
info.clientSDK info.clientSDK,
info.context
); );
if (!response.results || response.results.length == 0) { if (!response.results || response.results.length == 0) {
@@ -142,7 +143,8 @@ const findObjects = async (
className, className,
where, where,
preCountOptions, preCountOptions,
info.clientSDK info.clientSDK,
info.context
) )
).count; ).count;
if ((skip || 0) + limit < preCount) { if ((skip || 0) + limit < preCount) {
@@ -222,7 +224,8 @@ const findObjects = async (
className, className,
where, where,
options, options,
info.clientSDK info.clientSDK,
info.context
); );
results = findResult.results; results = findResult.results;
count = findResult.count; count = findResult.count;

View File

@@ -55,7 +55,8 @@ const getUserFromSessionToken = async (
'_Session', '_Session',
{ sessionToken }, { sessionToken },
options, options,
info.clientVersion info.clientVersion,
info.context,
); );
if ( if (
!response.results || !response.results ||

View File

@@ -107,6 +107,7 @@ function ParseServerRESTController(applicationId, router) {
info: { info: {
applicationId: applicationId, applicationId: applicationId,
sessionToken: options.sessionToken, sessionToken: options.sessionToken,
context: options.context || {}, // Add context
}, },
query, query,
}; };

View File

@@ -32,6 +32,7 @@ function RestWrite(
data, data,
originalData, originalData,
clientSDK, clientSDK,
context,
action action
) { ) {
if (auth.isReadOnly) { if (auth.isReadOnly) {
@@ -46,18 +47,12 @@ function RestWrite(
this.clientSDK = clientSDK; this.clientSDK = clientSDK;
this.storage = {}; this.storage = {};
this.runOptions = {}; this.runOptions = {};
this.context = {}; this.context = context || {};
if (action) { if (action) {
this.runOptions.action = action; this.runOptions.action = action;
} }
// Parse context
if (data._context && data._context instanceof Object) {
this.context = data._context;
delete data._context;
}
if (!query) { if (!query) {
if (this.config.allowCustomObjectId) { if (this.config.allowCustomObjectId) {
if ( if (

View File

@@ -69,7 +69,8 @@ export class AggregateRouter extends ClassesRouter {
this.className(req), this.className(req),
body.where, body.where,
options, options,
req.info.clientSDK req.info.clientSDK,
req.info.context,
) )
.then(response => { .then(response => {
for (const result of response.results) { for (const result of response.results) {

View File

@@ -21,7 +21,8 @@ export class AudiencesRouter extends ClassesRouter {
'_Audience', '_Audience',
body.where, body.where,
options, options,
req.info.clientSDK req.info.clientSDK,
req.info.context,
) )
.then(response => { .then(response => {
response.results.forEach(item => { response.results.forEach(item => {

View File

@@ -40,7 +40,8 @@ export class ClassesRouter extends PromiseRouter {
this.className(req), this.className(req),
body.where, body.where,
options, options,
req.info.clientSDK req.info.clientSDK,
req.info.context,
) )
.then(response => { .then(response => {
return { response: response }; return { response: response };
@@ -120,7 +121,8 @@ export class ClassesRouter extends PromiseRouter {
req.auth, req.auth,
this.className(req), this.className(req),
req.body, req.body,
req.info.clientSDK req.info.clientSDK,
req.info.context
); );
} }
@@ -132,7 +134,8 @@ export class ClassesRouter extends PromiseRouter {
this.className(req), this.className(req),
where, where,
req.body, req.body,
req.info.clientSDK req.info.clientSDK,
req.info.context
); );
} }
@@ -143,7 +146,7 @@ export class ClassesRouter extends PromiseRouter {
req.auth, req.auth,
this.className(req), this.className(req),
req.params.objectId, req.params.objectId,
req.info.clientSDK req.info.context
) )
.then(() => { .then(() => {
return { response: {} }; return { response: {} };

View File

@@ -88,7 +88,8 @@ export class CloudCodeRouter extends PromiseRouter {
req.auth, req.auth,
'_JobSchedule', '_JobSchedule',
formatJobSchedule(job_schedule), formatJobSchedule(job_schedule),
req.client req.client,
req.info.context
); );
} }
@@ -102,7 +103,9 @@ export class CloudCodeRouter extends PromiseRouter {
req.auth, req.auth,
'_JobSchedule', '_JobSchedule',
{ objectId }, { objectId },
formatJobSchedule(job_schedule) formatJobSchedule(job_schedule),
undefined,
req.info.context
) )
.then(response => { .then(response => {
return { return {
@@ -114,7 +117,7 @@ export class CloudCodeRouter extends PromiseRouter {
static deleteJob(req) { static deleteJob(req) {
const { objectId } = req.params; const { objectId } = req.params;
return rest return rest
.del(req.config, req.auth, '_JobSchedule', objectId) .del(req.config, req.auth, '_JobSchedule', objectId, req.info.context)
.then(response => { .then(response => {
return { return {
response, response,

View File

@@ -148,6 +148,7 @@ export class FunctionsRouter extends PromiseRouter {
headers: req.config.headers, headers: req.config.headers,
ip: req.config.ip, ip: req.config.ip,
functionName, functionName,
context: req.info.context,
}; };
if (theValidator && typeof theValidator === 'function') { if (theValidator && typeof theValidator === 'function') {

View File

@@ -51,7 +51,8 @@ function getFileForProductIdentifier(productIdentifier, req) {
'_Product', '_Product',
{ productIdentifier: productIdentifier }, { productIdentifier: productIdentifier },
undefined, undefined,
req.info.clientSDK req.info.clientSDK,
req.info.context
) )
.then(function(result) { .then(function(result) {
const products = result.results; const products = result.results;

View File

@@ -21,7 +21,8 @@ export class InstallationsRouter extends ClassesRouter {
'_Installation', '_Installation',
body.where, body.where,
options, options,
req.info.clientSDK req.info.clientSDK,
req.info.context
) )
.then(response => { .then(response => {
return { response: response }; return { response: response };

View File

@@ -23,7 +23,8 @@ export class SessionsRouter extends ClassesRouter {
'_Session', '_Session',
{ sessionToken: req.info.sessionToken }, { sessionToken: req.info.sessionToken },
undefined, undefined,
req.info.clientSDK req.info.clientSDK,
req.info.context
) )
.then(response => { .then(response => {
if (!response.results || response.results.length == 0) { if (!response.results || response.results.length == 0) {

View File

@@ -178,7 +178,8 @@ export class UsersRouter extends ClassesRouter {
'_Session', '_Session',
{ sessionToken }, { sessionToken },
{ include: 'user' }, { include: 'user' },
req.info.clientSDK req.info.clientSDK,
req.info.context
) )
.then(response => { .then(response => {
if ( if (
@@ -302,7 +303,8 @@ export class UsersRouter extends ClassesRouter {
'_Session', '_Session',
{ sessionToken: req.info.sessionToken }, { sessionToken: req.info.sessionToken },
undefined, undefined,
req.info.clientSDK req.info.clientSDK,
req.info.context
) )
.then(records => { .then(records => {
if (records.results && records.results.length) { if (records.results && records.results.length) {
@@ -311,7 +313,8 @@ export class UsersRouter extends ClassesRouter {
req.config, req.config,
Auth.master(req.config), Auth.master(req.config),
'_Session', '_Session',
records.results[0].objectId records.results[0].objectId,
req.info.context
) )
.then(() => { .then(() => {
this._runAfterLogoutTrigger(req, records.results[0]); this._runAfterLogoutTrigger(req, records.results[0]);

View File

@@ -100,9 +100,6 @@ function handleBatch(router, req) {
info: req.info, info: req.info,
}; };
// Add context to request body
if (req.body._context) { request.body._context = req.body._context; }
return router return router
.tryRouteRequest(restRequest.method, routablePath, request) .tryRouteRequest(restRequest.method, routablePath, request)
.then( .then(

View File

@@ -33,6 +33,7 @@ export function handleParseHeaders(req, res, next) {
dotNetKey: req.get('X-Parse-Windows-Key'), dotNetKey: req.get('X-Parse-Windows-Key'),
restAPIKey: req.get('X-Parse-REST-API-Key'), restAPIKey: req.get('X-Parse-REST-API-Key'),
clientVersion: req.get('X-Parse-Client-Version'), clientVersion: req.get('X-Parse-Client-Version'),
context: {},
}; };
var basicAuth = httpAuth(req); var basicAuth = httpAuth(req);
@@ -103,6 +104,10 @@ export function handleParseHeaders(req, res, next) {
info.masterKey = req.body._MasterKey; info.masterKey = req.body._MasterKey;
delete req.body._MasterKey; delete req.body._MasterKey;
} }
if (req.body._context && req.body._context instanceof Object) {
info.context = req.body._context;
delete req.body._context;
}
if (req.body._ContentType) { if (req.body._ContentType) {
req.headers['content-type'] = req.body._ContentType; req.headers['content-type'] = req.body._ContentType;
delete req.body._ContentType; delete req.body._ContentType;

View File

@@ -31,7 +31,7 @@ function checkLiveQuery(className, config) {
} }
// Returns a promise for an object with optional keys 'results' and 'count'. // Returns a promise for an object with optional keys 'results' and 'count'.
function find(config, auth, className, restWhere, restOptions, clientSDK) { function find(config, auth, className, restWhere, restOptions, clientSDK, context) {
enforceRoleSecurity('find', className, auth); enforceRoleSecurity('find', className, auth);
return triggers return triggers
.maybeRunQueryTrigger( .maybeRunQueryTrigger(
@@ -40,7 +40,8 @@ function find(config, auth, className, restWhere, restOptions, clientSDK) {
restWhere, restWhere,
restOptions, restOptions,
config, config,
auth auth,
context
) )
.then(result => { .then(result => {
restWhere = result.restWhere || restWhere; restWhere = result.restWhere || restWhere;
@@ -58,7 +59,7 @@ function find(config, auth, className, restWhere, restOptions, clientSDK) {
} }
// get is just like find but only queries an objectId. // get is just like find but only queries an objectId.
const get = (config, auth, className, objectId, restOptions, clientSDK) => { const get = (config, auth, className, objectId, restOptions, clientSDK, context) => {
var restWhere = { objectId }; var restWhere = { objectId };
enforceRoleSecurity('get', className, auth); enforceRoleSecurity('get', className, auth);
return triggers return triggers
@@ -69,6 +70,7 @@ const get = (config, auth, className, objectId, restOptions, clientSDK) => {
restOptions, restOptions,
config, config,
auth, auth,
context,
true true
) )
.then(result => { .then(result => {
@@ -87,7 +89,7 @@ const get = (config, auth, className, objectId, restOptions, clientSDK) => {
}; };
// Returns a promise that doesn't resolve to any useful value. // Returns a promise that doesn't resolve to any useful value.
function del(config, auth, className, objectId) { function del(config, auth, className, objectId, context) {
if (typeof objectId !== 'string') { if (typeof objectId !== 'string') {
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad objectId'); throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad objectId');
} }
@@ -134,7 +136,8 @@ function del(config, auth, className, objectId) {
auth, auth,
inflatedObject, inflatedObject,
null, null,
config config,
context
); );
} }
throw new Parse.Error( throw new Parse.Error(
@@ -187,7 +190,8 @@ function del(config, auth, className, objectId) {
auth, auth,
inflatedObject, inflatedObject,
null, null,
config config,
context
); );
}) })
.catch(error => { .catch(error => {
@@ -196,7 +200,7 @@ function del(config, auth, className, objectId) {
} }
// Returns a promise for a {response, status, location} object. // Returns a promise for a {response, status, location} object.
function create(config, auth, className, restObject, clientSDK) { function create(config, auth, className, restObject, clientSDK, context) {
enforceRoleSecurity('create', className, auth); enforceRoleSecurity('create', className, auth);
var write = new RestWrite( var write = new RestWrite(
config, config,
@@ -205,7 +209,8 @@ function create(config, auth, className, restObject, clientSDK) {
null, null,
restObject, restObject,
null, null,
clientSDK clientSDK,
context
); );
return write.execute(); return write.execute();
} }
@@ -213,7 +218,7 @@ function create(config, auth, className, restObject, clientSDK) {
// Returns a promise that contains the fields of the update that the // Returns a promise that contains the fields of the update that the
// REST API is supposed to return. // REST API is supposed to return.
// Usually, this is just updatedAt. // Usually, this is just updatedAt.
function update(config, auth, className, restWhere, restObject, clientSDK) { function update(config, auth, className, restWhere, restObject, clientSDK, context) {
enforceRoleSecurity('update', className, auth); enforceRoleSecurity('update', className, auth);
return Promise.resolve() return Promise.resolve()
@@ -252,6 +257,7 @@ function update(config, auth, className, restWhere, restObject, clientSDK) {
restObject, restObject,
originalRestObject, originalRestObject,
clientSDK, clientSDK,
context,
'update' 'update'
).execute(); ).execute();
}) })

View File

@@ -233,7 +233,10 @@ export function getRequestObject(
request.original = originalParseObject; request.original = originalParseObject;
} }
if (triggerType === Types.beforeSave || triggerType === Types.afterSave) { if (triggerType === Types.beforeSave ||
triggerType === Types.afterSave ||
triggerType === Types.beforeDelete ||
triggerType === Types.afterDelete) {
// Set a copy of the context on the request object. // Set a copy of the context on the request object.
request.context = Object.assign({}, context); request.context = Object.assign({}, context);
} }
@@ -259,6 +262,7 @@ export function getRequestQueryObject(
query, query,
count, count,
config, config,
context,
isGet isGet
) { ) {
isGet = !!isGet; isGet = !!isGet;
@@ -272,6 +276,7 @@ export function getRequestQueryObject(
isGet, isGet,
headers: config.headers, headers: config.headers,
ip: config.ip, ip: config.ip,
context: context || {},
}; };
if (!auth) { if (!auth) {
@@ -460,6 +465,7 @@ export function maybeRunQueryTrigger(
restOptions, restOptions,
config, config,
auth, auth,
context,
isGet isGet
) { ) {
const trigger = getTrigger(className, triggerType, config.applicationId); const trigger = getTrigger(className, triggerType, config.applicationId);
@@ -485,6 +491,7 @@ export function maybeRunQueryTrigger(
parseQuery, parseQuery,
count, count,
config, config,
context,
isGet isGet
); );
return Promise.resolve() return Promise.resolve()
@@ -605,7 +612,9 @@ export function maybeRunTrigger(
); );
if ( if (
triggerType === Types.beforeSave || triggerType === Types.beforeSave ||
triggerType === Types.afterSave triggerType === Types.afterSave ||
triggerType === Types.beforeDelete ||
triggerType === Types.afterDelete
) { ) {
Object.assign(context, request.context); Object.assign(context, request.context);
} }