Validation Handler Update (#6968)
* Initial Commit * Update FunctionsRouter.js * Update FunctionsRouter.js * Change params to fields * Changes requested * Fix failing tests * More tests * More tests * Remove existing functionality * Remove legacy tests * fix array typo * Update triggers.js * Docs * Allow requireUserKeys to be object * validateMasterKey * Improve documentation Co-authored-by: Diamond Lewis <findlewis@gmail.com>
This commit is contained in:
1174
spec/CloudCode.Validator.spec.js
Normal file
1174
spec/CloudCode.Validator.spec.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -113,6 +113,21 @@ describe('Cloud Code', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns an empty error', done => {
|
||||||
|
Parse.Cloud.define('cloudCodeWithError', () => {
|
||||||
|
throw null;
|
||||||
|
});
|
||||||
|
|
||||||
|
Parse.Cloud.run('cloudCodeWithError').then(
|
||||||
|
() => done.fail('should not succeed'),
|
||||||
|
e => {
|
||||||
|
expect(e.code).toEqual(141);
|
||||||
|
expect(e.message).toEqual('Script failed.');
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('beforeSave rejection with custom error code', function (done) {
|
it('beforeSave rejection with custom error code', function (done) {
|
||||||
Parse.Cloud.beforeSave('BeforeSaveFailWithErrorCode', function () {
|
Parse.Cloud.beforeSave('BeforeSaveFailWithErrorCode', function () {
|
||||||
throw new Parse.Error(999, 'Nope');
|
throw new Parse.Error(999, 'Nope');
|
||||||
@@ -2675,6 +2690,34 @@ describe('beforeLogin hook', () => {
|
|||||||
expect(result).toBe(file);
|
expect(result).toBe(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('throw custom error from beforeSaveFile', async done => {
|
||||||
|
Parse.Cloud.beforeSaveFile(() => {
|
||||||
|
throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail');
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
|
||||||
|
await file.save({ useMasterKey: true });
|
||||||
|
fail('error should have thrown');
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throw empty error from beforeSaveFile', async done => {
|
||||||
|
Parse.Cloud.beforeSaveFile(() => {
|
||||||
|
throw null;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
|
||||||
|
await file.save({ useMasterKey: true });
|
||||||
|
fail('error should have thrown');
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.code).toBe(130);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('beforeSaveFile should return file that is already saved and not save anything to files adapter', async () => {
|
it('beforeSaveFile should return file that is already saved and not save anything to files adapter', async () => {
|
||||||
await reconfigureServer({ filesAdapter: mockAdapter });
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
||||||
const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough();
|
const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough();
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const headers = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe_only_db('mongo')('miscellaneous', () => {
|
describe_only_db('mongo')('miscellaneous', () => {
|
||||||
it('test rest_create_app', function(done) {
|
it('test rest_create_app', function (done) {
|
||||||
let appId;
|
let appId;
|
||||||
Parse._request('POST', 'rest_create_app')
|
Parse._request('POST', 'rest_create_app')
|
||||||
.then(res => {
|
.then(res => {
|
||||||
@@ -57,19 +57,19 @@ describe_only_db('mongo')('miscellaneous', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('miscellaneous', function() {
|
describe('miscellaneous', function () {
|
||||||
it('create a GameScore object', function(done) {
|
it('create a GameScore object', function (done) {
|
||||||
const obj = new Parse.Object('GameScore');
|
const obj = new Parse.Object('GameScore');
|
||||||
obj.set('score', 1337);
|
obj.set('score', 1337);
|
||||||
obj.save().then(function(obj) {
|
obj.save().then(function (obj) {
|
||||||
expect(typeof obj.id).toBe('string');
|
expect(typeof obj.id).toBe('string');
|
||||||
expect(typeof obj.createdAt.toGMTString()).toBe('string');
|
expect(typeof obj.createdAt.toGMTString()).toBe('string');
|
||||||
done();
|
done();
|
||||||
}, done.fail);
|
}, done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get a TestObject', function(done) {
|
it('get a TestObject', function (done) {
|
||||||
create({ bloop: 'blarg' }, async function(obj) {
|
create({ bloop: 'blarg' }, async function (obj) {
|
||||||
const t2 = new TestObject({ objectId: obj.id });
|
const t2 = new TestObject({ objectId: obj.id });
|
||||||
const obj2 = await t2.fetch();
|
const obj2 = await t2.fetch();
|
||||||
expect(obj2.get('bloop')).toEqual('blarg');
|
expect(obj2.get('bloop')).toEqual('blarg');
|
||||||
@@ -79,8 +79,8 @@ describe('miscellaneous', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('create a valid parse user', function(done) {
|
it('create a valid parse user', function (done) {
|
||||||
createTestUser().then(function(data) {
|
createTestUser().then(function (data) {
|
||||||
expect(data.id).not.toBeUndefined();
|
expect(data.id).not.toBeUndefined();
|
||||||
expect(data.getSessionToken()).not.toBeUndefined();
|
expect(data.getSessionToken()).not.toBeUndefined();
|
||||||
expect(data.get('password')).toBeUndefined();
|
expect(data.get('password')).toBeUndefined();
|
||||||
@@ -297,8 +297,8 @@ describe('miscellaneous', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('succeed in logging in', function(done) {
|
it('succeed in logging in', function (done) {
|
||||||
createTestUser().then(async function(u) {
|
createTestUser().then(async function (u) {
|
||||||
expect(typeof u.id).toEqual('string');
|
expect(typeof u.id).toEqual('string');
|
||||||
|
|
||||||
const user = await Parse.User.logIn('test', 'moon-y');
|
const user = await Parse.User.logIn('test', 'moon-y');
|
||||||
@@ -310,7 +310,7 @@ describe('miscellaneous', function() {
|
|||||||
}, fail);
|
}, fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('increment with a user object', function(done) {
|
it('increment with a user object', function (done) {
|
||||||
createTestUser()
|
createTestUser()
|
||||||
.then(user => {
|
.then(user => {
|
||||||
user.increment('foo');
|
user.increment('foo');
|
||||||
@@ -338,7 +338,7 @@ describe('miscellaneous', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('save various data types', function(done) {
|
it('save various data types', function (done) {
|
||||||
const obj = new TestObject();
|
const obj = new TestObject();
|
||||||
obj.set('date', new Date());
|
obj.set('date', new Date());
|
||||||
obj.set('array', [1, 2, 3]);
|
obj.set('array', [1, 2, 3]);
|
||||||
@@ -358,7 +358,7 @@ describe('miscellaneous', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('query with limit', function(done) {
|
it('query with limit', function (done) {
|
||||||
const baz = new TestObject({ foo: 'baz' });
|
const baz = new TestObject({ foo: 'baz' });
|
||||||
const qux = new TestObject({ foo: 'qux' });
|
const qux = new TestObject({ foo: 'qux' });
|
||||||
baz
|
baz
|
||||||
@@ -383,7 +383,7 @@ describe('miscellaneous', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('query without limit get default 100 records', function(done) {
|
it('query without limit get default 100 records', function (done) {
|
||||||
const objects = [];
|
const objects = [];
|
||||||
for (let i = 0; i < 150; i++) {
|
for (let i = 0; i < 150; i++) {
|
||||||
objects.push(new TestObject({ name: 'name' + i }));
|
objects.push(new TestObject({ name: 'name' + i }));
|
||||||
@@ -404,7 +404,7 @@ describe('miscellaneous', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('basic saveAll', function(done) {
|
it('basic saveAll', function (done) {
|
||||||
const alpha = new TestObject({ letter: 'alpha' });
|
const alpha = new TestObject({ letter: 'alpha' });
|
||||||
const beta = new TestObject({ letter: 'beta' });
|
const beta = new TestObject({ letter: 'beta' });
|
||||||
Parse.Object.saveAll([alpha, beta])
|
Parse.Object.saveAll([alpha, beta])
|
||||||
@@ -425,26 +425,26 @@ describe('miscellaneous', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('test beforeSave set object acl success', function(done) {
|
it('test beforeSave set object acl success', function (done) {
|
||||||
const acl = new Parse.ACL({
|
const acl = new Parse.ACL({
|
||||||
'*': { read: true, write: false },
|
'*': { read: true, write: false },
|
||||||
});
|
});
|
||||||
Parse.Cloud.beforeSave('BeforeSaveAddACL', function(req) {
|
Parse.Cloud.beforeSave('BeforeSaveAddACL', function (req) {
|
||||||
req.object.setACL(acl);
|
req.object.setACL(acl);
|
||||||
});
|
});
|
||||||
|
|
||||||
const obj = new Parse.Object('BeforeSaveAddACL');
|
const obj = new Parse.Object('BeforeSaveAddACL');
|
||||||
obj.set('lol', true);
|
obj.set('lol', true);
|
||||||
obj.save().then(
|
obj.save().then(
|
||||||
function() {
|
function () {
|
||||||
const query = new Parse.Query('BeforeSaveAddACL');
|
const query = new Parse.Query('BeforeSaveAddACL');
|
||||||
query.get(obj.id).then(
|
query.get(obj.id).then(
|
||||||
function(objAgain) {
|
function (objAgain) {
|
||||||
expect(objAgain.get('lol')).toBeTruthy();
|
expect(objAgain.get('lol')).toBeTruthy();
|
||||||
expect(objAgain.getACL().equals(acl));
|
expect(objAgain.getACL().equals(acl));
|
||||||
done();
|
done();
|
||||||
},
|
},
|
||||||
function(error) {
|
function (error) {
|
||||||
fail(error);
|
fail(error);
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
@@ -667,10 +667,10 @@ describe('miscellaneous', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('test afterSave get full object on create and update', function(done) {
|
it('test afterSave get full object on create and update', function (done) {
|
||||||
let triggerTime = 0;
|
let triggerTime = 0;
|
||||||
// Register a mock beforeSave hook
|
// Register a mock beforeSave hook
|
||||||
Parse.Cloud.afterSave('GameScore', function(req) {
|
Parse.Cloud.afterSave('GameScore', function (req) {
|
||||||
const object = req.object;
|
const object = req.object;
|
||||||
expect(object instanceof Parse.Object).toBeTruthy();
|
expect(object instanceof Parse.Object).toBeTruthy();
|
||||||
expect(object.id).not.toBeUndefined();
|
expect(object.id).not.toBeUndefined();
|
||||||
@@ -694,29 +694,29 @@ describe('miscellaneous', function() {
|
|||||||
obj.set('fooAgain', 'barAgain');
|
obj.set('fooAgain', 'barAgain');
|
||||||
obj
|
obj
|
||||||
.save()
|
.save()
|
||||||
.then(function() {
|
.then(function () {
|
||||||
// We only update foo
|
// We only update foo
|
||||||
obj.set('foo', 'baz');
|
obj.set('foo', 'baz');
|
||||||
return obj.save();
|
return obj.save();
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
function() {
|
function () {
|
||||||
// Make sure the checking has been triggered
|
// Make sure the checking has been triggered
|
||||||
expect(triggerTime).toBe(2);
|
expect(triggerTime).toBe(2);
|
||||||
done();
|
done();
|
||||||
},
|
},
|
||||||
function(error) {
|
function (error) {
|
||||||
fail(error);
|
fail(error);
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('test afterSave get original object on update', function(done) {
|
it('test afterSave get original object on update', function (done) {
|
||||||
let triggerTime = 0;
|
let triggerTime = 0;
|
||||||
// Register a mock beforeSave hook
|
// Register a mock beforeSave hook
|
||||||
|
|
||||||
Parse.Cloud.afterSave('GameScore', function(req) {
|
Parse.Cloud.afterSave('GameScore', function (req) {
|
||||||
const object = req.object;
|
const 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');
|
||||||
@@ -750,18 +750,18 @@ describe('miscellaneous', function() {
|
|||||||
obj.set('fooAgain', 'barAgain');
|
obj.set('fooAgain', 'barAgain');
|
||||||
obj
|
obj
|
||||||
.save()
|
.save()
|
||||||
.then(function() {
|
.then(function () {
|
||||||
// We only update foo
|
// We only update foo
|
||||||
obj.set('foo', 'baz');
|
obj.set('foo', 'baz');
|
||||||
return obj.save();
|
return obj.save();
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
function() {
|
function () {
|
||||||
// Make sure the checking has been triggered
|
// Make sure the checking has been triggered
|
||||||
expect(triggerTime).toBe(2);
|
expect(triggerTime).toBe(2);
|
||||||
done();
|
done();
|
||||||
},
|
},
|
||||||
function(error) {
|
function (error) {
|
||||||
jfail(error);
|
jfail(error);
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
@@ -771,7 +771,7 @@ describe('miscellaneous', function() {
|
|||||||
it('test afterSave get full original object even req auth can not query it', done => {
|
it('test afterSave get full original object even req auth can not query it', done => {
|
||||||
let triggerTime = 0;
|
let triggerTime = 0;
|
||||||
// Register a mock beforeSave hook
|
// Register a mock beforeSave hook
|
||||||
Parse.Cloud.afterSave('GameScore', function(req) {
|
Parse.Cloud.afterSave('GameScore', function (req) {
|
||||||
const object = req.object;
|
const object = req.object;
|
||||||
const originalObject = req.original;
|
const originalObject = req.original;
|
||||||
if (triggerTime == 0) {
|
if (triggerTime == 0) {
|
||||||
@@ -802,18 +802,18 @@ describe('miscellaneous', function() {
|
|||||||
obj.setACL(acl);
|
obj.setACL(acl);
|
||||||
obj
|
obj
|
||||||
.save()
|
.save()
|
||||||
.then(function() {
|
.then(function () {
|
||||||
// We only update foo
|
// We only update foo
|
||||||
obj.set('foo', 'baz');
|
obj.set('foo', 'baz');
|
||||||
return obj.save();
|
return obj.save();
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
function() {
|
function () {
|
||||||
// Make sure the checking has been triggered
|
// Make sure the checking has been triggered
|
||||||
expect(triggerTime).toBe(2);
|
expect(triggerTime).toBe(2);
|
||||||
done();
|
done();
|
||||||
},
|
},
|
||||||
function(error) {
|
function (error) {
|
||||||
jfail(error);
|
jfail(error);
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
@@ -823,7 +823,7 @@ describe('miscellaneous', function() {
|
|||||||
it('afterSave flattens custom operations', done => {
|
it('afterSave flattens custom operations', done => {
|
||||||
let triggerTime = 0;
|
let triggerTime = 0;
|
||||||
// Register a mock beforeSave hook
|
// Register a mock beforeSave hook
|
||||||
Parse.Cloud.afterSave('GameScore', function(req) {
|
Parse.Cloud.afterSave('GameScore', function (req) {
|
||||||
const object = req.object;
|
const object = req.object;
|
||||||
expect(object instanceof Parse.Object).toBeTruthy();
|
expect(object instanceof Parse.Object).toBeTruthy();
|
||||||
const originalObject = req.original;
|
const originalObject = req.original;
|
||||||
@@ -865,7 +865,7 @@ describe('miscellaneous', function() {
|
|||||||
it('beforeSave receives ACL', done => {
|
it('beforeSave receives ACL', done => {
|
||||||
let triggerTime = 0;
|
let triggerTime = 0;
|
||||||
// Register a mock beforeSave hook
|
// Register a mock beforeSave hook
|
||||||
Parse.Cloud.beforeSave('GameScore', function(req) {
|
Parse.Cloud.beforeSave('GameScore', function (req) {
|
||||||
const object = req.object;
|
const object = req.object;
|
||||||
if (triggerTime == 0) {
|
if (triggerTime == 0) {
|
||||||
const acl = object.getACL();
|
const acl = object.getACL();
|
||||||
@@ -909,7 +909,7 @@ describe('miscellaneous', function() {
|
|||||||
it('afterSave receives ACL', done => {
|
it('afterSave receives ACL', done => {
|
||||||
let triggerTime = 0;
|
let triggerTime = 0;
|
||||||
// Register a mock beforeSave hook
|
// Register a mock beforeSave hook
|
||||||
Parse.Cloud.afterSave('GameScore', function(req) {
|
Parse.Cloud.afterSave('GameScore', function (req) {
|
||||||
const object = req.object;
|
const object = req.object;
|
||||||
if (triggerTime == 0) {
|
if (triggerTime == 0) {
|
||||||
const acl = object.getACL();
|
const acl = object.getACL();
|
||||||
@@ -1057,14 +1057,14 @@ describe('miscellaneous', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('test beforeSave/afterSave get installationId', function(done) {
|
it('test beforeSave/afterSave get installationId', function (done) {
|
||||||
let triggerTime = 0;
|
let triggerTime = 0;
|
||||||
Parse.Cloud.beforeSave('GameScore', function(req) {
|
Parse.Cloud.beforeSave('GameScore', function (req) {
|
||||||
triggerTime++;
|
triggerTime++;
|
||||||
expect(triggerTime).toEqual(1);
|
expect(triggerTime).toEqual(1);
|
||||||
expect(req.installationId).toEqual('yolo');
|
expect(req.installationId).toEqual('yolo');
|
||||||
});
|
});
|
||||||
Parse.Cloud.afterSave('GameScore', function(req) {
|
Parse.Cloud.afterSave('GameScore', function (req) {
|
||||||
triggerTime++;
|
triggerTime++;
|
||||||
expect(triggerTime).toEqual(2);
|
expect(triggerTime).toEqual(2);
|
||||||
expect(req.installationId).toEqual('yolo');
|
expect(req.installationId).toEqual('yolo');
|
||||||
@@ -1087,14 +1087,14 @@ describe('miscellaneous', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('test beforeDelete/afterDelete get installationId', function(done) {
|
it('test beforeDelete/afterDelete get installationId', function (done) {
|
||||||
let triggerTime = 0;
|
let triggerTime = 0;
|
||||||
Parse.Cloud.beforeDelete('GameScore', function(req) {
|
Parse.Cloud.beforeDelete('GameScore', function (req) {
|
||||||
triggerTime++;
|
triggerTime++;
|
||||||
expect(triggerTime).toEqual(1);
|
expect(triggerTime).toEqual(1);
|
||||||
expect(req.installationId).toEqual('yolo');
|
expect(req.installationId).toEqual('yolo');
|
||||||
});
|
});
|
||||||
Parse.Cloud.afterDelete('GameScore', function(req) {
|
Parse.Cloud.afterDelete('GameScore', function (req) {
|
||||||
triggerTime++;
|
triggerTime++;
|
||||||
expect(triggerTime).toEqual(2);
|
expect(triggerTime).toEqual(2);
|
||||||
expect(req.installationId).toEqual('yolo');
|
expect(req.installationId).toEqual('yolo');
|
||||||
@@ -1170,33 +1170,6 @@ describe('miscellaneous', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('test cloud function parameter validation', done => {
|
|
||||||
// Register a function with validation
|
|
||||||
Parse.Cloud.define(
|
|
||||||
'functionWithParameterValidationFailure',
|
|
||||||
() => {
|
|
||||||
return 'noway';
|
|
||||||
},
|
|
||||||
request => {
|
|
||||||
return request.params.success === 100;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
Parse.Cloud.run('functionWithParameterValidationFailure', {
|
|
||||||
success: 500,
|
|
||||||
}).then(
|
|
||||||
() => {
|
|
||||||
fail('Validation should not have succeeded');
|
|
||||||
done();
|
|
||||||
},
|
|
||||||
e => {
|
|
||||||
expect(e.code).toEqual(142);
|
|
||||||
expect(e.message).toEqual('Validation failed.');
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can handle null params in cloud functions (regression test for #1742)', done => {
|
it('can handle null params in cloud functions (regression test for #1742)', done => {
|
||||||
Parse.Cloud.define('func', request => {
|
Parse.Cloud.define('func', request => {
|
||||||
expect(request.params.nullParam).toEqual(null);
|
expect(request.params.nullParam).toEqual(null);
|
||||||
@@ -1715,10 +1688,7 @@ describe('miscellaneous', function() {
|
|||||||
|
|
||||||
it('purge empty class', done => {
|
it('purge empty class', done => {
|
||||||
const testSchema = new Parse.Schema('UnknownClass');
|
const testSchema = new Parse.Schema('UnknownClass');
|
||||||
testSchema
|
testSchema.purge().then(done).catch(done.fail);
|
||||||
.purge()
|
|
||||||
.then(done)
|
|
||||||
.catch(done.fail);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not update schema beforeSave #2672', done => {
|
it('should not update schema beforeSave #2672', done => {
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
const UserController = require('../lib/Controllers/UserController')
|
const UserController = require('../lib/Controllers/UserController')
|
||||||
.UserController;
|
.UserController;
|
||||||
const Config = require('../lib/Config');
|
const Config = require('../lib/Config');
|
||||||
|
const validatorFail = () => {
|
||||||
|
throw 'you are not authorized';
|
||||||
|
};
|
||||||
|
|
||||||
describe('ParseLiveQuery', function () {
|
describe('ParseLiveQuery', function () {
|
||||||
it('can subscribe to query', async done => {
|
it('can subscribe to query', async done => {
|
||||||
await reconfigureServer({
|
await reconfigureServer({
|
||||||
@@ -231,6 +235,7 @@ describe('ParseLiveQuery', function () {
|
|||||||
object.set({ foo: 'bar' });
|
object.set({ foo: 'bar' });
|
||||||
await object.save();
|
await object.save();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can handle afterEvent throw', async done => {
|
it('can handle afterEvent throw', async done => {
|
||||||
await reconfigureServer({
|
await reconfigureServer({
|
||||||
liveQuery: {
|
liveQuery: {
|
||||||
@@ -300,6 +305,7 @@ describe('ParseLiveQuery', function () {
|
|||||||
object.set({ foo: 'bar' });
|
object.set({ foo: 'bar' });
|
||||||
await object.save();
|
await object.save();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('expect afterEvent create', async done => {
|
it('expect afterEvent create', async done => {
|
||||||
await reconfigureServer({
|
await reconfigureServer({
|
||||||
liveQuery: {
|
liveQuery: {
|
||||||
@@ -551,6 +557,79 @@ describe('ParseLiveQuery', function () {
|
|||||||
await object.save();
|
await object.save();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can handle beforeConnect validation function', async done => {
|
||||||
|
await reconfigureServer({
|
||||||
|
liveQuery: {
|
||||||
|
classNames: ['TestObject'],
|
||||||
|
},
|
||||||
|
startLiveQueryServer: true,
|
||||||
|
verbose: false,
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const object = new TestObject();
|
||||||
|
await object.save();
|
||||||
|
|
||||||
|
Parse.Cloud.beforeConnect(() => {}, validatorFail);
|
||||||
|
let complete = false;
|
||||||
|
Parse.LiveQuery.on('error', error => {
|
||||||
|
if (complete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
complete = true;
|
||||||
|
expect(error).toBe('you are not authorized');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
const query = new Parse.Query(TestObject);
|
||||||
|
query.equalTo('objectId', object.id);
|
||||||
|
await query.subscribe();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can handle beforeSubscribe validation function', async done => {
|
||||||
|
await reconfigureServer({
|
||||||
|
liveQuery: {
|
||||||
|
classNames: ['TestObject'],
|
||||||
|
},
|
||||||
|
startLiveQueryServer: true,
|
||||||
|
verbose: false,
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
const object = new TestObject();
|
||||||
|
await object.save();
|
||||||
|
|
||||||
|
Parse.Cloud.beforeSubscribe(TestObject, () => {}, validatorFail);
|
||||||
|
const query = new Parse.Query(TestObject);
|
||||||
|
query.equalTo('objectId', object.id);
|
||||||
|
const subscription = await query.subscribe();
|
||||||
|
subscription.on('error', error => {
|
||||||
|
expect(error).toBe('you are not authorized');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can handle afterEvent validation function', async done => {
|
||||||
|
await reconfigureServer({
|
||||||
|
liveQuery: {
|
||||||
|
classNames: ['TestObject'],
|
||||||
|
},
|
||||||
|
startLiveQueryServer: true,
|
||||||
|
verbose: false,
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
Parse.Cloud.afterLiveQueryEvent('TestObject', () => {}, validatorFail);
|
||||||
|
|
||||||
|
const query = new Parse.Query(TestObject);
|
||||||
|
const subscription = await query.subscribe();
|
||||||
|
subscription.on('error', error => {
|
||||||
|
expect(error).toBe('you are not authorized');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
const object = new TestObject();
|
||||||
|
object.set('foo', 'bar');
|
||||||
|
await object.save();
|
||||||
|
});
|
||||||
|
|
||||||
it('can handle beforeConnect error', async done => {
|
it('can handle beforeConnect error', async done => {
|
||||||
await reconfigureServer({
|
await reconfigureServer({
|
||||||
liveQuery: {
|
liveQuery: {
|
||||||
|
|||||||
@@ -33,15 +33,6 @@ const addFileDataIfNeeded = async file => {
|
|||||||
return file;
|
return file;
|
||||||
};
|
};
|
||||||
|
|
||||||
const errorMessageFromError = e => {
|
|
||||||
if (typeof e === 'string') {
|
|
||||||
return e;
|
|
||||||
} else if (e && e.message) {
|
|
||||||
return e.message;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class FilesRouter {
|
export class FilesRouter {
|
||||||
expressRouter({ maxUploadSize = '20Mb' } = {}) {
|
expressRouter({ maxUploadSize = '20Mb' } = {}) {
|
||||||
var router = express.Router();
|
var router = express.Router();
|
||||||
@@ -192,10 +183,11 @@ export class FilesRouter {
|
|||||||
res.json(saveResult);
|
res.json(saveResult);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Error creating a file: ', e);
|
logger.error('Error creating a file: ', e);
|
||||||
const errorMessage =
|
const error = triggers.resolveError(e, {
|
||||||
errorMessageFromError(e) ||
|
code: Parse.Error.FILE_SAVE_ERROR,
|
||||||
`Could not store file: ${fileObject.file._name}.`;
|
message: `Could not store file: ${fileObject.file._name}.`,
|
||||||
next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR, errorMessage));
|
});
|
||||||
|
next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,8 +219,11 @@ export class FilesRouter {
|
|||||||
res.end();
|
res.end();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Error deleting a file: ', e);
|
logger.error('Error deleting a file: ', e);
|
||||||
const errorMessage = errorMessageFromError(e) || `Could not delete file.`;
|
const error = triggers.resolveError(e, {
|
||||||
next(new Parse.Error(Parse.Error.FILE_DELETE_ERROR, errorMessage));
|
code: Parse.Error.FILE_DELETE_ERROR,
|
||||||
|
message: 'Could not delete file.',
|
||||||
|
});
|
||||||
|
next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,37 +109,17 @@ export class FunctionsRouter extends PromiseRouter {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
error: function (message) {
|
error: function (message) {
|
||||||
// parse error, process away
|
const error = triggers.resolveError(message);
|
||||||
if (message instanceof Parse.Error) {
|
|
||||||
return reject(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
const code = Parse.Error.SCRIPT_FAILED;
|
|
||||||
// If it's an error, mark it as a script failed
|
|
||||||
if (typeof message === 'string') {
|
|
||||||
return reject(new Parse.Error(code, message));
|
|
||||||
}
|
|
||||||
const error = new Parse.Error(
|
|
||||||
code,
|
|
||||||
(message && message.message) || message
|
|
||||||
);
|
|
||||||
if (message instanceof Error) {
|
|
||||||
error.stack = message.stack;
|
|
||||||
}
|
|
||||||
reject(error);
|
reject(error);
|
||||||
},
|
},
|
||||||
message: message,
|
message: message,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static handleCloudFunction(req) {
|
static handleCloudFunction(req) {
|
||||||
const functionName = req.params.functionName;
|
const functionName = req.params.functionName;
|
||||||
const applicationId = req.config.applicationId;
|
const applicationId = req.config.applicationId;
|
||||||
const theFunction = triggers.getFunction(functionName, applicationId);
|
const theFunction = triggers.getFunction(functionName, applicationId);
|
||||||
const theValidator = triggers.getValidator(
|
|
||||||
req.params.functionName,
|
|
||||||
applicationId
|
|
||||||
);
|
|
||||||
if (!theFunction) {
|
if (!theFunction) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.SCRIPT_FAILED,
|
Parse.Error.SCRIPT_FAILED,
|
||||||
@@ -160,16 +140,6 @@ export class FunctionsRouter extends PromiseRouter {
|
|||||||
context: req.info.context,
|
context: req.info.context,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (theValidator && typeof theValidator === 'function') {
|
|
||||||
var result = theValidator(request);
|
|
||||||
if (!result) {
|
|
||||||
throw new Parse.Error(
|
|
||||||
Parse.Error.VALIDATION_ERROR,
|
|
||||||
'Validation failed.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
const userString =
|
const userString =
|
||||||
req.auth && req.auth.user ? req.auth.user.id : undefined;
|
req.auth && req.auth.user ? req.auth.user.id : undefined;
|
||||||
@@ -212,6 +182,9 @@ export class FunctionsRouter extends PromiseRouter {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
.then(() => {
|
||||||
|
return triggers.maybeRunValidator(request, functionName);
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return theFunction(request, { message });
|
return theFunction(request, { message });
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -32,11 +32,24 @@ var ParseCloud = {};
|
|||||||
* Defines a Cloud Function.
|
* Defines a Cloud Function.
|
||||||
*
|
*
|
||||||
* **Available in Cloud Code only.**
|
* **Available in Cloud Code only.**
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* Parse.Cloud.define('functionName', (request) => {
|
||||||
|
* // code here
|
||||||
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* Parse.Cloud.define('functionName', (request) => {
|
||||||
|
* // code here
|
||||||
|
* }, { ...validationObject });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
* @static
|
* @static
|
||||||
* @memberof Parse.Cloud
|
* @memberof Parse.Cloud
|
||||||
* @param {String} name The name of the Cloud Function
|
* @param {String} name The name of the Cloud Function
|
||||||
* @param {Function} data The Cloud Function to register. This function can be an async function and should take one parameter a {@link Parse.Cloud.FunctionRequest}.
|
* @param {Function} data The Cloud Function to register. This function can be an async function and should take one parameter a {@link Parse.Cloud.FunctionRequest}.
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FunctionRequest}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.define = function (functionName, handler, validationHandler) {
|
ParseCloud.define = function (functionName, handler, validationHandler) {
|
||||||
triggers.addFunction(
|
triggers.addFunction(
|
||||||
@@ -73,25 +86,29 @@ ParseCloud.job = function (functionName, handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.beforeSave('MyCustomClass', (request) => {
|
* Parse.Cloud.beforeSave('MyCustomClass', (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
*
|
*
|
||||||
* Parse.Cloud.beforeSave(Parse.User, (request) => {
|
* Parse.Cloud.beforeSave(Parse.User, (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, { ...validationObject })
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @method beforeSave
|
* @method beforeSave
|
||||||
* @name Parse.Cloud.beforeSave
|
* @name Parse.Cloud.beforeSave
|
||||||
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after save function for. This can instead be a String that is the className of the subclass.
|
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after save function for. This can instead be a String that is the className of the subclass.
|
||||||
* @param {Function} func The function to run before a save. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest};
|
* @param {Function} func The function to run before a save. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest};
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.TriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.beforeSave = function (parseClass, handler) {
|
ParseCloud.beforeSave = function (parseClass, handler, validationHandler) {
|
||||||
var className = getClassName(parseClass);
|
var className = getClassName(parseClass);
|
||||||
triggers.addTrigger(
|
triggers.addTrigger(
|
||||||
triggers.Types.beforeSave,
|
triggers.Types.beforeSave,
|
||||||
className,
|
className,
|
||||||
handler,
|
handler,
|
||||||
Parse.applicationId
|
Parse.applicationId,
|
||||||
|
validationHandler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -104,25 +121,29 @@ ParseCloud.beforeSave = function (parseClass, handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.beforeDelete('MyCustomClass', (request) => {
|
* Parse.Cloud.beforeDelete('MyCustomClass', (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
*
|
*
|
||||||
* Parse.Cloud.beforeDelete(Parse.User, (request) => {
|
* Parse.Cloud.beforeDelete(Parse.User, (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, { ...validationObject })
|
||||||
*```
|
*```
|
||||||
*
|
*
|
||||||
* @method beforeDelete
|
* @method beforeDelete
|
||||||
* @name Parse.Cloud.beforeDelete
|
* @name Parse.Cloud.beforeDelete
|
||||||
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the before delete function for. This can instead be a String that is the className of the subclass.
|
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the before delete function for. This can instead be a String that is the className of the subclass.
|
||||||
* @param {Function} func The function to run before a delete. This function can be async and should take one parameter, a {@link Parse.Cloud.TriggerRequest}.
|
* @param {Function} func The function to run before a delete. This function can be async and should take one parameter, a {@link Parse.Cloud.TriggerRequest}.
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.TriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.beforeDelete = function (parseClass, handler) {
|
ParseCloud.beforeDelete = function (parseClass, handler, validationHandler) {
|
||||||
var className = getClassName(parseClass);
|
var className = getClassName(parseClass);
|
||||||
triggers.addTrigger(
|
triggers.addTrigger(
|
||||||
triggers.Types.beforeDelete,
|
triggers.Types.beforeDelete,
|
||||||
className,
|
className,
|
||||||
handler,
|
handler,
|
||||||
Parse.applicationId
|
Parse.applicationId,
|
||||||
|
validationHandler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -177,8 +198,7 @@ ParseCloud.beforeLogin = function (handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.afterLogin((request) => {
|
* Parse.Cloud.afterLogin((request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* });
|
||||||
*
|
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @method afterLogin
|
* @method afterLogin
|
||||||
@@ -212,8 +232,7 @@ ParseCloud.afterLogin = function (handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.afterLogout((request) => {
|
* Parse.Cloud.afterLogout((request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* });
|
||||||
*
|
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @method afterLogout
|
* @method afterLogout
|
||||||
@@ -246,25 +265,29 @@ ParseCloud.afterLogout = function (handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.afterSave('MyCustomClass', async function(request) {
|
* Parse.Cloud.afterSave('MyCustomClass', async function(request) {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
*
|
*
|
||||||
* Parse.Cloud.afterSave(Parse.User, async function(request) {
|
* Parse.Cloud.afterSave(Parse.User, async function(request) {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, { ...validationObject });
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @method afterSave
|
* @method afterSave
|
||||||
* @name Parse.Cloud.afterSave
|
* @name Parse.Cloud.afterSave
|
||||||
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after save function for. This can instead be a String that is the className of the subclass.
|
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after save function for. This can instead be a String that is the className of the subclass.
|
||||||
* @param {Function} func The function to run after a save. This function can be an async function and should take just one parameter, {@link Parse.Cloud.TriggerRequest}.
|
* @param {Function} func The function to run after a save. This function can be an async function and should take just one parameter, {@link Parse.Cloud.TriggerRequest}.
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.TriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.afterSave = function (parseClass, handler) {
|
ParseCloud.afterSave = function (parseClass, handler, validationHandler) {
|
||||||
var className = getClassName(parseClass);
|
var className = getClassName(parseClass);
|
||||||
triggers.addTrigger(
|
triggers.addTrigger(
|
||||||
triggers.Types.afterSave,
|
triggers.Types.afterSave,
|
||||||
className,
|
className,
|
||||||
handler,
|
handler,
|
||||||
Parse.applicationId
|
Parse.applicationId,
|
||||||
|
validationHandler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -277,25 +300,29 @@ ParseCloud.afterSave = function (parseClass, handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.afterDelete('MyCustomClass', async (request) => {
|
* Parse.Cloud.afterDelete('MyCustomClass', async (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
*
|
*
|
||||||
* Parse.Cloud.afterDelete(Parse.User, async (request) => {
|
* Parse.Cloud.afterDelete(Parse.User, async (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, { ...validationObject });
|
||||||
*```
|
*```
|
||||||
*
|
*
|
||||||
* @method afterDelete
|
* @method afterDelete
|
||||||
* @name Parse.Cloud.afterDelete
|
* @name Parse.Cloud.afterDelete
|
||||||
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after delete function for. This can instead be a String that is the className of the subclass.
|
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after delete function for. This can instead be a String that is the className of the subclass.
|
||||||
* @param {Function} func The function to run after a delete. This function can be async and should take just one parameter, {@link Parse.Cloud.TriggerRequest}.
|
* @param {Function} func The function to run after a delete. This function can be async and should take just one parameter, {@link Parse.Cloud.TriggerRequest}.
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.TriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.afterDelete = function (parseClass, handler) {
|
ParseCloud.afterDelete = function (parseClass, handler, validationHandler) {
|
||||||
var className = getClassName(parseClass);
|
var className = getClassName(parseClass);
|
||||||
triggers.addTrigger(
|
triggers.addTrigger(
|
||||||
triggers.Types.afterDelete,
|
triggers.Types.afterDelete,
|
||||||
className,
|
className,
|
||||||
handler,
|
handler,
|
||||||
Parse.applicationId
|
Parse.applicationId,
|
||||||
|
validationHandler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -308,25 +335,29 @@ ParseCloud.afterDelete = function (parseClass, handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.beforeFind('MyCustomClass', async (request) => {
|
* Parse.Cloud.beforeFind('MyCustomClass', async (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
*
|
*
|
||||||
* Parse.Cloud.beforeFind(Parse.User, async (request) => {
|
* Parse.Cloud.beforeFind(Parse.User, async (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, { ...validationObject });
|
||||||
*```
|
*```
|
||||||
*
|
*
|
||||||
* @method beforeFind
|
* @method beforeFind
|
||||||
* @name Parse.Cloud.beforeFind
|
* @name Parse.Cloud.beforeFind
|
||||||
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the before find function for. This can instead be a String that is the className of the subclass.
|
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the before find function for. This can instead be a String that is the className of the subclass.
|
||||||
* @param {Function} func The function to run before a find. This function can be async and should take just one parameter, {@link Parse.Cloud.BeforeFindRequest}.
|
* @param {Function} func The function to run before a find. This function can be async and should take just one parameter, {@link Parse.Cloud.BeforeFindRequest}.
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.BeforeFindRequest}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.beforeFind = function (parseClass, handler) {
|
ParseCloud.beforeFind = function (parseClass, handler, validationHandler) {
|
||||||
var className = getClassName(parseClass);
|
var className = getClassName(parseClass);
|
||||||
triggers.addTrigger(
|
triggers.addTrigger(
|
||||||
triggers.Types.beforeFind,
|
triggers.Types.beforeFind,
|
||||||
className,
|
className,
|
||||||
handler,
|
handler,
|
||||||
Parse.applicationId
|
Parse.applicationId,
|
||||||
|
validationHandler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -339,25 +370,29 @@ ParseCloud.beforeFind = function (parseClass, handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.afterFind('MyCustomClass', async (request) => {
|
* Parse.Cloud.afterFind('MyCustomClass', async (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
*
|
*
|
||||||
* Parse.Cloud.afterFind(Parse.User, async (request) => {
|
* Parse.Cloud.afterFind(Parse.User, async (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, { ...validationObject });
|
||||||
*```
|
*```
|
||||||
*
|
*
|
||||||
* @method afterFind
|
* @method afterFind
|
||||||
* @name Parse.Cloud.afterFind
|
* @name Parse.Cloud.afterFind
|
||||||
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after find function for. This can instead be a String that is the className of the subclass.
|
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after find function for. This can instead be a String that is the className of the subclass.
|
||||||
* @param {Function} func The function to run before a find. This function can be async and should take just one parameter, {@link Parse.Cloud.AfterFindRequest}.
|
* @param {Function} func The function to run before a find. This function can be async and should take just one parameter, {@link Parse.Cloud.AfterFindRequest}.
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.AfterFindRequest}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.afterFind = function (parseClass, handler) {
|
ParseCloud.afterFind = function (parseClass, handler, validationHandler) {
|
||||||
const className = getClassName(parseClass);
|
const className = getClassName(parseClass);
|
||||||
triggers.addTrigger(
|
triggers.addTrigger(
|
||||||
triggers.Types.afterFind,
|
triggers.Types.afterFind,
|
||||||
className,
|
className,
|
||||||
handler,
|
handler,
|
||||||
Parse.applicationId
|
Parse.applicationId,
|
||||||
|
validationHandler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -369,18 +404,26 @@ ParseCloud.afterFind = function (parseClass, handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.beforeSaveFile(async (request) => {
|
* Parse.Cloud.beforeSaveFile(async (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* Parse.Cloud.beforeSaveFile(async (request) => {
|
||||||
|
* // code here
|
||||||
|
* }, { ...validationObject });
|
||||||
*```
|
*```
|
||||||
*
|
*
|
||||||
* @method beforeSaveFile
|
* @method beforeSaveFile
|
||||||
* @name Parse.Cloud.beforeSaveFile
|
* @name Parse.Cloud.beforeSaveFile
|
||||||
* @param {Function} func The function to run before saving a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}.
|
* @param {Function} func The function to run before saving a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}.
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.beforeSaveFile = function (handler) {
|
ParseCloud.beforeSaveFile = function (handler, validationHandler) {
|
||||||
triggers.addFileTrigger(
|
triggers.addFileTrigger(
|
||||||
triggers.Types.beforeSaveFile,
|
triggers.Types.beforeSaveFile,
|
||||||
handler,
|
handler,
|
||||||
Parse.applicationId
|
Parse.applicationId,
|
||||||
|
validationHandler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -392,18 +435,26 @@ ParseCloud.beforeSaveFile = function (handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.afterSaveFile(async (request) => {
|
* Parse.Cloud.afterSaveFile(async (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* Parse.Cloud.afterSaveFile(async (request) => {
|
||||||
|
* // code here
|
||||||
|
* }, { ...validationObject });
|
||||||
*```
|
*```
|
||||||
*
|
*
|
||||||
* @method afterSaveFile
|
* @method afterSaveFile
|
||||||
* @name Parse.Cloud.afterSaveFile
|
* @name Parse.Cloud.afterSaveFile
|
||||||
* @param {Function} func The function to run after saving a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}.
|
* @param {Function} func The function to run after saving a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}.
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.afterSaveFile = function (handler) {
|
ParseCloud.afterSaveFile = function (handler, validationHandler) {
|
||||||
triggers.addFileTrigger(
|
triggers.addFileTrigger(
|
||||||
triggers.Types.afterSaveFile,
|
triggers.Types.afterSaveFile,
|
||||||
handler,
|
handler,
|
||||||
Parse.applicationId
|
Parse.applicationId,
|
||||||
|
validationHandler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -415,18 +466,26 @@ ParseCloud.afterSaveFile = function (handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.beforeDeleteFile(async (request) => {
|
* Parse.Cloud.beforeDeleteFile(async (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* Parse.Cloud.beforeDeleteFile(async (request) => {
|
||||||
|
* // code here
|
||||||
|
* }, { ...validationObject });
|
||||||
*```
|
*```
|
||||||
*
|
*
|
||||||
* @method beforeDeleteFile
|
* @method beforeDeleteFile
|
||||||
* @name Parse.Cloud.beforeDeleteFile
|
* @name Parse.Cloud.beforeDeleteFile
|
||||||
* @param {Function} func The function to run before deleting a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}.
|
* @param {Function} func The function to run before deleting a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}.
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.beforeDeleteFile = function (handler) {
|
ParseCloud.beforeDeleteFile = function (handler, validationHandler) {
|
||||||
triggers.addFileTrigger(
|
triggers.addFileTrigger(
|
||||||
triggers.Types.beforeDeleteFile,
|
triggers.Types.beforeDeleteFile,
|
||||||
handler,
|
handler,
|
||||||
Parse.applicationId
|
Parse.applicationId,
|
||||||
|
validationHandler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -438,18 +497,26 @@ ParseCloud.beforeDeleteFile = function (handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.afterDeleteFile(async (request) => {
|
* Parse.Cloud.afterDeleteFile(async (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* Parse.Cloud.afterDeleteFile(async (request) => {
|
||||||
|
* // code here
|
||||||
|
* }, { ...validationObject });
|
||||||
*```
|
*```
|
||||||
*
|
*
|
||||||
* @method afterDeleteFile
|
* @method afterDeleteFile
|
||||||
* @name Parse.Cloud.afterDeleteFile
|
* @name Parse.Cloud.afterDeleteFile
|
||||||
* @param {Function} func The function to after before deleting a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}.
|
* @param {Function} func The function to after before deleting a file. This function can be async and should take just one parameter, {@link Parse.Cloud.FileTriggerRequest}.
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.afterDeleteFile = function (handler) {
|
ParseCloud.afterDeleteFile = function (handler, validationHandler) {
|
||||||
triggers.addFileTrigger(
|
triggers.addFileTrigger(
|
||||||
triggers.Types.afterDeleteFile,
|
triggers.Types.afterDeleteFile,
|
||||||
handler,
|
handler,
|
||||||
Parse.applicationId
|
Parse.applicationId,
|
||||||
|
validationHandler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -461,18 +528,26 @@ ParseCloud.afterDeleteFile = function (handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.beforeConnect(async (request) => {
|
* Parse.Cloud.beforeConnect(async (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* Parse.Cloud.beforeConnect(async (request) => {
|
||||||
|
* // code here
|
||||||
|
* }, { ...validationObject });
|
||||||
*```
|
*```
|
||||||
*
|
*
|
||||||
* @method beforeConnect
|
* @method beforeConnect
|
||||||
* @name Parse.Cloud.beforeConnect
|
* @name Parse.Cloud.beforeConnect
|
||||||
* @param {Function} func The function to before connection is made. This function can be async and should take just one parameter, {@link Parse.Cloud.ConnectTriggerRequest}.
|
* @param {Function} func The function to before connection is made. This function can be async and should take just one parameter, {@link Parse.Cloud.ConnectTriggerRequest}.
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.ConnectTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.beforeConnect = function (handler) {
|
ParseCloud.beforeConnect = function (handler, validationHandler) {
|
||||||
triggers.addConnectTrigger(
|
triggers.addConnectTrigger(
|
||||||
triggers.Types.beforeConnect,
|
triggers.Types.beforeConnect,
|
||||||
handler,
|
handler,
|
||||||
Parse.applicationId
|
Parse.applicationId,
|
||||||
|
validationHandler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -485,25 +560,29 @@ ParseCloud.beforeConnect = function (handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.beforeSubscribe('MyCustomClass', (request) => {
|
* Parse.Cloud.beforeSubscribe('MyCustomClass', (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
*
|
*
|
||||||
* Parse.Cloud.beforeSubscribe(Parse.User, (request) => {
|
* Parse.Cloud.beforeSubscribe(Parse.User, (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, { ...validationObject });
|
||||||
*```
|
*```
|
||||||
*
|
*
|
||||||
* @method beforeSubscribe
|
* @method beforeSubscribe
|
||||||
* @name Parse.Cloud.beforeSubscribe
|
* @name Parse.Cloud.beforeSubscribe
|
||||||
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the before subscription function for. This can instead be a String that is the className of the subclass.
|
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the before subscription function for. This can instead be a String that is the className of the subclass.
|
||||||
* @param {Function} func The function to run before a subscription. This function can be async and should take one parameter, a {@link Parse.Cloud.TriggerRequest}.
|
* @param {Function} func The function to run before a subscription. This function can be async and should take one parameter, a {@link Parse.Cloud.TriggerRequest}.
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.TriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.beforeSubscribe = function (parseClass, handler) {
|
ParseCloud.beforeSubscribe = function (parseClass, handler, validationHandler) {
|
||||||
var className = getClassName(parseClass);
|
var className = getClassName(parseClass);
|
||||||
triggers.addTrigger(
|
triggers.addTrigger(
|
||||||
triggers.Types.beforeSubscribe,
|
triggers.Types.beforeSubscribe,
|
||||||
className,
|
className,
|
||||||
handler,
|
handler,
|
||||||
Parse.applicationId
|
Parse.applicationId,
|
||||||
|
validationHandler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -519,21 +598,33 @@ ParseCloud.onLiveQueryEvent = function (handler) {
|
|||||||
* ```
|
* ```
|
||||||
* Parse.Cloud.afterLiveQueryEvent('MyCustomClass', (request) => {
|
* Parse.Cloud.afterLiveQueryEvent('MyCustomClass', (request) => {
|
||||||
* // code here
|
* // code here
|
||||||
* })
|
* }, (request) => {
|
||||||
|
* // validation code here
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* Parse.Cloud.afterLiveQueryEvent('MyCustomClass', (request) => {
|
||||||
|
* // code here
|
||||||
|
* }, { ...validationObject });
|
||||||
*```
|
*```
|
||||||
*
|
*
|
||||||
* @method afterLiveQueryEvent
|
* @method afterLiveQueryEvent
|
||||||
* @name Parse.Cloud.afterLiveQueryEvent
|
* @name Parse.Cloud.afterLiveQueryEvent
|
||||||
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after live query event function for. This can instead be a String that is the className of the subclass.
|
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after live query event function for. This can instead be a String that is the className of the subclass.
|
||||||
* @param {Function} func The function to run after a live query event. This function can be async and should take one parameter, a {@link Parse.Cloud.LiveQueryEventTrigger}.
|
* @param {Function} func The function to run after a live query event. This function can be async and should take one parameter, a {@link Parse.Cloud.LiveQueryEventTrigger}.
|
||||||
|
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.LiveQueryEventTrigger}, or a {@link Parse.Cloud.ValidatorObject}.
|
||||||
*/
|
*/
|
||||||
ParseCloud.afterLiveQueryEvent = function (parseClass, handler) {
|
ParseCloud.afterLiveQueryEvent = function (
|
||||||
|
parseClass,
|
||||||
|
handler,
|
||||||
|
validationHandler
|
||||||
|
) {
|
||||||
const className = getClassName(parseClass);
|
const className = getClassName(parseClass);
|
||||||
triggers.addTrigger(
|
triggers.addTrigger(
|
||||||
triggers.Types.afterEvent,
|
triggers.Types.afterEvent,
|
||||||
className,
|
className,
|
||||||
handler,
|
handler,
|
||||||
Parse.applicationId
|
Parse.applicationId,
|
||||||
|
validationHandler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -642,3 +733,23 @@ module.exports = ParseCloud;
|
|||||||
* @property {Object} params The params passed to the background job.
|
* @property {Object} params The params passed to the background job.
|
||||||
* @property {function} message If message is called with a string argument, will update the current message to be stored in the job status.
|
* @property {function} message If message is called with a string argument, will update the current message to be stored in the job status.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @interface Parse.Cloud.ValidatorObject
|
||||||
|
* @property {Boolean} requireUser whether the cloud trigger requires a user.
|
||||||
|
* @property {Boolean} requireMaster whether the cloud trigger requires a master key.
|
||||||
|
* @property {Boolean} validateMasterKey whether the validator should run if masterKey is provided. Defaults to false.
|
||||||
|
*
|
||||||
|
* @property {Array<String>|Object} requireUserKeys If set, keys required on request.user to make the request.
|
||||||
|
* @property {String} requireUserKeys.field If requireUserKeys is an object, name of field to validate on request user
|
||||||
|
* @property {Array|function|Any} requireUserKeys.field.options array of options that the field can be, function to validate field, or single value. Throw an error if value is invalid.
|
||||||
|
* @property {String} requireUserKeys.field.error custom error message if field is invalid.
|
||||||
|
*
|
||||||
|
* @property {Object|Array<String>} fields if an array of strings, validator will look for keys in request.params, and throw if not provided. If Object, fields to validate. If the trigger is a cloud function, `request.params` will be validated, otherwise `request.object`.
|
||||||
|
* @property {String} fields.field name of field to validate.
|
||||||
|
* @property {String} fields.field.type expected type of data for field.
|
||||||
|
* @property {Boolean} fields.field.constant whether the field can be modified on the object.
|
||||||
|
* @property {Any} fields.field.default default value if field is `null`, or initial value `constant` is `true`.
|
||||||
|
* @property {Array|function|Any} fields.field.options array of options that the field can be, function to validate field, or single value. Throw an error if value is invalid.
|
||||||
|
* @property {String} fields.field.error custom error message if field is invalid.
|
||||||
|
*/
|
||||||
|
|||||||
239
src/triggers.js
239
src/triggers.js
@@ -25,7 +25,10 @@ const FileClassName = '@File';
|
|||||||
const ConnectClassName = '@Connect';
|
const ConnectClassName = '@Connect';
|
||||||
|
|
||||||
const baseStore = function () {
|
const baseStore = function () {
|
||||||
const Validators = {};
|
const Validators = Object.keys(Types).reduce(function (base, key) {
|
||||||
|
base[key] = {};
|
||||||
|
return base;
|
||||||
|
}, {});
|
||||||
const Functions = {};
|
const Functions = {};
|
||||||
const Jobs = {};
|
const Jobs = {};
|
||||||
const LiveQuery = [];
|
const LiveQuery = [];
|
||||||
@@ -132,17 +135,51 @@ export function addJob(jobName, handler, applicationId) {
|
|||||||
add(Category.Jobs, jobName, handler, applicationId);
|
add(Category.Jobs, jobName, handler, applicationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addTrigger(type, className, handler, applicationId) {
|
export function addTrigger(
|
||||||
|
type,
|
||||||
|
className,
|
||||||
|
handler,
|
||||||
|
applicationId,
|
||||||
|
validationHandler
|
||||||
|
) {
|
||||||
validateClassNameForTriggers(className, type);
|
validateClassNameForTriggers(className, type);
|
||||||
add(Category.Triggers, `${type}.${className}`, handler, applicationId);
|
add(Category.Triggers, `${type}.${className}`, handler, applicationId);
|
||||||
|
add(
|
||||||
|
Category.Validators,
|
||||||
|
`${type}.${className}`,
|
||||||
|
validationHandler,
|
||||||
|
applicationId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addFileTrigger(type, handler, applicationId) {
|
export function addFileTrigger(
|
||||||
|
type,
|
||||||
|
handler,
|
||||||
|
applicationId,
|
||||||
|
validationHandler
|
||||||
|
) {
|
||||||
add(Category.Triggers, `${type}.${FileClassName}`, handler, applicationId);
|
add(Category.Triggers, `${type}.${FileClassName}`, handler, applicationId);
|
||||||
|
add(
|
||||||
|
Category.Validators,
|
||||||
|
`${type}.${FileClassName}`,
|
||||||
|
validationHandler,
|
||||||
|
applicationId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addConnectTrigger(type, handler, applicationId) {
|
export function addConnectTrigger(
|
||||||
|
type,
|
||||||
|
handler,
|
||||||
|
applicationId,
|
||||||
|
validationHandler
|
||||||
|
) {
|
||||||
add(Category.Triggers, `${type}.${ConnectClassName}`, handler, applicationId);
|
add(Category.Triggers, `${type}.${ConnectClassName}`, handler, applicationId);
|
||||||
|
add(
|
||||||
|
Category.Validators,
|
||||||
|
`${type}.${ConnectClassName}`,
|
||||||
|
validationHandler,
|
||||||
|
applicationId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addLiveQueryEventHandler(handler, applicationId) {
|
export function addLiveQueryEventHandler(handler, applicationId) {
|
||||||
@@ -455,6 +492,9 @@ export function maybeRunAfterFindTrigger(
|
|||||||
return Parse.Object.fromJSON(object);
|
return Parse.Object.fromJSON(object);
|
||||||
});
|
});
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
.then(() => {
|
||||||
|
return maybeRunValidator(request, `${triggerType}.${className}`);
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const response = trigger(request);
|
const response = trigger(request);
|
||||||
if (response && typeof response.then === 'function') {
|
if (response && typeof response.then === 'function') {
|
||||||
@@ -514,6 +554,9 @@ export function maybeRunQueryTrigger(
|
|||||||
isGet
|
isGet
|
||||||
);
|
);
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
.then(() => {
|
||||||
|
return maybeRunValidator(requestObject, `${triggerType}.${className}`);
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return trigger(requestObject);
|
return trigger(requestObject);
|
||||||
})
|
})
|
||||||
@@ -588,6 +631,184 @@ export function maybeRunQueryTrigger(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveError(message, defaultOpts) {
|
||||||
|
if (!defaultOpts) {
|
||||||
|
defaultOpts = {};
|
||||||
|
}
|
||||||
|
if (!message) {
|
||||||
|
return new Parse.Error(
|
||||||
|
defaultOpts.code || Parse.Error.SCRIPT_FAILED,
|
||||||
|
defaultOpts.message || 'Script failed.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (message instanceof Parse.Error) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = defaultOpts.code || Parse.Error.SCRIPT_FAILED;
|
||||||
|
// If it's an error, mark it as a script failed
|
||||||
|
if (typeof message === 'string') {
|
||||||
|
return new Parse.Error(code, message);
|
||||||
|
}
|
||||||
|
const error = new Parse.Error(code, message.message || message);
|
||||||
|
if (message instanceof Error) {
|
||||||
|
error.stack = message.stack;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
export function maybeRunValidator(request, functionName) {
|
||||||
|
const theValidator = getValidator(functionName, Parse.applicationId);
|
||||||
|
if (!theValidator) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() => {
|
||||||
|
return typeof theValidator === 'object'
|
||||||
|
? builtInTriggerValidator(theValidator, request)
|
||||||
|
: theValidator(request);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
const error = resolveError(e, {
|
||||||
|
code: Parse.Error.VALIDATION_ERROR,
|
||||||
|
message: 'Validation failed.',
|
||||||
|
});
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function builtInTriggerValidator(options, request) {
|
||||||
|
if (request.master && !options.validateMasterKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let reqUser = request.user;
|
||||||
|
if (
|
||||||
|
!reqUser &&
|
||||||
|
request.object &&
|
||||||
|
request.object.className === '_User' &&
|
||||||
|
!request.object.existed()
|
||||||
|
) {
|
||||||
|
reqUser = request.object;
|
||||||
|
}
|
||||||
|
if (options.requireUser && !reqUser) {
|
||||||
|
throw 'Validation failed. Please login to continue.';
|
||||||
|
}
|
||||||
|
if (options.requireMaster && !request.master) {
|
||||||
|
throw 'Validation failed. Master key is required to complete this request.';
|
||||||
|
}
|
||||||
|
let params = request.params || {};
|
||||||
|
if (request.object) {
|
||||||
|
params = request.object.toJSON();
|
||||||
|
}
|
||||||
|
const requiredParam = key => {
|
||||||
|
const value = params[key];
|
||||||
|
if (value == null) {
|
||||||
|
throw `Validation failed. Please specify data for ${key}.`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateOptions = (opt, key, val) => {
|
||||||
|
let opts = opt.options;
|
||||||
|
if (typeof opts === 'function') {
|
||||||
|
try {
|
||||||
|
const result = opts(val);
|
||||||
|
if (!result && result != null) {
|
||||||
|
throw opt.error || `Validation failed. Invalid value for ${key}.`;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (!e) {
|
||||||
|
throw opt.error || `Validation failed. Invalid value for ${key}.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw opt.error || e.message || e;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Array.isArray(opts)) {
|
||||||
|
opts = [opt.options];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!opts.includes(val)) {
|
||||||
|
throw (
|
||||||
|
opt.error ||
|
||||||
|
`Validation failed. Invalid option for ${key}. Expected: ${opts.join(
|
||||||
|
', '
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getType = fn => {
|
||||||
|
const match = fn && fn.toString().match(/^\s*function (\w+)/);
|
||||||
|
return (match ? match[1] : '').toLowerCase();
|
||||||
|
};
|
||||||
|
if (Array.isArray(options.fields)) {
|
||||||
|
for (const key of options.fields) {
|
||||||
|
requiredParam(key);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const key in options.fields) {
|
||||||
|
const opt = options.fields[key];
|
||||||
|
let val = params[key];
|
||||||
|
if (typeof opt === 'string') {
|
||||||
|
requiredParam(opt);
|
||||||
|
}
|
||||||
|
if (typeof opt === 'object') {
|
||||||
|
if (opt.default != null && val == null) {
|
||||||
|
val = opt.default;
|
||||||
|
params[key] = val;
|
||||||
|
if (request.object) {
|
||||||
|
request.object.set(key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opt.constant && request.object) {
|
||||||
|
if (request.original) {
|
||||||
|
request.object.set(key, request.original.get(key));
|
||||||
|
} else if (opt.default != null) {
|
||||||
|
request.object.set(key, opt.default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opt.required) {
|
||||||
|
requiredParam(key);
|
||||||
|
}
|
||||||
|
if (opt.type) {
|
||||||
|
const type = getType(opt.type);
|
||||||
|
if (type == 'array' && !Array.isArray(val)) {
|
||||||
|
throw `Validation failed. Invalid type for ${key}. Expected: array`;
|
||||||
|
} else if (typeof val !== type) {
|
||||||
|
throw `Validation failed. Invalid type for ${key}. Expected: ${type}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opt.options) {
|
||||||
|
validateOptions(opt, key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const userKeys = options.requireUserKeys || [];
|
||||||
|
if (Array.isArray(userKeys)) {
|
||||||
|
for (const key of userKeys) {
|
||||||
|
if (!reqUser) {
|
||||||
|
throw 'Please login to make this request.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reqUser.get(key) == null) {
|
||||||
|
throw `Validation failed. Please set data for ${key} on your account.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (typeof userKeys === 'object') {
|
||||||
|
for (const key in options.requireUserKeys) {
|
||||||
|
const opt = options.requireUserKeys[key];
|
||||||
|
if (opt.options) {
|
||||||
|
validateOptions(opt, key, reqUser.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// To be used as part of the promise chain when saving/deleting an object
|
// To be used as part of the promise chain when saving/deleting an object
|
||||||
// Will resolve successfully if no trigger is configured
|
// Will resolve successfully if no trigger is configured
|
||||||
// Resolves to an object, empty or containing an object key. A beforeSave
|
// Resolves to an object, empty or containing an object key. A beforeSave
|
||||||
@@ -657,6 +878,12 @@ export function maybeRunTrigger(
|
|||||||
// If triggers do not return a promise, they can run async code parallel
|
// If triggers do not return a promise, they can run async code parallel
|
||||||
// to the RestWrite.execute() call.
|
// to the RestWrite.execute() call.
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
.then(() => {
|
||||||
|
return maybeRunValidator(
|
||||||
|
request,
|
||||||
|
`${triggerType}.${parseObject.className}`
|
||||||
|
);
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const promise = trigger(request);
|
const promise = trigger(request);
|
||||||
if (
|
if (
|
||||||
@@ -755,6 +982,7 @@ export async function maybeRunFileTrigger(
|
|||||||
fileObject,
|
fileObject,
|
||||||
config
|
config
|
||||||
);
|
);
|
||||||
|
await maybeRunValidator(request, `${triggerType}.${FileClassName}`);
|
||||||
const result = await fileTrigger(request);
|
const result = await fileTrigger(request);
|
||||||
logTriggerSuccessBeforeHook(
|
logTriggerSuccessBeforeHook(
|
||||||
triggerType,
|
triggerType,
|
||||||
@@ -788,6 +1016,7 @@ export async function maybeRunConnectTrigger(triggerType, request) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
request.user = await userForSessionToken(request.sessionToken);
|
request.user = await userForSessionToken(request.sessionToken);
|
||||||
|
await maybeRunValidator(request, `${triggerType}.${ConnectClassName}`);
|
||||||
return trigger(request);
|
return trigger(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -804,6 +1033,7 @@ export async function maybeRunSubscribeTrigger(
|
|||||||
parseQuery.withJSON(request.query);
|
parseQuery.withJSON(request.query);
|
||||||
request.query = parseQuery;
|
request.query = parseQuery;
|
||||||
request.user = await userForSessionToken(request.sessionToken);
|
request.user = await userForSessionToken(request.sessionToken);
|
||||||
|
await maybeRunValidator(request, `${triggerType}.${className}`);
|
||||||
await trigger(request);
|
await trigger(request);
|
||||||
const query = request.query.toJSON();
|
const query = request.query.toJSON();
|
||||||
if (query.keys) {
|
if (query.keys) {
|
||||||
@@ -828,6 +1058,7 @@ export async function maybeRunAfterEventTrigger(
|
|||||||
request.original = Parse.Object.fromJSON(request.original);
|
request.original = Parse.Object.fromJSON(request.original);
|
||||||
}
|
}
|
||||||
request.user = await userForSessionToken(request.sessionToken);
|
request.user = await userForSessionToken(request.sessionToken);
|
||||||
|
await maybeRunValidator(request, `${triggerType}.${className}`);
|
||||||
return trigger(request);
|
return trigger(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user