2018-09-01 13:58:06 -04:00
|
|
|
'use strict';
|
2019-05-11 10:37:27 -07:00
|
|
|
const Config = require('../lib/Config');
|
2018-09-01 13:58:06 -04:00
|
|
|
const Parse = require('parse/node');
|
2018-09-24 17:07:51 -04:00
|
|
|
const request = require('../lib/request');
|
2018-09-01 13:58:06 -04:00
|
|
|
const InMemoryCacheAdapter = require('../lib/Adapters/Cache/InMemoryCacheAdapter')
|
|
|
|
|
.InMemoryCacheAdapter;
|
2016-05-19 13:38:16 -07:00
|
|
|
|
2020-04-02 17:00:15 -04:00
|
|
|
const mockAdapter = {
|
2020-10-02 00:19:26 +02:00
|
|
|
createFile: async filename => ({
|
2020-04-02 17:00:15 -04:00
|
|
|
name: filename,
|
|
|
|
|
location: `http://www.somewhere.com/${filename}`,
|
|
|
|
|
}),
|
|
|
|
|
deleteFile: () => {},
|
|
|
|
|
getFileData: () => {},
|
|
|
|
|
getFileLocation: (config, filename) => `http://www.somewhere.com/${filename}`,
|
|
|
|
|
validateFilename: () => {
|
|
|
|
|
return null;
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
2016-05-19 13:38:16 -07:00
|
|
|
describe('Cloud Code', () => {
|
|
|
|
|
it('can load absolute cloud code file', done => {
|
2018-09-01 13:58:06 -04:00
|
|
|
reconfigureServer({
|
|
|
|
|
cloud: __dirname + '/cloud/cloudCodeRelativeFile.js',
|
|
|
|
|
}).then(() => {
|
|
|
|
|
Parse.Cloud.run('cloudCodeInFile', {}).then(result => {
|
2020-10-25 15:06:58 -05:00
|
|
|
expect(result).toEqual('It is possible to define cloud code in a file.');
|
2018-09-01 13:58:06 -04:00
|
|
|
done();
|
2018-08-05 13:58:07 -04:00
|
|
|
});
|
2018-09-01 13:58:06 -04:00
|
|
|
});
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can load relative cloud code file', done => {
|
2020-10-25 15:06:58 -05:00
|
|
|
reconfigureServer({ cloud: './spec/cloud/cloudCodeAbsoluteFile.js' }).then(() => {
|
|
|
|
|
Parse.Cloud.run('cloudCodeInFile', {}).then(result => {
|
|
|
|
|
expect(result).toEqual('It is possible to define cloud code in a file.');
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can create functions', done => {
|
2018-08-06 17:39:38 -04:00
|
|
|
Parse.Cloud.define('hello', () => {
|
|
|
|
|
return 'Hello world!';
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2018-08-05 13:58:07 -04:00
|
|
|
Parse.Cloud.run('hello', {}).then(result => {
|
2016-05-19 13:38:16 -07:00
|
|
|
expect(result).toEqual('Hello world!');
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-23 12:06:25 +11:00
|
|
|
it('show warning on duplicate cloud functions', done => {
|
|
|
|
|
const logger = require('../lib/logger').logger;
|
|
|
|
|
spyOn(logger, 'warn').and.callFake(() => {});
|
|
|
|
|
Parse.Cloud.define('hello', () => {
|
|
|
|
|
return 'Hello world!';
|
|
|
|
|
});
|
|
|
|
|
Parse.Cloud.define('hello', () => {
|
|
|
|
|
return 'Hello world!';
|
|
|
|
|
});
|
|
|
|
|
expect(logger.warn).toHaveBeenCalledWith(
|
|
|
|
|
'Warning: Duplicate cloud functions exist for hello. Only the last one will be used and the others will be ignored.'
|
|
|
|
|
);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
|
2016-05-19 13:38:16 -07:00
|
|
|
it('is cleared cleared after the previous test', done => {
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.run('hello', {}).catch(error => {
|
|
|
|
|
expect(error.code).toEqual(141);
|
|
|
|
|
done();
|
|
|
|
|
});
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('basic beforeSave rejection', function (done) {
|
|
|
|
|
Parse.Cloud.beforeSave('BeforeSaveFail', function () {
|
2018-08-06 17:39:38 -04:00
|
|
|
throw new Error('You shall not pass!');
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('BeforeSaveFail');
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.set('foo', 'bar');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.save().then(
|
|
|
|
|
() => {
|
|
|
|
|
fail('Should not have been able to save BeforeSaveFailure class.');
|
|
|
|
|
done();
|
|
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('returns an error', done => {
|
2018-08-06 17:39:38 -04:00
|
|
|
Parse.Cloud.define('cloudCodeWithError', () => {
|
2016-11-24 15:47:41 -05:00
|
|
|
/* eslint-disable no-undef */
|
Make parse-server cloud code logging closer parse.com legacy (#2550)
* Make parse-server cloud code logging much to parse.com legacy. (fixes #2501)
1. More closely mimic the wording. Include the user id.
2. Truncate input and result at 1k char.
3. Use more sensible metadata that would makes sense to index. The guideline I used was: if it makes sense to filter on, put it in metadata. If it makes sense to "free text" search on, then put it in the message.
- file and console output, logging an object does not do what on might expect. For example, logging a function's "params":
```
expected:
info: Ran cloud function aFunction for user qWHLVEsbEe with:
Input: {"foo":"bar","bar":"baz"}
Result: "it worked!" functionName=aFunction, params= { foo: "bar", "bar": baz }, user=qWHLVEsbEe
what you actually get:
info: Ran cloud function aFunction for user qWHLVEsbEe with:
Input: {"foo":"bar","bar":"baz"}
Result: "it worked!" functionName=aFunction, foo=bar, bar=baz, user=qWHLVEsbEe
```
- logging highly variable metadata is pretty useless for indexing when logs are sent to a logging repository like elastic search. In that use case, you want to index stuff you expect to filter on like user, hook type.
- finally, putting the same input and result data in both the metadata and the message makes each message much larger with no additional value (that I know of anyway :).
4. Change some of the naming of functions in trigger.js to make future work easier. I was confused about why there were three logging functions in trigger and it took me awhile to get that before hooks and after hooks are logged differently. I just changed the names to make it obvious at first glance.
5. Add some try/catches to help any future futzers see syntax errors, etc instead of just hanging.
Some log examples from unit test output:
```
info: Ran cloud function loggerTest for user YUD2os1i5B with:
Input: {}
Result: {} functionName=loggerTest, user=YUD2os1i5B
info: beforeSave triggered for MyObject for user nssehQ3wtz:
Input: {}
Result: {} className=MyObject, triggerType=beforeSave, user=nssehQ3wtz
info: afterSave triggered for MyObject for user XdznQgTD0p:
Input: {"createdAt":"2016-08-19T01:11:31.249Z","updatedAt":"2016-08-19T01:11:31.249Z","objectId":"POoOOLL89U"} className=MyObject, triggerType=afterSave, user=XdznQgTD0p
error: beforeSave failed for MyObject for user 7JHqCZgnhf:
Input: {}
Error: {"code":141,"message":"uh oh!"} className=MyObject, triggerType=beforeSave, code=141, message=uh oh!, user=7JHqCZgnhf
info: Ran cloud function aFunction for user YR3nOoT3r9 with:
Input: {"foo":"bar"}
Result: "it worked!" functionName=aFunction, user=YR3nOoT3r9
error: Failed running cloud function aFunction for user Xm6NpOyuMC with:
Input: {"foo":"bar"}
Error: {"code":141,"message":"it failed!"} functionName=aFunction, code=141, message=it failed!, user=Xm6NpOyuMC
info: Ran cloud function aFunction for user CK1lvkmaLg with:
Input: {"longString":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lobortis semper diam, ac euismod diam pharetra sed. Etiam eget efficitur neque. Proin nec diam mi. Sed ut purus dolor. Nulla nulla nibh, ornare vitae ornare et, scelerisque rutrum eros. Mauris venenatis tincidunt turpis a mollis. Donec gravida eget enim in luctus.\n\nSed porttitor commodo orci, ut pretium eros convallis eget. Curabitur pretium velit in odio dictum luctus. Vivamus ac tristique arcu, a semper tellus. Morbi euismod purus dapibus vestibulum sagittis. Nunc dapibus vehicula leo at scelerisque. Donec porta mauris quis nulla imperdiet consectetur. Curabitur sagittis eleifend arcu eget elementum. Aenean interdum tincidunt ornare. Pellentesque sit amet interdum tortor. Pellentesque blandit nisl eget euismod consequat. Etiam feugiat felis sit amet porta pulvinar. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nNulla faucibus sem ipsum, at rhoncus diam pulvinar at. Vivamus consectetur, diam... (truncated)
Result: {"longString":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lobortis semper diam, ac euismod diam pharetra sed. Etiam eget efficitur neque. Proin nec diam mi. Sed ut purus dolor. Nulla nulla nibh, ornare vitae ornare et, scelerisque rutrum eros. Mauris venenatis tincidunt turpis a mollis. Donec gravida eget enim in luctus.\n\nSed porttitor commodo orci, ut pretium eros convallis eget. Curabitur pretium velit in odio dictum luctus. Vivamus ac tristique arcu, a semper tellus. Morbi euismod purus dapibus vestibulum sagittis. Nunc dapibus vehicula leo at scelerisque. Donec porta mauris quis nulla imperdiet consectetur. Curabitur sagittis eleifend arcu eget elementum. Aenean interdum tincidunt ornare. Pellentesque sit amet interdum tortor. Pellentesque blandit nisl eget euismod consequat. Etiam feugiat felis sit amet porta pulvinar. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nNulla faucibus sem ipsum, at rhoncus diam pulvinar at. Vivamus consectetur, diam... (truncated) functionName=aFunction, user=CK1lvkmaLg
```
* Implement PR comments:
- add back params to metadata and add back to the test
- use screaming snake case for conts
* fix typo
2016-08-19 13:39:51 -07:00
|
|
|
foo.bar();
|
2016-11-24 15:47:41 -05:00
|
|
|
/* eslint-enable no-undef */
|
2018-08-06 17:39:38 -04:00
|
|
|
return 'I better throw an error.';
|
Make parse-server cloud code logging closer parse.com legacy (#2550)
* Make parse-server cloud code logging much to parse.com legacy. (fixes #2501)
1. More closely mimic the wording. Include the user id.
2. Truncate input and result at 1k char.
3. Use more sensible metadata that would makes sense to index. The guideline I used was: if it makes sense to filter on, put it in metadata. If it makes sense to "free text" search on, then put it in the message.
- file and console output, logging an object does not do what on might expect. For example, logging a function's "params":
```
expected:
info: Ran cloud function aFunction for user qWHLVEsbEe with:
Input: {"foo":"bar","bar":"baz"}
Result: "it worked!" functionName=aFunction, params= { foo: "bar", "bar": baz }, user=qWHLVEsbEe
what you actually get:
info: Ran cloud function aFunction for user qWHLVEsbEe with:
Input: {"foo":"bar","bar":"baz"}
Result: "it worked!" functionName=aFunction, foo=bar, bar=baz, user=qWHLVEsbEe
```
- logging highly variable metadata is pretty useless for indexing when logs are sent to a logging repository like elastic search. In that use case, you want to index stuff you expect to filter on like user, hook type.
- finally, putting the same input and result data in both the metadata and the message makes each message much larger with no additional value (that I know of anyway :).
4. Change some of the naming of functions in trigger.js to make future work easier. I was confused about why there were three logging functions in trigger and it took me awhile to get that before hooks and after hooks are logged differently. I just changed the names to make it obvious at first glance.
5. Add some try/catches to help any future futzers see syntax errors, etc instead of just hanging.
Some log examples from unit test output:
```
info: Ran cloud function loggerTest for user YUD2os1i5B with:
Input: {}
Result: {} functionName=loggerTest, user=YUD2os1i5B
info: beforeSave triggered for MyObject for user nssehQ3wtz:
Input: {}
Result: {} className=MyObject, triggerType=beforeSave, user=nssehQ3wtz
info: afterSave triggered for MyObject for user XdznQgTD0p:
Input: {"createdAt":"2016-08-19T01:11:31.249Z","updatedAt":"2016-08-19T01:11:31.249Z","objectId":"POoOOLL89U"} className=MyObject, triggerType=afterSave, user=XdznQgTD0p
error: beforeSave failed for MyObject for user 7JHqCZgnhf:
Input: {}
Error: {"code":141,"message":"uh oh!"} className=MyObject, triggerType=beforeSave, code=141, message=uh oh!, user=7JHqCZgnhf
info: Ran cloud function aFunction for user YR3nOoT3r9 with:
Input: {"foo":"bar"}
Result: "it worked!" functionName=aFunction, user=YR3nOoT3r9
error: Failed running cloud function aFunction for user Xm6NpOyuMC with:
Input: {"foo":"bar"}
Error: {"code":141,"message":"it failed!"} functionName=aFunction, code=141, message=it failed!, user=Xm6NpOyuMC
info: Ran cloud function aFunction for user CK1lvkmaLg with:
Input: {"longString":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lobortis semper diam, ac euismod diam pharetra sed. Etiam eget efficitur neque. Proin nec diam mi. Sed ut purus dolor. Nulla nulla nibh, ornare vitae ornare et, scelerisque rutrum eros. Mauris venenatis tincidunt turpis a mollis. Donec gravida eget enim in luctus.\n\nSed porttitor commodo orci, ut pretium eros convallis eget. Curabitur pretium velit in odio dictum luctus. Vivamus ac tristique arcu, a semper tellus. Morbi euismod purus dapibus vestibulum sagittis. Nunc dapibus vehicula leo at scelerisque. Donec porta mauris quis nulla imperdiet consectetur. Curabitur sagittis eleifend arcu eget elementum. Aenean interdum tincidunt ornare. Pellentesque sit amet interdum tortor. Pellentesque blandit nisl eget euismod consequat. Etiam feugiat felis sit amet porta pulvinar. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nNulla faucibus sem ipsum, at rhoncus diam pulvinar at. Vivamus consectetur, diam... (truncated)
Result: {"longString":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lobortis semper diam, ac euismod diam pharetra sed. Etiam eget efficitur neque. Proin nec diam mi. Sed ut purus dolor. Nulla nulla nibh, ornare vitae ornare et, scelerisque rutrum eros. Mauris venenatis tincidunt turpis a mollis. Donec gravida eget enim in luctus.\n\nSed porttitor commodo orci, ut pretium eros convallis eget. Curabitur pretium velit in odio dictum luctus. Vivamus ac tristique arcu, a semper tellus. Morbi euismod purus dapibus vestibulum sagittis. Nunc dapibus vehicula leo at scelerisque. Donec porta mauris quis nulla imperdiet consectetur. Curabitur sagittis eleifend arcu eget elementum. Aenean interdum tincidunt ornare. Pellentesque sit amet interdum tortor. Pellentesque blandit nisl eget euismod consequat. Etiam feugiat felis sit amet porta pulvinar. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nNulla faucibus sem ipsum, at rhoncus diam pulvinar at. Vivamus consectetur, diam... (truncated) functionName=aFunction, user=CK1lvkmaLg
```
* Implement PR comments:
- add back params to metadata and add back to the test
- use screaming snake case for conts
* fix typo
2016-08-19 13:39:51 -07:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.run('cloudCodeWithError').then(
|
|
|
|
|
() => done.fail('should not succeed'),
|
|
|
|
|
e => {
|
|
|
|
|
expect(e).toEqual(new Parse.Error(141, 'foo is not defined'));
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
Make parse-server cloud code logging closer parse.com legacy (#2550)
* Make parse-server cloud code logging much to parse.com legacy. (fixes #2501)
1. More closely mimic the wording. Include the user id.
2. Truncate input and result at 1k char.
3. Use more sensible metadata that would makes sense to index. The guideline I used was: if it makes sense to filter on, put it in metadata. If it makes sense to "free text" search on, then put it in the message.
- file and console output, logging an object does not do what on might expect. For example, logging a function's "params":
```
expected:
info: Ran cloud function aFunction for user qWHLVEsbEe with:
Input: {"foo":"bar","bar":"baz"}
Result: "it worked!" functionName=aFunction, params= { foo: "bar", "bar": baz }, user=qWHLVEsbEe
what you actually get:
info: Ran cloud function aFunction for user qWHLVEsbEe with:
Input: {"foo":"bar","bar":"baz"}
Result: "it worked!" functionName=aFunction, foo=bar, bar=baz, user=qWHLVEsbEe
```
- logging highly variable metadata is pretty useless for indexing when logs are sent to a logging repository like elastic search. In that use case, you want to index stuff you expect to filter on like user, hook type.
- finally, putting the same input and result data in both the metadata and the message makes each message much larger with no additional value (that I know of anyway :).
4. Change some of the naming of functions in trigger.js to make future work easier. I was confused about why there were three logging functions in trigger and it took me awhile to get that before hooks and after hooks are logged differently. I just changed the names to make it obvious at first glance.
5. Add some try/catches to help any future futzers see syntax errors, etc instead of just hanging.
Some log examples from unit test output:
```
info: Ran cloud function loggerTest for user YUD2os1i5B with:
Input: {}
Result: {} functionName=loggerTest, user=YUD2os1i5B
info: beforeSave triggered for MyObject for user nssehQ3wtz:
Input: {}
Result: {} className=MyObject, triggerType=beforeSave, user=nssehQ3wtz
info: afterSave triggered for MyObject for user XdznQgTD0p:
Input: {"createdAt":"2016-08-19T01:11:31.249Z","updatedAt":"2016-08-19T01:11:31.249Z","objectId":"POoOOLL89U"} className=MyObject, triggerType=afterSave, user=XdznQgTD0p
error: beforeSave failed for MyObject for user 7JHqCZgnhf:
Input: {}
Error: {"code":141,"message":"uh oh!"} className=MyObject, triggerType=beforeSave, code=141, message=uh oh!, user=7JHqCZgnhf
info: Ran cloud function aFunction for user YR3nOoT3r9 with:
Input: {"foo":"bar"}
Result: "it worked!" functionName=aFunction, user=YR3nOoT3r9
error: Failed running cloud function aFunction for user Xm6NpOyuMC with:
Input: {"foo":"bar"}
Error: {"code":141,"message":"it failed!"} functionName=aFunction, code=141, message=it failed!, user=Xm6NpOyuMC
info: Ran cloud function aFunction for user CK1lvkmaLg with:
Input: {"longString":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lobortis semper diam, ac euismod diam pharetra sed. Etiam eget efficitur neque. Proin nec diam mi. Sed ut purus dolor. Nulla nulla nibh, ornare vitae ornare et, scelerisque rutrum eros. Mauris venenatis tincidunt turpis a mollis. Donec gravida eget enim in luctus.\n\nSed porttitor commodo orci, ut pretium eros convallis eget. Curabitur pretium velit in odio dictum luctus. Vivamus ac tristique arcu, a semper tellus. Morbi euismod purus dapibus vestibulum sagittis. Nunc dapibus vehicula leo at scelerisque. Donec porta mauris quis nulla imperdiet consectetur. Curabitur sagittis eleifend arcu eget elementum. Aenean interdum tincidunt ornare. Pellentesque sit amet interdum tortor. Pellentesque blandit nisl eget euismod consequat. Etiam feugiat felis sit amet porta pulvinar. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nNulla faucibus sem ipsum, at rhoncus diam pulvinar at. Vivamus consectetur, diam... (truncated)
Result: {"longString":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lobortis semper diam, ac euismod diam pharetra sed. Etiam eget efficitur neque. Proin nec diam mi. Sed ut purus dolor. Nulla nulla nibh, ornare vitae ornare et, scelerisque rutrum eros. Mauris venenatis tincidunt turpis a mollis. Donec gravida eget enim in luctus.\n\nSed porttitor commodo orci, ut pretium eros convallis eget. Curabitur pretium velit in odio dictum luctus. Vivamus ac tristique arcu, a semper tellus. Morbi euismod purus dapibus vestibulum sagittis. Nunc dapibus vehicula leo at scelerisque. Donec porta mauris quis nulla imperdiet consectetur. Curabitur sagittis eleifend arcu eget elementum. Aenean interdum tincidunt ornare. Pellentesque sit amet interdum tortor. Pellentesque blandit nisl eget euismod consequat. Etiam feugiat felis sit amet porta pulvinar. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nNulla faucibus sem ipsum, at rhoncus diam pulvinar at. Vivamus consectetur, diam... (truncated) functionName=aFunction, user=CK1lvkmaLg
```
* Implement PR comments:
- add back params to metadata and add back to the test
- use screaming snake case for conts
* fix typo
2016-08-19 13:39:51 -07:00
|
|
|
});
|
|
|
|
|
|
2020-10-26 04:36:54 +11:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-27 06:49:30 +11:00
|
|
|
it('beforeFind can throw string', async function (done) {
|
|
|
|
|
Parse.Cloud.beforeFind('beforeFind', () => {
|
|
|
|
|
throw 'throw beforeFind';
|
|
|
|
|
});
|
|
|
|
|
const obj = new Parse.Object('beforeFind');
|
|
|
|
|
obj.set('foo', 'bar');
|
|
|
|
|
await obj.save();
|
|
|
|
|
expect(obj.get('foo')).toBe('bar');
|
|
|
|
|
try {
|
|
|
|
|
const query = new Parse.Query('beforeFind');
|
|
|
|
|
await query.first();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
expect(e.code).toBe(141);
|
|
|
|
|
expect(e.message).toBe('throw beforeFind');
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('beforeSave rejection with custom error code', function (done) {
|
|
|
|
|
Parse.Cloud.beforeSave('BeforeSaveFailWithErrorCode', function () {
|
2018-08-06 17:39:38 -04:00
|
|
|
throw new Parse.Error(999, 'Nope');
|
2016-06-01 10:28:06 -04:00
|
|
|
});
|
|
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('BeforeSaveFailWithErrorCode');
|
2016-06-01 10:28:06 -04:00
|
|
|
obj.set('foo', 'bar');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.save().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2020-10-25 15:06:58 -05:00
|
|
|
fail('Should not have been able to save BeforeSaveFailWithErrorCode class.');
|
2018-09-01 13:58:06 -04:00
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(error.code).toEqual(999);
|
|
|
|
|
expect(error.message).toEqual('Nope');
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-06-01 10:28:06 -04:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('basic beforeSave rejection via promise', function (done) {
|
|
|
|
|
Parse.Cloud.beforeSave('BeforeSaveFailWithPromise', function () {
|
2018-02-17 09:55:30 -05:00
|
|
|
const query = new Parse.Query('Yolo');
|
2018-09-01 13:58:06 -04:00
|
|
|
return query.find().then(
|
|
|
|
|
() => {
|
|
|
|
|
throw 'Nope';
|
|
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
return Promise.response();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('BeforeSaveFailWithPromise');
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.set('foo', 'bar');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.save().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail('Should not have been able to save BeforeSaveFailure class.');
|
|
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED);
|
|
|
|
|
expect(error.message).toEqual('Nope');
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test beforeSave changed object success', function (done) {
|
|
|
|
|
Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
|
2016-05-19 13:38:16 -07:00
|
|
|
req.object.set('foo', 'baz');
|
|
|
|
|
});
|
|
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('BeforeSaveChanged');
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.set('foo', 'bar');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.save().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
const query = new Parse.Query('BeforeSaveChanged');
|
|
|
|
|
query.get(obj.id).then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function (objAgain) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(objAgain.get('foo')).toEqual('baz');
|
|
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2016-05-19 13:38:16 -07:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
2018-09-01 13:58:06 -04:00
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2020-12-09 12:19:15 -06:00
|
|
|
it('test beforeSave with invalid field', async () => {
|
|
|
|
|
Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
|
|
|
|
|
req.object.set('length', 0);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const obj = new Parse.Object('BeforeSaveChanged');
|
|
|
|
|
obj.set('foo', 'bar');
|
|
|
|
|
try {
|
|
|
|
|
await obj.save();
|
|
|
|
|
fail('should not succeed');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
expect(e.message).toBe('Invalid field name: length.');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it("test beforeSave changed object fail doesn't change object", async function () {
|
|
|
|
|
Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
|
2019-03-14 14:17:29 -04:00
|
|
|
if (req.object.has('fail')) {
|
|
|
|
|
return Promise.reject(new Error('something went wrong'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const obj = new Parse.Object('BeforeSaveChanged');
|
|
|
|
|
obj.set('foo', 'bar');
|
|
|
|
|
await obj.save();
|
|
|
|
|
obj.set('foo', 'baz').set('fail', true);
|
|
|
|
|
try {
|
|
|
|
|
await obj.save();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
await obj.fetch();
|
|
|
|
|
expect(obj.get('foo')).toBe('bar');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('test beforeSave returns value on create and update', done => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
|
2016-05-19 13:38:16 -07:00
|
|
|
req.object.set('foo', 'baz');
|
|
|
|
|
});
|
|
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('BeforeSaveChanged');
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.set('foo', 'bing');
|
2016-11-24 15:47:41 -05:00
|
|
|
obj.save().then(() => {
|
2016-05-19 13:38:16 -07:00
|
|
|
expect(obj.get('foo')).toEqual('baz');
|
|
|
|
|
obj.set('foo', 'bar');
|
2016-11-24 15:47:41 -05:00
|
|
|
return obj.save().then(() => {
|
2016-05-19 13:38:16 -07:00
|
|
|
expect(obj.get('foo')).toEqual('baz');
|
|
|
|
|
done();
|
2018-09-01 13:58:06 -04:00
|
|
|
});
|
|
|
|
|
});
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2019-03-14 14:17:29 -04:00
|
|
|
it('test beforeSave applies changes when beforeSave returns true', done => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('Insurance', function (req) {
|
2019-03-14 14:17:29 -04:00
|
|
|
req.object.set('rate', '$49.99/Month');
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const insurance = new Parse.Object('Insurance');
|
|
|
|
|
insurance.set('rate', '$5.00/Month');
|
|
|
|
|
insurance.save().then(insurance => {
|
|
|
|
|
expect(insurance.get('rate')).toEqual('$49.99/Month');
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('test beforeSave applies changes and resolves returned promise', done => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('Insurance', function (req) {
|
2019-03-14 14:17:29 -04:00
|
|
|
req.object.set('rate', '$49.99/Month');
|
|
|
|
|
return new Parse.Query('Pet').get(req.object.get('pet').id).then(pet => {
|
|
|
|
|
pet.set('healthy', true);
|
|
|
|
|
return pet.save();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const pet = new Parse.Object('Pet');
|
|
|
|
|
pet.set('healthy', false);
|
|
|
|
|
pet.save().then(pet => {
|
|
|
|
|
const insurance = new Parse.Object('Insurance');
|
|
|
|
|
insurance.set('pet', pet);
|
|
|
|
|
insurance.set('rate', '$5.00/Month');
|
|
|
|
|
insurance.save().then(insurance => {
|
|
|
|
|
expect(insurance.get('rate')).toEqual('$49.99/Month');
|
|
|
|
|
new Parse.Query('Pet').get(insurance.get('pet').id).then(pet => {
|
|
|
|
|
expect(pet.get('healthy')).toEqual(true);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2019-05-11 10:37:27 -07:00
|
|
|
it('beforeSave should be called only if user fulfills permissions', async () => {
|
|
|
|
|
const triggeruser = new Parse.User();
|
|
|
|
|
triggeruser.setUsername('triggeruser');
|
|
|
|
|
triggeruser.setPassword('triggeruser');
|
|
|
|
|
await triggeruser.signUp();
|
|
|
|
|
|
|
|
|
|
const triggeruser2 = new Parse.User();
|
|
|
|
|
triggeruser2.setUsername('triggeruser2');
|
|
|
|
|
triggeruser2.setPassword('triggeruser2');
|
|
|
|
|
await triggeruser2.signUp();
|
|
|
|
|
|
|
|
|
|
const triggeruser3 = new Parse.User();
|
|
|
|
|
triggeruser3.setUsername('triggeruser3');
|
|
|
|
|
triggeruser3.setPassword('triggeruser3');
|
|
|
|
|
await triggeruser3.signUp();
|
|
|
|
|
|
|
|
|
|
const triggeruser4 = new Parse.User();
|
|
|
|
|
triggeruser4.setUsername('triggeruser4');
|
|
|
|
|
triggeruser4.setPassword('triggeruser4');
|
|
|
|
|
await triggeruser4.signUp();
|
|
|
|
|
|
|
|
|
|
const triggeruser5 = new Parse.User();
|
|
|
|
|
triggeruser5.setUsername('triggeruser5');
|
|
|
|
|
triggeruser5.setPassword('triggeruser5');
|
|
|
|
|
await triggeruser5.signUp();
|
|
|
|
|
|
|
|
|
|
const triggerroleacl = new Parse.ACL();
|
|
|
|
|
triggerroleacl.setPublicReadAccess(true);
|
|
|
|
|
|
|
|
|
|
const triggerrole = new Parse.Role();
|
|
|
|
|
triggerrole.setName('triggerrole');
|
|
|
|
|
triggerrole.setACL(triggerroleacl);
|
|
|
|
|
triggerrole.getUsers().add(triggeruser);
|
|
|
|
|
triggerrole.getUsers().add(triggeruser3);
|
|
|
|
|
await triggerrole.save();
|
|
|
|
|
|
|
|
|
|
const config = Config.get('test');
|
|
|
|
|
const schema = await config.database.loadSchema();
|
|
|
|
|
await schema.addClassIfNotExists(
|
|
|
|
|
'triggerclass',
|
|
|
|
|
{
|
|
|
|
|
someField: { type: 'String' },
|
|
|
|
|
pointerToUser: { type: 'Pointer', targetClass: '_User' },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
find: {
|
|
|
|
|
'role:triggerrole': true,
|
|
|
|
|
[triggeruser.id]: true,
|
|
|
|
|
[triggeruser2.id]: true,
|
|
|
|
|
},
|
|
|
|
|
create: {
|
|
|
|
|
'role:triggerrole': true,
|
|
|
|
|
[triggeruser.id]: true,
|
|
|
|
|
[triggeruser2.id]: true,
|
|
|
|
|
},
|
|
|
|
|
get: {
|
|
|
|
|
'role:triggerrole': true,
|
|
|
|
|
[triggeruser.id]: true,
|
|
|
|
|
[triggeruser2.id]: true,
|
|
|
|
|
},
|
|
|
|
|
update: {
|
|
|
|
|
'role:triggerrole': true,
|
|
|
|
|
[triggeruser.id]: true,
|
|
|
|
|
[triggeruser2.id]: true,
|
|
|
|
|
},
|
|
|
|
|
addField: {
|
|
|
|
|
'role:triggerrole': true,
|
|
|
|
|
[triggeruser.id]: true,
|
|
|
|
|
[triggeruser2.id]: true,
|
|
|
|
|
},
|
|
|
|
|
delete: {
|
|
|
|
|
'role:triggerrole': true,
|
|
|
|
|
[triggeruser.id]: true,
|
|
|
|
|
[triggeruser2.id]: true,
|
|
|
|
|
},
|
|
|
|
|
readUserFields: ['pointerToUser'],
|
|
|
|
|
writeUserFields: ['pointerToUser'],
|
|
|
|
|
},
|
|
|
|
|
{}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let called = 0;
|
|
|
|
|
Parse.Cloud.beforeSave('triggerclass', () => {
|
|
|
|
|
called++;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const triggerobject = new Parse.Object('triggerclass');
|
|
|
|
|
triggerobject.set('someField', 'someValue');
|
|
|
|
|
triggerobject.set('someField2', 'someValue');
|
|
|
|
|
const triggerobjectacl = new Parse.ACL();
|
|
|
|
|
triggerobjectacl.setPublicReadAccess(false);
|
|
|
|
|
triggerobjectacl.setPublicWriteAccess(false);
|
|
|
|
|
triggerobjectacl.setRoleReadAccess(triggerrole, true);
|
|
|
|
|
triggerobjectacl.setRoleWriteAccess(triggerrole, true);
|
|
|
|
|
triggerobjectacl.setReadAccess(triggeruser.id, true);
|
|
|
|
|
triggerobjectacl.setWriteAccess(triggeruser.id, true);
|
|
|
|
|
triggerobjectacl.setReadAccess(triggeruser2.id, true);
|
|
|
|
|
triggerobjectacl.setWriteAccess(triggeruser2.id, true);
|
|
|
|
|
triggerobject.setACL(triggerobjectacl);
|
|
|
|
|
|
|
|
|
|
await triggerobject.save(undefined, {
|
|
|
|
|
sessionToken: triggeruser.getSessionToken(),
|
|
|
|
|
});
|
|
|
|
|
expect(called).toBe(1);
|
|
|
|
|
await triggerobject.save(undefined, {
|
|
|
|
|
sessionToken: triggeruser.getSessionToken(),
|
|
|
|
|
});
|
|
|
|
|
expect(called).toBe(2);
|
|
|
|
|
await triggerobject.save(undefined, {
|
|
|
|
|
sessionToken: triggeruser2.getSessionToken(),
|
|
|
|
|
});
|
|
|
|
|
expect(called).toBe(3);
|
|
|
|
|
await triggerobject.save(undefined, {
|
|
|
|
|
sessionToken: triggeruser3.getSessionToken(),
|
|
|
|
|
});
|
|
|
|
|
expect(called).toBe(4);
|
|
|
|
|
|
|
|
|
|
const triggerobject2 = new Parse.Object('triggerclass');
|
|
|
|
|
triggerobject2.set('someField', 'someValue');
|
|
|
|
|
triggerobject2.set('someField22', 'someValue');
|
|
|
|
|
const triggerobjectacl2 = new Parse.ACL();
|
|
|
|
|
triggerobjectacl2.setPublicReadAccess(false);
|
|
|
|
|
triggerobjectacl2.setPublicWriteAccess(false);
|
|
|
|
|
triggerobjectacl2.setReadAccess(triggeruser.id, true);
|
|
|
|
|
triggerobjectacl2.setWriteAccess(triggeruser.id, true);
|
|
|
|
|
triggerobjectacl2.setReadAccess(triggeruser2.id, true);
|
|
|
|
|
triggerobjectacl2.setWriteAccess(triggeruser2.id, true);
|
|
|
|
|
triggerobjectacl2.setReadAccess(triggeruser5.id, true);
|
|
|
|
|
triggerobjectacl2.setWriteAccess(triggeruser5.id, true);
|
|
|
|
|
triggerobject2.setACL(triggerobjectacl2);
|
|
|
|
|
|
|
|
|
|
await triggerobject2.save(undefined, {
|
|
|
|
|
sessionToken: triggeruser2.getSessionToken(),
|
|
|
|
|
});
|
|
|
|
|
expect(called).toBe(5);
|
|
|
|
|
await triggerobject2.save(undefined, {
|
|
|
|
|
sessionToken: triggeruser2.getSessionToken(),
|
|
|
|
|
});
|
|
|
|
|
expect(called).toBe(6);
|
|
|
|
|
await triggerobject2.save(undefined, {
|
|
|
|
|
sessionToken: triggeruser.getSessionToken(),
|
|
|
|
|
});
|
|
|
|
|
expect(called).toBe(7);
|
|
|
|
|
|
|
|
|
|
let catched = false;
|
|
|
|
|
try {
|
|
|
|
|
await triggerobject2.save(undefined, {
|
|
|
|
|
sessionToken: triggeruser3.getSessionToken(),
|
|
|
|
|
});
|
|
|
|
|
} catch (e) {
|
|
|
|
|
catched = true;
|
|
|
|
|
expect(e.code).toBe(101);
|
|
|
|
|
}
|
|
|
|
|
expect(catched).toBe(true);
|
|
|
|
|
expect(called).toBe(7);
|
|
|
|
|
|
|
|
|
|
catched = false;
|
|
|
|
|
try {
|
|
|
|
|
await triggerobject2.save(undefined, {
|
|
|
|
|
sessionToken: triggeruser4.getSessionToken(),
|
|
|
|
|
});
|
|
|
|
|
} catch (e) {
|
|
|
|
|
catched = true;
|
|
|
|
|
expect(e.code).toBe(101);
|
|
|
|
|
}
|
|
|
|
|
expect(catched).toBe(true);
|
|
|
|
|
expect(called).toBe(7);
|
|
|
|
|
|
|
|
|
|
catched = false;
|
|
|
|
|
try {
|
|
|
|
|
await triggerobject2.save(undefined, {
|
|
|
|
|
sessionToken: triggeruser5.getSessionToken(),
|
|
|
|
|
});
|
|
|
|
|
} catch (e) {
|
|
|
|
|
catched = true;
|
|
|
|
|
expect(e.code).toBe(101);
|
|
|
|
|
}
|
|
|
|
|
expect(catched).toBe(true);
|
|
|
|
|
expect(called).toBe(7);
|
|
|
|
|
|
|
|
|
|
const triggerobject3 = new Parse.Object('triggerclass');
|
|
|
|
|
triggerobject3.set('someField', 'someValue');
|
|
|
|
|
triggerobject3.set('someField33', 'someValue');
|
|
|
|
|
|
|
|
|
|
catched = false;
|
|
|
|
|
try {
|
|
|
|
|
await triggerobject3.save(undefined, {
|
|
|
|
|
sessionToken: triggeruser4.getSessionToken(),
|
|
|
|
|
});
|
|
|
|
|
} catch (e) {
|
|
|
|
|
catched = true;
|
|
|
|
|
expect(e.code).toBe(119);
|
|
|
|
|
}
|
|
|
|
|
expect(catched).toBe(true);
|
|
|
|
|
expect(called).toBe(7);
|
|
|
|
|
|
|
|
|
|
catched = false;
|
|
|
|
|
try {
|
|
|
|
|
await triggerobject3.save(undefined, {
|
|
|
|
|
sessionToken: triggeruser5.getSessionToken(),
|
|
|
|
|
});
|
|
|
|
|
} catch (e) {
|
|
|
|
|
catched = true;
|
|
|
|
|
expect(e.code).toBe(119);
|
|
|
|
|
}
|
|
|
|
|
expect(catched).toBe(true);
|
|
|
|
|
expect(called).toBe(7);
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test afterSave ran and created an object', function (done) {
|
|
|
|
|
Parse.Cloud.afterSave('AfterSaveTest', function (req) {
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('AfterSaveProof');
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.set('proof', req.object.id);
|
2019-05-14 11:34:51 -07:00
|
|
|
obj.save().then(test);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('AfterSaveTest');
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.save();
|
|
|
|
|
|
2019-05-14 11:34:51 -07:00
|
|
|
function test() {
|
2018-02-17 09:55:30 -05:00
|
|
|
const query = new Parse.Query('AfterSaveProof');
|
2016-05-19 13:38:16 -07:00
|
|
|
query.equalTo('proof', obj.id);
|
2018-09-01 13:58:06 -04:00
|
|
|
query.find().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function (results) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(results.length).toEqual(1);
|
|
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2019-05-14 11:34:51 -07:00
|
|
|
}
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test afterSave ran on created object and returned a promise', function (done) {
|
|
|
|
|
Parse.Cloud.afterSave('AfterSaveTest2', function (req) {
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj = req.object;
|
2018-09-01 13:58:06 -04:00
|
|
|
if (!obj.existed()) {
|
|
|
|
|
return new Promise(resolve => {
|
2020-10-02 00:19:26 +02:00
|
|
|
setTimeout(function () {
|
2018-08-05 13:58:07 -04:00
|
|
|
obj.set('proof', obj.id);
|
2020-10-02 00:19:26 +02:00
|
|
|
obj.save().then(function () {
|
2018-08-05 13:58:07 -04:00
|
|
|
resolve();
|
|
|
|
|
});
|
|
|
|
|
}, 1000);
|
|
|
|
|
});
|
2016-11-24 15:47:41 -05:00
|
|
|
}
|
2016-08-17 15:26:42 +02:00
|
|
|
});
|
|
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj = new Parse.Object('AfterSaveTest2');
|
2020-10-02 00:19:26 +02:00
|
|
|
obj.save().then(function () {
|
2016-12-07 15:17:05 -08:00
|
|
|
const query = new Parse.Query('AfterSaveTest2');
|
2016-11-24 15:47:41 -05:00
|
|
|
query.equalTo('proof', obj.id);
|
2018-09-01 13:58:06 -04:00
|
|
|
query.find().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function (results) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(results.length).toEqual(1);
|
|
|
|
|
const savedObject = results[0];
|
|
|
|
|
expect(savedObject.get('proof')).toEqual(obj.id);
|
|
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-08-17 15:26:42 +02:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2016-08-30 07:19:21 -04:00
|
|
|
// TODO: Fails on CI randomly as racing
|
2020-10-02 00:19:26 +02:00
|
|
|
xit('test afterSave ignoring promise, object not found', function (done) {
|
|
|
|
|
Parse.Cloud.afterSave('AfterSaveTest2', function (req) {
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj = req.object;
|
2018-09-01 13:58:06 -04:00
|
|
|
if (!obj.existed()) {
|
|
|
|
|
return new Promise(resolve => {
|
2020-10-02 00:19:26 +02:00
|
|
|
setTimeout(function () {
|
2018-08-05 13:58:07 -04:00
|
|
|
obj.set('proof', obj.id);
|
2020-10-02 00:19:26 +02:00
|
|
|
obj.save().then(function () {
|
2018-08-05 13:58:07 -04:00
|
|
|
resolve();
|
|
|
|
|
});
|
|
|
|
|
}, 1000);
|
|
|
|
|
});
|
2016-11-24 15:47:41 -05:00
|
|
|
}
|
2016-08-17 15:26:42 +02:00
|
|
|
});
|
|
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj = new Parse.Object('AfterSaveTest2');
|
2020-10-02 00:19:26 +02:00
|
|
|
obj.save().then(function () {
|
2016-11-24 15:47:41 -05:00
|
|
|
done();
|
2018-09-01 13:58:06 -04:00
|
|
|
});
|
2016-08-17 15:26:42 +02:00
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const query = new Parse.Query('AfterSaveTest2');
|
2016-08-17 15:26:42 +02:00
|
|
|
query.equalTo('proof', obj.id);
|
2018-09-01 13:58:06 -04:00
|
|
|
query.find().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function (results) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(results.length).toEqual(0);
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-08-17 15:26:42 +02:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test afterSave rejecting promise', function (done) {
|
|
|
|
|
Parse.Cloud.afterSave('AfterSaveTest2', function () {
|
2018-08-05 13:58:07 -04:00
|
|
|
return new Promise((resolve, reject) => {
|
2020-10-02 00:19:26 +02:00
|
|
|
setTimeout(function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
reject('THIS SHOULD BE IGNORED');
|
2018-08-05 13:58:07 -04:00
|
|
|
}, 1000);
|
|
|
|
|
});
|
2016-11-24 15:47:41 -05:00
|
|
|
});
|
2016-08-17 15:26:42 +02:00
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj = new Parse.Object('AfterSaveTest2');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.save().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-08-17 15:26:42 +02:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test afterDelete returning promise, object is deleted when destroy resolves', function (done) {
|
|
|
|
|
Parse.Cloud.afterDelete('AfterDeleteTest2', function (req) {
|
2018-09-01 13:58:06 -04:00
|
|
|
return new Promise(resolve => {
|
2020-10-02 00:19:26 +02:00
|
|
|
setTimeout(function () {
|
2018-08-05 13:58:07 -04:00
|
|
|
const obj = new Parse.Object('AfterDeleteTestProof');
|
|
|
|
|
obj.set('proof', req.object.id);
|
2020-10-02 00:19:26 +02:00
|
|
|
obj.save().then(function () {
|
2018-08-05 13:58:07 -04:00
|
|
|
resolve();
|
|
|
|
|
});
|
|
|
|
|
}, 1000);
|
|
|
|
|
});
|
2016-11-24 15:47:41 -05:00
|
|
|
});
|
2016-08-17 15:26:42 +02:00
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
const errorHandler = function (error) {
|
2016-11-24 15:47:41 -05:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
2018-09-01 13:58:06 -04:00
|
|
|
};
|
2016-08-17 15:26:42 +02:00
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj = new Parse.Object('AfterDeleteTest2');
|
2020-10-02 00:19:26 +02:00
|
|
|
obj.save().then(function () {
|
|
|
|
|
obj.destroy().then(function () {
|
2016-12-07 15:17:05 -08:00
|
|
|
const query = new Parse.Query('AfterDeleteTestProof');
|
2016-11-24 15:47:41 -05:00
|
|
|
query.equalTo('proof', obj.id);
|
2020-10-02 00:19:26 +02:00
|
|
|
query.find().then(function (results) {
|
2016-11-24 15:47:41 -05:00
|
|
|
expect(results.length).toEqual(1);
|
2016-12-07 15:17:05 -08:00
|
|
|
const deletedObject = results[0];
|
2016-11-24 15:47:41 -05:00
|
|
|
expect(deletedObject.get('proof')).toEqual(obj.id);
|
|
|
|
|
done();
|
|
|
|
|
}, errorHandler);
|
2018-09-01 13:58:06 -04:00
|
|
|
}, errorHandler);
|
2016-11-24 15:47:41 -05:00
|
|
|
}, errorHandler);
|
2016-08-17 15:26:42 +02:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test afterDelete ignoring promise, object is not yet deleted', function (done) {
|
|
|
|
|
Parse.Cloud.afterDelete('AfterDeleteTest2', function (req) {
|
2018-09-01 13:58:06 -04:00
|
|
|
return new Promise(resolve => {
|
2020-10-02 00:19:26 +02:00
|
|
|
setTimeout(function () {
|
2018-08-05 13:58:07 -04:00
|
|
|
const obj = new Parse.Object('AfterDeleteTestProof');
|
|
|
|
|
obj.set('proof', req.object.id);
|
2020-10-02 00:19:26 +02:00
|
|
|
obj.save().then(function () {
|
2018-08-05 13:58:07 -04:00
|
|
|
resolve();
|
|
|
|
|
});
|
|
|
|
|
}, 1000);
|
|
|
|
|
});
|
2016-11-24 15:47:41 -05:00
|
|
|
});
|
2016-08-17 15:26:42 +02:00
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
const errorHandler = function (error) {
|
2016-11-24 15:47:41 -05:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
2018-09-01 13:58:06 -04:00
|
|
|
};
|
2016-11-24 15:47:41 -05:00
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj = new Parse.Object('AfterDeleteTest2');
|
2020-10-02 00:19:26 +02:00
|
|
|
obj.save().then(function () {
|
|
|
|
|
obj.destroy().then(function () {
|
2016-11-24 15:47:41 -05:00
|
|
|
done();
|
2018-09-01 13:58:06 -04:00
|
|
|
});
|
2016-08-17 15:26:42 +02:00
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const query = new Parse.Query('AfterDeleteTestProof');
|
2016-11-24 15:47:41 -05:00
|
|
|
query.equalTo('proof', obj.id);
|
2020-10-02 00:19:26 +02:00
|
|
|
query.find().then(function (results) {
|
2016-11-24 15:47:41 -05:00
|
|
|
expect(results.length).toEqual(0);
|
2016-08-17 15:26:42 +02:00
|
|
|
}, errorHandler);
|
2016-11-24 15:47:41 -05:00
|
|
|
}, errorHandler);
|
2016-08-17 15:26:42 +02:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test beforeSave happens on update', function (done) {
|
|
|
|
|
Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
|
2016-05-19 13:38:16 -07:00
|
|
|
req.object.set('foo', 'baz');
|
|
|
|
|
});
|
|
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('BeforeSaveChanged');
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.set('foo', 'bar');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj
|
|
|
|
|
.save()
|
2020-10-02 00:19:26 +02:00
|
|
|
.then(function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.set('foo', 'bar');
|
|
|
|
|
return obj.save();
|
|
|
|
|
})
|
|
|
|
|
.then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
const query = new Parse.Query('BeforeSaveChanged');
|
2020-10-02 00:19:26 +02:00
|
|
|
return query.get(obj.id).then(function (objAgain) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(objAgain.get('foo')).toEqual('baz');
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test beforeDelete failure', function (done) {
|
|
|
|
|
Parse.Cloud.beforeDelete('BeforeDeleteFail', function () {
|
2018-08-06 17:39:38 -04:00
|
|
|
throw 'Nope';
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('BeforeDeleteFail');
|
|
|
|
|
let id;
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.set('foo', 'bar');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj
|
|
|
|
|
.save()
|
|
|
|
|
.then(() => {
|
|
|
|
|
id = obj.id;
|
|
|
|
|
return obj.destroy();
|
|
|
|
|
})
|
|
|
|
|
.then(
|
|
|
|
|
() => {
|
|
|
|
|
fail('obj.destroy() should have failed, but it succeeded');
|
|
|
|
|
done();
|
|
|
|
|
},
|
|
|
|
|
error => {
|
|
|
|
|
expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED);
|
|
|
|
|
expect(error.message).toEqual('Nope');
|
|
|
|
|
|
|
|
|
|
const objAgain = new Parse.Object('BeforeDeleteFail', {
|
|
|
|
|
objectId: id,
|
|
|
|
|
});
|
|
|
|
|
return objAgain.fetch();
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
.then(
|
|
|
|
|
objAgain => {
|
|
|
|
|
if (objAgain) {
|
|
|
|
|
expect(objAgain.get('foo')).toEqual('bar');
|
|
|
|
|
} else {
|
|
|
|
|
fail('unable to fetch the object ', id);
|
|
|
|
|
}
|
|
|
|
|
done();
|
|
|
|
|
},
|
|
|
|
|
error => {
|
|
|
|
|
// We should have been able to fetch the object again
|
|
|
|
|
fail(error);
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('basic beforeDelete rejection via promise', function (done) {
|
|
|
|
|
Parse.Cloud.beforeSave('BeforeDeleteFailWithPromise', function () {
|
2018-02-17 09:55:30 -05:00
|
|
|
const query = new Parse.Query('Yolo');
|
2018-08-06 17:39:38 -04:00
|
|
|
return query.find().then(() => {
|
|
|
|
|
throw 'Nope';
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('BeforeDeleteFailWithPromise');
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.set('foo', 'bar');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.save().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail('Should not have been able to save BeforeSaveFailure class.');
|
|
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED);
|
|
|
|
|
expect(error.message).toEqual('Nope');
|
2016-05-19 13:38:16 -07:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test afterDelete ran and created an object', function (done) {
|
|
|
|
|
Parse.Cloud.afterDelete('AfterDeleteTest', function (req) {
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('AfterDeleteProof');
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.set('proof', req.object.id);
|
2019-05-14 11:34:51 -07:00
|
|
|
obj.save().then(test);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('AfterDeleteTest');
|
2020-10-02 00:19:26 +02:00
|
|
|
obj.save().then(function () {
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.destroy();
|
|
|
|
|
});
|
|
|
|
|
|
2019-05-14 11:34:51 -07:00
|
|
|
function test() {
|
2018-02-17 09:55:30 -05:00
|
|
|
const query = new Parse.Query('AfterDeleteProof');
|
2016-05-19 13:38:16 -07:00
|
|
|
query.equalTo('proof', obj.id);
|
2018-09-01 13:58:06 -04:00
|
|
|
query.find().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function (results) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(results.length).toEqual(1);
|
|
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2019-05-14 11:34:51 -07:00
|
|
|
}
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test cloud function return types', function (done) {
|
|
|
|
|
Parse.Cloud.define('foo', function () {
|
2018-08-06 17:39:38 -04:00
|
|
|
return {
|
2016-05-19 13:38:16 -07:00
|
|
|
object: {
|
|
|
|
|
__type: 'Object',
|
|
|
|
|
className: 'Foo',
|
|
|
|
|
objectId: '123',
|
|
|
|
|
x: 2,
|
|
|
|
|
relation: {
|
|
|
|
|
__type: 'Object',
|
|
|
|
|
className: 'Bar',
|
|
|
|
|
objectId: '234',
|
2018-09-01 13:58:06 -04:00
|
|
|
x: 3,
|
|
|
|
|
},
|
2016-05-19 13:38:16 -07:00
|
|
|
},
|
2018-09-01 13:58:06 -04:00
|
|
|
array: [
|
|
|
|
|
{
|
|
|
|
|
__type: 'Object',
|
|
|
|
|
className: 'Bar',
|
|
|
|
|
objectId: '345',
|
|
|
|
|
x: 2,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
a: 2,
|
2018-08-06 17:39:38 -04:00
|
|
|
};
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.run('foo').then(result => {
|
2016-05-19 13:38:16 -07:00
|
|
|
expect(result.object instanceof Parse.Object).toBeTruthy();
|
|
|
|
|
if (!result.object) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail('Unable to run foo');
|
2016-05-19 13:38:16 -07:00
|
|
|
done();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
expect(result.object.className).toEqual('Foo');
|
|
|
|
|
expect(result.object.get('x')).toEqual(2);
|
2018-02-17 09:55:30 -05:00
|
|
|
const bar = result.object.get('relation');
|
2016-05-19 13:38:16 -07:00
|
|
|
expect(bar instanceof Parse.Object).toBeTruthy();
|
|
|
|
|
expect(bar.className).toEqual('Bar');
|
|
|
|
|
expect(bar.get('x')).toEqual(3);
|
|
|
|
|
expect(Array.isArray(result.array)).toEqual(true);
|
|
|
|
|
expect(result.array[0] instanceof Parse.Object).toBeTruthy();
|
|
|
|
|
expect(result.array[0].get('x')).toEqual(2);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test cloud function request params types', function (done) {
|
|
|
|
|
Parse.Cloud.define('params', function (req) {
|
2016-06-10 03:37:05 +08:00
|
|
|
expect(req.params.date instanceof Date).toBe(true);
|
|
|
|
|
expect(req.params.date.getTime()).toBe(1463907600000);
|
|
|
|
|
expect(req.params.dateList[0] instanceof Date).toBe(true);
|
|
|
|
|
expect(req.params.dateList[0].getTime()).toBe(1463907600000);
|
|
|
|
|
expect(req.params.complexStructure.date[0] instanceof Date).toBe(true);
|
|
|
|
|
expect(req.params.complexStructure.date[0].getTime()).toBe(1463907600000);
|
2020-10-25 15:06:58 -05:00
|
|
|
expect(req.params.complexStructure.deepDate.date[0] instanceof Date).toBe(true);
|
|
|
|
|
expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe(1463907600000);
|
|
|
|
|
expect(req.params.complexStructure.deepDate2[0].date instanceof Date).toBe(true);
|
|
|
|
|
expect(req.params.complexStructure.deepDate2[0].date.getTime()).toBe(1463907600000);
|
2016-07-19 02:14:32 -04:00
|
|
|
// Regression for #2294
|
|
|
|
|
expect(req.params.file instanceof Parse.File).toBe(true);
|
|
|
|
|
expect(req.params.file.url()).toEqual('https://some.url');
|
|
|
|
|
// Regression for #2204
|
|
|
|
|
expect(req.params.array).toEqual(['a', 'b', 'c']);
|
|
|
|
|
expect(Array.isArray(req.params.array)).toBe(true);
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(req.params.arrayOfArray).toEqual([
|
|
|
|
|
['a', 'b', 'c'],
|
|
|
|
|
['d', 'e', 'f'],
|
|
|
|
|
]);
|
2016-07-19 02:14:32 -04:00
|
|
|
expect(Array.isArray(req.params.arrayOfArray)).toBe(true);
|
|
|
|
|
expect(Array.isArray(req.params.arrayOfArray[0])).toBe(true);
|
|
|
|
|
expect(Array.isArray(req.params.arrayOfArray[1])).toBe(true);
|
2018-08-06 17:39:38 -04:00
|
|
|
return {};
|
2016-06-10 03:37:05 +08:00
|
|
|
});
|
|
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const params = {
|
2018-09-01 13:58:06 -04:00
|
|
|
date: {
|
|
|
|
|
__type: 'Date',
|
|
|
|
|
iso: '2016-05-22T09:00:00.000Z',
|
2016-06-10 03:37:05 +08:00
|
|
|
},
|
2018-09-01 13:58:06 -04:00
|
|
|
dateList: [
|
2016-06-10 03:37:05 +08:00
|
|
|
{
|
2018-09-01 13:58:06 -04:00
|
|
|
__type: 'Date',
|
|
|
|
|
iso: '2016-05-22T09:00:00.000Z',
|
|
|
|
|
},
|
2016-06-10 03:37:05 +08:00
|
|
|
],
|
2018-09-01 13:58:06 -04:00
|
|
|
lol: 'hello',
|
|
|
|
|
complexStructure: {
|
|
|
|
|
date: [
|
2016-06-10 03:37:05 +08:00
|
|
|
{
|
2018-09-01 13:58:06 -04:00
|
|
|
__type: 'Date',
|
|
|
|
|
iso: '2016-05-22T09:00:00.000Z',
|
|
|
|
|
},
|
2016-06-10 03:37:05 +08:00
|
|
|
],
|
2018-09-01 13:58:06 -04:00
|
|
|
deepDate: {
|
|
|
|
|
date: [
|
2016-06-10 03:37:05 +08:00
|
|
|
{
|
2018-09-01 13:58:06 -04:00
|
|
|
__type: 'Date',
|
|
|
|
|
iso: '2016-05-22T09:00:00.000Z',
|
|
|
|
|
},
|
|
|
|
|
],
|
2016-06-10 03:37:05 +08:00
|
|
|
},
|
2018-09-01 13:58:06 -04:00
|
|
|
deepDate2: [
|
2016-06-10 03:37:05 +08:00
|
|
|
{
|
2018-09-01 13:58:06 -04:00
|
|
|
date: {
|
|
|
|
|
__type: 'Date',
|
|
|
|
|
iso: '2016-05-22T09:00:00.000Z',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
2016-07-19 02:14:32 -04:00
|
|
|
},
|
2018-09-01 13:58:06 -04:00
|
|
|
file: Parse.File.fromJSON({
|
2016-07-19 02:14:32 -04:00
|
|
|
__type: 'File',
|
|
|
|
|
name: 'name',
|
2018-09-01 13:58:06 -04:00
|
|
|
url: 'https://some.url',
|
2016-07-19 02:14:32 -04:00
|
|
|
}),
|
2018-09-01 13:58:06 -04:00
|
|
|
array: ['a', 'b', 'c'],
|
2019-11-23 11:38:13 -06:00
|
|
|
arrayOfArray: [
|
|
|
|
|
['a', 'b', 'c'],
|
|
|
|
|
['d', 'e', 'f'],
|
|
|
|
|
],
|
2016-06-10 03:37:05 +08:00
|
|
|
};
|
2016-11-24 15:47:41 -05:00
|
|
|
Parse.Cloud.run('params', params).then(() => {
|
2016-06-10 03:37:05 +08:00
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test cloud function should echo keys', function (done) {
|
|
|
|
|
Parse.Cloud.define('echoKeys', function () {
|
2018-08-06 17:39:38 -04:00
|
|
|
return {
|
2016-05-19 13:38:16 -07:00
|
|
|
applicationId: Parse.applicationId,
|
|
|
|
|
masterKey: Parse.masterKey,
|
2018-09-01 13:58:06 -04:00
|
|
|
javascriptKey: Parse.javascriptKey,
|
2018-08-06 17:39:38 -04:00
|
|
|
};
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.run('echoKeys').then(result => {
|
2016-05-19 13:38:16 -07:00
|
|
|
expect(result.applicationId).toEqual(Parse.applicationId);
|
|
|
|
|
expect(result.masterKey).toEqual(Parse.masterKey);
|
|
|
|
|
expect(result.javascriptKey).toEqual(Parse.javascriptKey);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should properly create an object in before save', done => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
|
2016-05-19 13:38:16 -07:00
|
|
|
req.object.set('foo', 'baz');
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.define('createBeforeSaveChangedObject', function () {
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('BeforeSaveChanged');
|
2018-08-06 17:39:38 -04:00
|
|
|
return obj.save().then(() => {
|
|
|
|
|
return obj;
|
2018-09-01 13:58:06 -04:00
|
|
|
});
|
|
|
|
|
});
|
2016-05-19 13:38:16 -07:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.run('createBeforeSaveChangedObject').then(res => {
|
2016-05-19 13:38:16 -07:00
|
|
|
expect(res.get('foo')).toEqual('baz');
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('dirtyKeys are set on update', done => {
|
|
|
|
|
let triggerTime = 0;
|
|
|
|
|
// Register a mock beforeSave hook
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.beforeSave('GameScore', req => {
|
2018-02-17 09:55:30 -05:00
|
|
|
const object = req.object;
|
2016-05-19 13:38:16 -07:00
|
|
|
expect(object instanceof Parse.Object).toBeTruthy();
|
|
|
|
|
expect(object.get('fooAgain')).toEqual('barAgain');
|
|
|
|
|
if (triggerTime == 0) {
|
|
|
|
|
// Create
|
|
|
|
|
expect(object.get('foo')).toEqual('bar');
|
|
|
|
|
} else if (triggerTime == 1) {
|
|
|
|
|
// Update
|
|
|
|
|
expect(object.dirtyKeys()).toEqual(['foo']);
|
|
|
|
|
expect(object.dirty('foo')).toBeTruthy();
|
|
|
|
|
expect(object.get('foo')).toEqual('baz');
|
|
|
|
|
} else {
|
2018-08-06 17:39:38 -04:00
|
|
|
throw new Error();
|
2016-05-19 13:38:16 -07:00
|
|
|
}
|
|
|
|
|
triggerTime++;
|
|
|
|
|
});
|
|
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj = new Parse.Object('GameScore');
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.set('foo', 'bar');
|
|
|
|
|
obj.set('fooAgain', 'barAgain');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj
|
|
|
|
|
.save()
|
|
|
|
|
.then(() => {
|
|
|
|
|
// We only update foo
|
|
|
|
|
obj.set('foo', 'baz');
|
|
|
|
|
return obj.save();
|
|
|
|
|
})
|
|
|
|
|
.then(
|
|
|
|
|
() => {
|
|
|
|
|
// Make sure the checking has been triggered
|
|
|
|
|
expect(triggerTime).toBe(2);
|
|
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test beforeSave unchanged success', function (done) {
|
|
|
|
|
Parse.Cloud.beforeSave('BeforeSaveUnchanged', function () {
|
2018-08-06 17:39:38 -04:00
|
|
|
return;
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('BeforeSaveUnchanged');
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.set('foo', 'bar');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.save().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('test beforeDelete success', function (done) {
|
|
|
|
|
Parse.Cloud.beforeDelete('BeforeDeleteTest', function () {
|
2018-08-06 17:39:38 -04:00
|
|
|
return;
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const obj = new Parse.Object('BeforeDeleteTest');
|
2016-05-19 13:38:16 -07:00
|
|
|
obj.set('foo', 'bar');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj
|
|
|
|
|
.save()
|
2020-10-02 00:19:26 +02:00
|
|
|
.then(function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
return obj.destroy();
|
|
|
|
|
})
|
|
|
|
|
.then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
const objAgain = new Parse.Object('BeforeDeleteTest', obj.id);
|
2018-09-08 20:05:32 -04:00
|
|
|
return objAgain.fetch().then(fail, () => done());
|
2018-09-01 13:58:06 -04:00
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('test save triggers get user', async done => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('SaveTriggerUser', function (req) {
|
2016-05-19 13:38:16 -07:00
|
|
|
if (req.user && req.user.id) {
|
2018-08-06 17:39:38 -04:00
|
|
|
return;
|
2016-05-19 13:38:16 -07:00
|
|
|
} else {
|
2018-08-06 17:39:38 -04:00
|
|
|
throw new Error('No user present on request object for beforeSave.');
|
2016-05-19 13:38:16 -07:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterSave('SaveTriggerUser', function (req) {
|
2016-05-19 13:38:16 -07:00
|
|
|
if (!req.user || !req.user.id) {
|
|
|
|
|
console.log('No user present on request object for afterSave.');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const user = new Parse.User();
|
2018-09-01 13:58:06 -04:00
|
|
|
user.set('password', 'asdf');
|
|
|
|
|
user.set('email', 'asdf@example.com');
|
|
|
|
|
user.set('username', 'zxcv');
|
2018-08-05 13:58:07 -04:00
|
|
|
await user.signUp();
|
|
|
|
|
const obj = new Parse.Object('SaveTriggerUser');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.save().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('beforeSave change propagates through the save response', done => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('ChangingObject', function (request) {
|
2016-05-19 13:38:16 -07:00
|
|
|
request.object.set('foo', 'baz');
|
|
|
|
|
});
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj = new Parse.Object('ChangingObject');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.save({ foo: 'bar' }).then(
|
|
|
|
|
objAgain => {
|
|
|
|
|
expect(objAgain.get('foo')).toEqual('baz');
|
|
|
|
|
done();
|
|
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
fail('Should not have failed to save.');
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
2016-12-01 07:04:47 -08:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('beforeSave change propagates through the afterSave #1931', done => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('ChangingObject', function (request) {
|
2016-09-24 13:53:04 -04:00
|
|
|
request.object.unset('file');
|
|
|
|
|
request.object.unset('date');
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterSave('ChangingObject', function (request) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(request.object.has('file')).toBe(false);
|
|
|
|
|
expect(request.object.has('date')).toBe(false);
|
2016-09-24 13:53:04 -04:00
|
|
|
expect(request.object.get('file')).toBeUndefined();
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
});
|
2018-09-01 13:58:06 -04:00
|
|
|
const file = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain');
|
|
|
|
|
file
|
|
|
|
|
.save()
|
|
|
|
|
.then(() => {
|
|
|
|
|
const obj = new Parse.Object('ChangingObject');
|
|
|
|
|
return obj.save({ file, date: new Date() });
|
|
|
|
|
})
|
|
|
|
|
.then(
|
|
|
|
|
() => {
|
|
|
|
|
done();
|
|
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
fail();
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-09-24 13:53:04 -04:00
|
|
|
});
|
2016-05-19 13:38:16 -07:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('test cloud function parameter validation success', done => {
|
2016-05-19 13:38:16 -07:00
|
|
|
// Register a function with validation
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.define(
|
|
|
|
|
'functionWithParameterValidation',
|
|
|
|
|
() => {
|
|
|
|
|
return 'works';
|
|
|
|
|
},
|
|
|
|
|
request => {
|
|
|
|
|
return request.params.success === 100;
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.run('functionWithParameterValidation', { success: 100 }).then(
|
|
|
|
|
() => {
|
|
|
|
|
done();
|
|
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
fail('Validation should not have failed.');
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
2016-05-22 09:59:36 -07:00
|
|
|
|
|
|
|
|
it('doesnt receive stale user in cloud code functions after user has been updated with master key (regression test for #1836)', done => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.define('testQuery', function (request) {
|
2018-08-06 17:39:38 -04:00
|
|
|
return request.user.get('data');
|
2016-05-22 09:59:36 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Parse.User.signUp('user', 'pass')
|
2017-06-20 09:15:26 -07:00
|
|
|
.then(user => {
|
|
|
|
|
user.set('data', 'AAA');
|
|
|
|
|
return user.save();
|
|
|
|
|
})
|
|
|
|
|
.then(() => Parse.Cloud.run('testQuery'))
|
|
|
|
|
.then(result => {
|
|
|
|
|
expect(result).toEqual('AAA');
|
|
|
|
|
Parse.User.current().set('data', 'BBB');
|
2018-09-01 13:58:06 -04:00
|
|
|
return Parse.User.current().save(null, { useMasterKey: true });
|
2017-06-20 09:15:26 -07:00
|
|
|
})
|
|
|
|
|
.then(() => Parse.Cloud.run('testQuery'))
|
|
|
|
|
.then(result => {
|
|
|
|
|
expect(result).toEqual('BBB');
|
|
|
|
|
done();
|
|
|
|
|
});
|
2016-05-22 09:59:36 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('clears out the user cache for all sessions when the user is changed', done => {
|
2016-06-10 20:27:21 -07:00
|
|
|
let session1;
|
|
|
|
|
let session2;
|
|
|
|
|
let user;
|
2016-05-22 09:59:36 -07:00
|
|
|
const cacheAdapter = new InMemoryCacheAdapter({ ttl: 100000000 });
|
2016-06-10 20:27:21 -07:00
|
|
|
reconfigureServer({ cacheAdapter })
|
2017-06-20 09:15:26 -07:00
|
|
|
.then(() => {
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.define('checkStaleUser', request => {
|
2018-08-06 17:39:38 -04:00
|
|
|
return request.user.get('data');
|
2017-06-20 09:15:26 -07:00
|
|
|
});
|
2016-05-22 09:59:36 -07:00
|
|
|
|
2017-06-20 09:15:26 -07:00
|
|
|
user = new Parse.User();
|
|
|
|
|
user.set('username', 'test');
|
|
|
|
|
user.set('password', 'moon-y');
|
|
|
|
|
user.set('data', 'first data');
|
|
|
|
|
return user.signUp();
|
|
|
|
|
})
|
|
|
|
|
.then(user => {
|
|
|
|
|
session1 = user.getSessionToken();
|
2018-09-24 17:07:51 -04:00
|
|
|
return request({
|
|
|
|
|
url: 'http://localhost:8378/1/login?username=test&password=moon-y',
|
2017-06-20 09:15:26 -07:00
|
|
|
headers: {
|
|
|
|
|
'X-Parse-Application-Id': 'test',
|
|
|
|
|
'X-Parse-REST-API-Key': 'rest',
|
|
|
|
|
},
|
2018-09-01 13:58:06 -04:00
|
|
|
});
|
2017-06-20 09:15:26 -07:00
|
|
|
})
|
2018-09-24 17:07:51 -04:00
|
|
|
.then(response => {
|
|
|
|
|
session2 = response.data.sessionToken;
|
2018-09-01 13:58:06 -04:00
|
|
|
//Ensure both session tokens are in the cache
|
2018-09-24 17:07:51 -04:00
|
|
|
return Parse.Cloud.run('checkStaleUser', { sessionToken: session2 });
|
2018-09-01 13:58:06 -04:00
|
|
|
})
|
|
|
|
|
.then(() =>
|
2018-09-24 17:07:51 -04:00
|
|
|
request({
|
2018-09-01 13:58:06 -04:00
|
|
|
method: 'POST',
|
2018-09-24 17:07:51 -04:00
|
|
|
url: 'http://localhost:8378/1/functions/checkStaleUser',
|
2018-09-01 13:58:06 -04:00
|
|
|
headers: {
|
|
|
|
|
'X-Parse-Application-Id': 'test',
|
|
|
|
|
'X-Parse-REST-API-Key': 'rest',
|
|
|
|
|
'X-Parse-Session-Token': session2,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
.then(() =>
|
|
|
|
|
Promise.all([
|
|
|
|
|
cacheAdapter.get('test:user:' + session1),
|
|
|
|
|
cacheAdapter.get('test:user:' + session2),
|
|
|
|
|
])
|
|
|
|
|
)
|
2017-06-20 09:15:26 -07:00
|
|
|
.then(cachedVals => {
|
|
|
|
|
expect(cachedVals[0].objectId).toEqual(user.id);
|
|
|
|
|
expect(cachedVals[1].objectId).toEqual(user.id);
|
|
|
|
|
|
|
|
|
|
//Change with session 1 and then read with session 2.
|
|
|
|
|
user.set('data', 'second data');
|
2018-09-01 13:58:06 -04:00
|
|
|
return user.save();
|
2016-06-10 20:27:21 -07:00
|
|
|
})
|
2018-09-01 13:58:06 -04:00
|
|
|
.then(() =>
|
2018-09-24 17:07:51 -04:00
|
|
|
request({
|
2018-09-01 13:58:06 -04:00
|
|
|
method: 'POST',
|
2018-09-24 17:07:51 -04:00
|
|
|
url: 'http://localhost:8378/1/functions/checkStaleUser',
|
2018-09-01 13:58:06 -04:00
|
|
|
headers: {
|
|
|
|
|
'X-Parse-Application-Id': 'test',
|
|
|
|
|
'X-Parse-REST-API-Key': 'rest',
|
|
|
|
|
'X-Parse-Session-Token': session2,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
)
|
2018-09-24 17:07:51 -04:00
|
|
|
.then(response => {
|
|
|
|
|
expect(response.data.result).toEqual('second data');
|
2017-06-20 09:15:26 -07:00
|
|
|
done();
|
|
|
|
|
})
|
2018-10-17 17:53:49 -04:00
|
|
|
.catch(done.fail);
|
2016-05-22 09:59:36 -07:00
|
|
|
});
|
2016-05-23 17:13:32 -07:00
|
|
|
|
|
|
|
|
it('trivial beforeSave should not affect fetched pointers (regression test for #1238)', done => {
|
2018-08-06 17:39:38 -04:00
|
|
|
Parse.Cloud.beforeSave('BeforeSaveUnchanged', () => {});
|
2016-05-23 17:13:32 -07:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
const TestObject = Parse.Object.extend('TestObject');
|
|
|
|
|
const NoBeforeSaveObject = Parse.Object.extend('NoBeforeSave');
|
|
|
|
|
const BeforeSaveObject = Parse.Object.extend('BeforeSaveUnchanged');
|
2016-05-23 17:13:32 -07:00
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const aTestObject = new TestObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
aTestObject.set('foo', 'bar');
|
|
|
|
|
aTestObject
|
|
|
|
|
.save()
|
2017-06-20 09:15:26 -07:00
|
|
|
.then(aTestObject => {
|
2018-02-17 09:55:30 -05:00
|
|
|
const aNoBeforeSaveObj = new NoBeforeSaveObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
aNoBeforeSaveObj.set('aTestObject', aTestObject);
|
|
|
|
|
expect(aNoBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar');
|
2017-06-20 09:15:26 -07:00
|
|
|
return aNoBeforeSaveObj.save();
|
|
|
|
|
})
|
|
|
|
|
.then(aNoBeforeSaveObj => {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(aNoBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar');
|
2016-05-23 17:13:32 -07:00
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const aBeforeSaveObj = new BeforeSaveObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
aBeforeSaveObj.set('aTestObject', aTestObject);
|
|
|
|
|
expect(aBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar');
|
2017-06-20 09:15:26 -07:00
|
|
|
return aBeforeSaveObj.save();
|
|
|
|
|
})
|
|
|
|
|
.then(aBeforeSaveObj => {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(aBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar');
|
2017-06-20 09:15:26 -07:00
|
|
|
done();
|
|
|
|
|
});
|
2016-05-23 17:13:32 -07:00
|
|
|
});
|
2016-07-15 09:24:53 -04:00
|
|
|
|
2016-11-24 15:47:41 -05:00
|
|
|
it('beforeSave should not affect fetched pointers', done => {
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.beforeSave('BeforeSaveUnchanged', () => {});
|
2016-08-05 18:23:54 +05:30
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
|
2016-08-05 18:23:54 +05:30
|
|
|
req.object.set('foo', 'baz');
|
|
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
const TestObject = Parse.Object.extend('TestObject');
|
2020-10-25 15:06:58 -05:00
|
|
|
const BeforeSaveUnchangedObject = Parse.Object.extend('BeforeSaveUnchanged');
|
2018-09-01 13:58:06 -04:00
|
|
|
const BeforeSaveChangedObject = Parse.Object.extend('BeforeSaveChanged');
|
2016-08-05 18:23:54 +05:30
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const aTestObject = new TestObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
aTestObject.set('foo', 'bar');
|
|
|
|
|
aTestObject
|
|
|
|
|
.save()
|
2017-06-20 09:15:26 -07:00
|
|
|
.then(aTestObject => {
|
2018-02-17 09:55:30 -05:00
|
|
|
const aBeforeSaveUnchangedObject = new BeforeSaveUnchangedObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
aBeforeSaveUnchangedObject.set('aTestObject', aTestObject);
|
2020-10-25 15:06:58 -05:00
|
|
|
expect(aBeforeSaveUnchangedObject.get('aTestObject').get('foo')).toEqual('bar');
|
2017-06-20 09:15:26 -07:00
|
|
|
return aBeforeSaveUnchangedObject.save();
|
|
|
|
|
})
|
|
|
|
|
.then(aBeforeSaveUnchangedObject => {
|
2020-10-25 15:06:58 -05:00
|
|
|
expect(aBeforeSaveUnchangedObject.get('aTestObject').get('foo')).toEqual('bar');
|
2016-08-05 18:23:54 +05:30
|
|
|
|
2018-02-17 09:55:30 -05:00
|
|
|
const aBeforeSaveChangedObject = new BeforeSaveChangedObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
aBeforeSaveChangedObject.set('aTestObject', aTestObject);
|
2020-10-25 15:06:58 -05:00
|
|
|
expect(aBeforeSaveChangedObject.get('aTestObject').get('foo')).toEqual('bar');
|
2017-06-20 09:15:26 -07:00
|
|
|
return aBeforeSaveChangedObject.save();
|
|
|
|
|
})
|
|
|
|
|
.then(aBeforeSaveChangedObject => {
|
2020-10-25 15:06:58 -05:00
|
|
|
expect(aBeforeSaveChangedObject.get('aTestObject').get('foo')).toEqual('bar');
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(aBeforeSaveChangedObject.get('foo')).toEqual('baz');
|
2017-06-20 09:15:26 -07:00
|
|
|
done();
|
|
|
|
|
});
|
2016-08-05 18:23:54 +05:30
|
|
|
});
|
|
|
|
|
|
2016-08-15 16:48:39 -04:00
|
|
|
it('should fully delete objects when using `unset` with beforeSave (regression test for #1840)', done => {
|
2018-02-17 09:55:30 -05:00
|
|
|
const TestObject = Parse.Object.extend('TestObject');
|
|
|
|
|
const NoBeforeSaveObject = Parse.Object.extend('NoBeforeSave');
|
|
|
|
|
const BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged');
|
2016-07-15 09:24:53 -04:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.beforeSave('BeforeSaveChanged', req => {
|
2018-02-17 09:55:30 -05:00
|
|
|
const object = req.object;
|
2016-07-15 09:24:53 -04:00
|
|
|
object.set('before', 'save');
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-25 22:41:23 -05:00
|
|
|
Parse.Cloud.define('removeme', () => {
|
2018-02-17 09:55:30 -05:00
|
|
|
const testObject = new TestObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
return testObject
|
|
|
|
|
.save()
|
2017-06-20 09:15:26 -07:00
|
|
|
.then(testObject => {
|
2018-09-01 13:58:06 -04:00
|
|
|
const object = new NoBeforeSaveObject({ remove: testObject });
|
2017-06-20 09:15:26 -07:00
|
|
|
return object.save();
|
|
|
|
|
})
|
|
|
|
|
.then(object => {
|
|
|
|
|
object.unset('remove');
|
|
|
|
|
return object.save();
|
2020-10-25 22:41:23 -05:00
|
|
|
});
|
2016-07-15 09:24:53 -04:00
|
|
|
});
|
|
|
|
|
|
2020-10-25 22:41:23 -05:00
|
|
|
Parse.Cloud.define('removeme2', () => {
|
2018-02-17 09:55:30 -05:00
|
|
|
const testObject = new TestObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
return testObject
|
|
|
|
|
.save()
|
2017-06-20 09:15:26 -07:00
|
|
|
.then(testObject => {
|
2018-09-01 13:58:06 -04:00
|
|
|
const object = new BeforeSaveObject({ remove: testObject });
|
2017-06-20 09:15:26 -07:00
|
|
|
return object.save();
|
|
|
|
|
})
|
|
|
|
|
.then(object => {
|
|
|
|
|
object.unset('remove');
|
|
|
|
|
return object.save();
|
2020-10-25 22:41:23 -05:00
|
|
|
});
|
2016-07-15 09:24:53 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Parse.Cloud.run('removeme')
|
2017-06-20 09:15:26 -07:00
|
|
|
.then(aNoBeforeSaveObj => {
|
|
|
|
|
expect(aNoBeforeSaveObj.get('remove')).toEqual(undefined);
|
2016-07-15 09:24:53 -04:00
|
|
|
|
2017-06-20 09:15:26 -07:00
|
|
|
return Parse.Cloud.run('removeme2');
|
|
|
|
|
})
|
|
|
|
|
.then(aBeforeSaveObj => {
|
|
|
|
|
expect(aBeforeSaveObj.get('before')).toEqual('save');
|
|
|
|
|
expect(aBeforeSaveObj.get('remove')).toEqual(undefined);
|
|
|
|
|
done();
|
2018-09-01 13:58:06 -04:00
|
|
|
})
|
|
|
|
|
.catch(err => {
|
2017-06-20 09:15:26 -07:00
|
|
|
jfail(err);
|
|
|
|
|
done();
|
|
|
|
|
});
|
2016-07-15 09:24:53 -04:00
|
|
|
});
|
|
|
|
|
|
2016-09-09 14:41:03 -04:00
|
|
|
/*
|
|
|
|
|
TODO: fix for Postgres
|
|
|
|
|
trying to delete a field that doesn't exists doesn't play nice
|
|
|
|
|
*/
|
2018-09-01 13:58:06 -04:00
|
|
|
it_exclude_dbs(['postgres'])(
|
|
|
|
|
'should fully delete objects when using `unset` and `set` with beforeSave (regression test for #1840)',
|
|
|
|
|
done => {
|
|
|
|
|
const TestObject = Parse.Object.extend('TestObject');
|
|
|
|
|
const BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged');
|
|
|
|
|
|
|
|
|
|
Parse.Cloud.beforeSave('BeforeSaveChanged', req => {
|
|
|
|
|
const object = req.object;
|
|
|
|
|
object.set('before', 'save');
|
|
|
|
|
object.unset('remove');
|
2016-11-24 15:47:41 -05:00
|
|
|
});
|
2018-09-01 13:58:06 -04:00
|
|
|
|
|
|
|
|
let object;
|
|
|
|
|
const testObject = new TestObject({ key: 'value' });
|
|
|
|
|
testObject
|
|
|
|
|
.save()
|
|
|
|
|
.then(() => {
|
|
|
|
|
object = new BeforeSaveObject();
|
|
|
|
|
return object.save().then(() => {
|
|
|
|
|
object.set({ remove: testObject });
|
|
|
|
|
return object.save();
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.then(objectAgain => {
|
|
|
|
|
expect(objectAgain.get('remove')).toBeUndefined();
|
|
|
|
|
expect(object.get('remove')).toBeUndefined();
|
|
|
|
|
done();
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
jfail(err);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-07-15 09:24:53 -04:00
|
|
|
|
2016-08-15 16:48:39 -04:00
|
|
|
it('should not include relation op (regression test for #1606)', done => {
|
2018-02-17 09:55:30 -05:00
|
|
|
const TestObject = Parse.Object.extend('TestObject');
|
|
|
|
|
const BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged');
|
2016-07-15 09:24:53 -04:00
|
|
|
let testObj;
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.beforeSave('BeforeSaveChanged', req => {
|
2018-02-17 09:55:30 -05:00
|
|
|
const object = req.object;
|
2016-07-15 09:24:53 -04:00
|
|
|
object.set('before', 'save');
|
|
|
|
|
testObj = new TestObject();
|
2018-08-06 17:39:38 -04:00
|
|
|
return testObj.save().then(() => {
|
2016-07-15 09:24:53 -04:00
|
|
|
object.relation('testsRelation').add(testObj);
|
2018-08-06 17:39:38 -04:00
|
|
|
});
|
2016-07-15 09:24:53 -04:00
|
|
|
});
|
|
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const object = new BeforeSaveObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
object
|
|
|
|
|
.save()
|
|
|
|
|
.then(objectAgain => {
|
|
|
|
|
// Originally it would throw as it would be a non-relation
|
|
|
|
|
expect(() => {
|
|
|
|
|
objectAgain.relation('testsRelation');
|
|
|
|
|
}).not.toThrow();
|
|
|
|
|
done();
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
jfail(err);
|
|
|
|
|
done();
|
|
|
|
|
});
|
2016-07-15 09:24:53 -04:00
|
|
|
});
|
2016-08-30 07:19:21 -04:00
|
|
|
|
2017-10-15 21:15:30 -07:00
|
|
|
/**
|
|
|
|
|
* Checks that incrementing a value to a zero in a beforeSave hook
|
|
|
|
|
* does not result in that key being omitted from the response.
|
|
|
|
|
*/
|
2018-09-01 13:58:06 -04:00
|
|
|
it('before save increment does not return undefined', done => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.define('cloudIncrementClassFunction', function (req) {
|
2018-09-01 13:58:06 -04:00
|
|
|
const CloudIncrementClass = Parse.Object.extend('CloudIncrementClass');
|
2017-10-15 21:15:30 -07:00
|
|
|
const obj = new CloudIncrementClass();
|
|
|
|
|
obj.id = req.params.objectId;
|
2018-08-06 17:39:38 -04:00
|
|
|
return obj.save();
|
2017-10-15 21:15:30 -07:00
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('CloudIncrementClass', function (req) {
|
2017-10-15 21:15:30 -07:00
|
|
|
const obj = req.object;
|
2018-09-01 13:58:06 -04:00
|
|
|
if (!req.master) {
|
2017-10-15 21:15:30 -07:00
|
|
|
obj.increment('points', -10);
|
|
|
|
|
obj.increment('num', -9);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const CloudIncrementClass = Parse.Object.extend('CloudIncrementClass');
|
|
|
|
|
const obj = new CloudIncrementClass();
|
|
|
|
|
obj.set('points', 10);
|
|
|
|
|
obj.set('num', 10);
|
2020-10-02 00:19:26 +02:00
|
|
|
obj.save(null, { useMasterKey: true }).then(function () {
|
2020-10-25 15:06:58 -05:00
|
|
|
Parse.Cloud.run('cloudIncrementClassFunction', { objectId: obj.id }).then(function (
|
|
|
|
|
savedObj
|
|
|
|
|
) {
|
|
|
|
|
expect(savedObj.get('num')).toEqual(1);
|
|
|
|
|
expect(savedObj.get('points')).toEqual(0);
|
|
|
|
|
done();
|
|
|
|
|
});
|
2018-09-01 13:58:06 -04:00
|
|
|
});
|
2017-10-15 21:15:30 -07:00
|
|
|
});
|
|
|
|
|
|
2020-11-11 19:39:25 -06:00
|
|
|
it('beforeSave should not sanitize database', async done => {
|
2020-11-12 15:14:44 -06:00
|
|
|
const { adapter } = Config.get(Parse.applicationId).database;
|
|
|
|
|
const spy = spyOn(adapter, 'findOneAndUpdate').and.callThrough();
|
|
|
|
|
spy.calls.saveArgumentsByValue();
|
|
|
|
|
|
2020-11-11 19:39:25 -06:00
|
|
|
let count = 0;
|
2020-11-12 15:14:44 -06:00
|
|
|
Parse.Cloud.beforeSave('CloudIncrementNested', req => {
|
2020-11-11 19:39:25 -06:00
|
|
|
count += 1;
|
2020-11-12 15:14:44 -06:00
|
|
|
req.object.set('foo', 'baz');
|
|
|
|
|
expect(typeof req.object.get('objectField').number).toBe('number');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Parse.Cloud.afterSave('CloudIncrementNested', req => {
|
|
|
|
|
expect(typeof req.object.get('objectField').number).toBe('number');
|
2020-11-11 19:39:25 -06:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const obj = new Parse.Object('CloudIncrementNested');
|
|
|
|
|
obj.set('objectField', { number: 5 });
|
2020-11-12 15:14:44 -06:00
|
|
|
obj.set('foo', 'bar');
|
2020-11-11 19:39:25 -06:00
|
|
|
await obj.save();
|
|
|
|
|
|
|
|
|
|
obj.increment('objectField.number', 10);
|
|
|
|
|
await obj.save();
|
2020-11-12 15:14:44 -06:00
|
|
|
|
|
|
|
|
const [
|
|
|
|
|
,
|
|
|
|
|
,
|
|
|
|
|
,
|
|
|
|
|
/* className */ /* schema */ /* query */ update,
|
|
|
|
|
] = adapter.findOneAndUpdate.calls.first().args;
|
|
|
|
|
expect(update).toEqual({
|
|
|
|
|
'objectField.number': { __op: 'Increment', amount: 10 },
|
|
|
|
|
foo: 'baz',
|
|
|
|
|
updatedAt: obj.updatedAt.toISOString(),
|
|
|
|
|
});
|
|
|
|
|
|
2020-11-11 19:39:25 -06:00
|
|
|
count === 2 ? done() : fail();
|
|
|
|
|
});
|
|
|
|
|
|
2017-10-26 11:28:13 -07:00
|
|
|
/**
|
|
|
|
|
* Verifies that an afterSave hook throwing an exception
|
|
|
|
|
* will not prevent a successful save response from being returned
|
|
|
|
|
*/
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should succeed on afterSave exception', done => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterSave('AfterSaveTestClass', function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
throw 'Exception';
|
2017-10-26 11:28:13 -07:00
|
|
|
});
|
|
|
|
|
const AfterSaveTestClass = Parse.Object.extend('AfterSaveTestClass');
|
|
|
|
|
const obj = new AfterSaveTestClass();
|
|
|
|
|
obj.save().then(done, done.fail);
|
|
|
|
|
});
|
|
|
|
|
|
2016-11-24 15:47:41 -05:00
|
|
|
describe('cloud jobs', () => {
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should define a job', done => {
|
2016-11-24 15:47:41 -05:00
|
|
|
expect(() => {
|
2018-08-06 17:39:38 -04:00
|
|
|
Parse.Cloud.job('myJob', () => {});
|
2016-08-30 07:19:21 -04:00
|
|
|
}).not.toThrow();
|
2016-12-01 07:04:47 -08:00
|
|
|
|
2018-09-24 17:07:51 -04:00
|
|
|
request({
|
|
|
|
|
method: 'POST',
|
2016-08-30 07:19:21 -04:00
|
|
|
url: 'http://localhost:8378/1/jobs/myJob',
|
|
|
|
|
headers: {
|
|
|
|
|
'X-Parse-Application-Id': Parse.applicationId,
|
|
|
|
|
'X-Parse-Master-Key': Parse.masterKey,
|
|
|
|
|
},
|
2018-09-01 13:58:06 -04:00
|
|
|
}).then(
|
|
|
|
|
() => {
|
|
|
|
|
done();
|
|
|
|
|
},
|
|
|
|
|
err => {
|
|
|
|
|
fail(err);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-08-30 07:19:21 -04:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should not run without master key', done => {
|
2016-11-24 15:47:41 -05:00
|
|
|
expect(() => {
|
2018-08-06 17:39:38 -04:00
|
|
|
Parse.Cloud.job('myJob', () => {});
|
2016-08-30 07:19:21 -04:00
|
|
|
}).not.toThrow();
|
2016-12-01 07:04:47 -08:00
|
|
|
|
2018-09-24 17:07:51 -04:00
|
|
|
request({
|
|
|
|
|
method: 'POST',
|
2016-08-30 07:19:21 -04:00
|
|
|
url: 'http://localhost:8378/1/jobs/myJob',
|
|
|
|
|
headers: {
|
|
|
|
|
'X-Parse-Application-Id': Parse.applicationId,
|
|
|
|
|
'X-Parse-REST-API-Key': 'rest',
|
|
|
|
|
},
|
2018-09-01 13:58:06 -04:00
|
|
|
}).then(
|
|
|
|
|
() => {
|
|
|
|
|
fail('Expected to be unauthorized');
|
|
|
|
|
done();
|
|
|
|
|
},
|
|
|
|
|
err => {
|
2018-09-24 17:07:51 -04:00
|
|
|
expect(err.status).toBe(403);
|
2018-09-01 13:58:06 -04:00
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-08-30 07:19:21 -04:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should run with master key', done => {
|
2016-11-24 15:47:41 -05:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.job('myJob', (req, res) => {
|
2016-08-30 07:19:21 -04:00
|
|
|
expect(req.functionName).toBeUndefined();
|
|
|
|
|
expect(req.jobName).toBe('myJob');
|
|
|
|
|
expect(typeof req.jobId).toBe('string');
|
2018-08-06 17:39:38 -04:00
|
|
|
expect(typeof req.message).toBe('function');
|
|
|
|
|
expect(typeof res).toBe('undefined');
|
2016-08-30 07:19:21 -04:00
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
}).not.toThrow();
|
2016-12-01 07:04:47 -08:00
|
|
|
|
2018-09-24 17:07:51 -04:00
|
|
|
request({
|
|
|
|
|
method: 'POST',
|
2016-08-30 07:19:21 -04:00
|
|
|
url: 'http://localhost:8378/1/jobs/myJob',
|
|
|
|
|
headers: {
|
|
|
|
|
'X-Parse-Application-Id': Parse.applicationId,
|
|
|
|
|
'X-Parse-Master-Key': Parse.masterKey,
|
|
|
|
|
},
|
2018-09-01 13:58:06 -04:00
|
|
|
}).then(
|
|
|
|
|
() => {},
|
|
|
|
|
err => {
|
|
|
|
|
fail(err);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-08-30 07:19:21 -04:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should run with master key basic auth', done => {
|
2016-11-24 15:47:41 -05:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.job('myJob', (req, res) => {
|
2016-08-30 07:19:21 -04:00
|
|
|
expect(req.functionName).toBeUndefined();
|
|
|
|
|
expect(req.jobName).toBe('myJob');
|
|
|
|
|
expect(typeof req.jobId).toBe('string');
|
2018-08-06 17:39:38 -04:00
|
|
|
expect(typeof req.message).toBe('function');
|
|
|
|
|
expect(typeof res).toBe('undefined');
|
2016-08-30 07:19:21 -04:00
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
}).not.toThrow();
|
2016-12-01 07:04:47 -08:00
|
|
|
|
2018-09-24 17:07:51 -04:00
|
|
|
request({
|
|
|
|
|
method: 'POST',
|
2019-08-29 19:07:39 -07:00
|
|
|
url: `http://${Parse.applicationId}:${Parse.masterKey}@localhost:8378/1/jobs/myJob`,
|
2018-09-01 13:58:06 -04:00
|
|
|
}).then(
|
|
|
|
|
() => {},
|
|
|
|
|
err => {
|
|
|
|
|
fail(err);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-08-30 07:19:21 -04:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should set the message / success on the job', done => {
|
|
|
|
|
Parse.Cloud.job('myJob', req => {
|
|
|
|
|
const promise = req
|
2020-12-03 08:15:48 -08:00
|
|
|
.message('hello')
|
2018-09-01 13:58:06 -04:00
|
|
|
.then(() => {
|
|
|
|
|
return getJobStatus(req.jobId);
|
|
|
|
|
})
|
|
|
|
|
.then(jobStatus => {
|
|
|
|
|
expect(jobStatus.get('message')).toEqual('hello');
|
|
|
|
|
expect(jobStatus.get('status')).toEqual('running');
|
|
|
|
|
});
|
|
|
|
|
promise
|
|
|
|
|
.then(() => {
|
|
|
|
|
return getJobStatus(req.jobId);
|
|
|
|
|
})
|
|
|
|
|
.then(jobStatus => {
|
|
|
|
|
expect(jobStatus.get('message')).toEqual('hello');
|
|
|
|
|
expect(jobStatus.get('status')).toEqual('succeeded');
|
|
|
|
|
done();
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
console.error(err);
|
|
|
|
|
jfail(err);
|
|
|
|
|
done();
|
|
|
|
|
});
|
2018-08-06 17:39:38 -04:00
|
|
|
return promise;
|
2016-08-30 07:19:21 -04:00
|
|
|
});
|
2016-12-01 07:04:47 -08:00
|
|
|
|
2018-09-24 17:07:51 -04:00
|
|
|
request({
|
|
|
|
|
method: 'POST',
|
2016-08-30 07:19:21 -04:00
|
|
|
url: 'http://localhost:8378/1/jobs/myJob',
|
|
|
|
|
headers: {
|
|
|
|
|
'X-Parse-Application-Id': Parse.applicationId,
|
|
|
|
|
'X-Parse-Master-Key': Parse.masterKey,
|
|
|
|
|
},
|
2018-09-01 13:58:06 -04:00
|
|
|
}).then(
|
|
|
|
|
() => {},
|
|
|
|
|
err => {
|
|
|
|
|
fail(err);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-08-30 07:19:21 -04:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should set the failure on the job', done => {
|
|
|
|
|
Parse.Cloud.job('myJob', req => {
|
2018-08-06 17:39:38 -04:00
|
|
|
const promise = Promise.reject('Something went wrong');
|
2018-09-01 13:58:06 -04:00
|
|
|
new Promise(resolve => setTimeout(resolve, 200))
|
|
|
|
|
.then(() => {
|
|
|
|
|
return getJobStatus(req.jobId);
|
|
|
|
|
})
|
|
|
|
|
.then(jobStatus => {
|
|
|
|
|
expect(jobStatus.get('message')).toEqual('Something went wrong');
|
|
|
|
|
expect(jobStatus.get('status')).toEqual('failed');
|
|
|
|
|
done();
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
jfail(err);
|
|
|
|
|
done();
|
|
|
|
|
});
|
2018-08-06 17:39:38 -04:00
|
|
|
return promise;
|
2016-08-30 07:19:21 -04:00
|
|
|
});
|
2016-12-01 07:04:47 -08:00
|
|
|
|
2018-09-24 17:07:51 -04:00
|
|
|
request({
|
|
|
|
|
method: 'POST',
|
2016-08-30 07:19:21 -04:00
|
|
|
url: 'http://localhost:8378/1/jobs/myJob',
|
|
|
|
|
headers: {
|
|
|
|
|
'X-Parse-Application-Id': Parse.applicationId,
|
|
|
|
|
'X-Parse-Master-Key': Parse.masterKey,
|
|
|
|
|
},
|
2018-09-01 13:58:06 -04:00
|
|
|
}).then(
|
|
|
|
|
() => {},
|
|
|
|
|
err => {
|
|
|
|
|
fail(err);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-08-30 07:19:21 -04:00
|
|
|
});
|
|
|
|
|
|
2020-10-20 16:55:24 -05:00
|
|
|
it('should set the failure message on the job error', async () => {
|
|
|
|
|
Parse.Cloud.job('myJobError', () => {
|
|
|
|
|
throw new Parse.Error(101, 'Something went wrong');
|
|
|
|
|
});
|
|
|
|
|
const job = await Parse.Cloud.startJob('myJobError');
|
2020-12-03 08:15:48 -08:00
|
|
|
let jobStatus, status;
|
|
|
|
|
while (status !== 'failed') {
|
|
|
|
|
if (jobStatus) {
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 10));
|
|
|
|
|
}
|
|
|
|
|
jobStatus = await Parse.Cloud.getJobStatus(job);
|
|
|
|
|
status = jobStatus.get('status');
|
|
|
|
|
}
|
2020-10-20 16:55:24 -05:00
|
|
|
expect(jobStatus.get('message')).toEqual('Something went wrong');
|
|
|
|
|
});
|
|
|
|
|
|
2016-08-30 07:19:21 -04:00
|
|
|
function getJobStatus(jobId) {
|
2016-12-07 15:17:05 -08:00
|
|
|
const q = new Parse.Query('_JobStatus');
|
2018-09-01 13:58:06 -04:00
|
|
|
return q.get(jobId, { useMasterKey: true });
|
2016-08-30 07:19:21 -04:00
|
|
|
}
|
|
|
|
|
});
|
2016-05-19 13:38:16 -07:00
|
|
|
});
|
2016-09-17 16:52:35 -04:00
|
|
|
|
2017-10-18 14:13:09 +02:00
|
|
|
describe('cloud functions', () => {
|
2018-09-01 13:58:06 -04:00
|
|
|
it('Should have request ip', done => {
|
|
|
|
|
Parse.Cloud.define('myFunction', req => {
|
2017-10-18 14:13:09 +02:00
|
|
|
expect(req.ip).toBeDefined();
|
2018-09-01 13:58:06 -04:00
|
|
|
return 'success';
|
2017-10-18 14:13:09 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Parse.Cloud.run('myFunction', {}).then(() => done());
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2017-07-14 19:19:00 +02:00
|
|
|
describe('beforeSave hooks', () => {
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should have request headers', done => {
|
|
|
|
|
Parse.Cloud.beforeSave('MyObject', req => {
|
2017-07-14 19:19:00 +02:00
|
|
|
expect(req.headers).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const MyObject = Parse.Object.extend('MyObject');
|
|
|
|
|
const myObject = new MyObject();
|
|
|
|
|
myObject.save().then(() => done());
|
|
|
|
|
});
|
2017-10-18 14:13:09 +02:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should have request ip', done => {
|
|
|
|
|
Parse.Cloud.beforeSave('MyObject', req => {
|
2017-10-18 14:13:09 +02:00
|
|
|
expect(req.ip).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const MyObject = Parse.Object.extend('MyObject');
|
|
|
|
|
const myObject = new MyObject();
|
|
|
|
|
myObject.save().then(() => done());
|
|
|
|
|
});
|
2017-07-14 19:19:00 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('afterSave hooks', () => {
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should have request headers', done => {
|
|
|
|
|
Parse.Cloud.afterSave('MyObject', req => {
|
2017-07-14 19:19:00 +02:00
|
|
|
expect(req.headers).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const MyObject = Parse.Object.extend('MyObject');
|
|
|
|
|
const myObject = new MyObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
myObject.save().then(() => done());
|
2017-07-14 19:19:00 +02:00
|
|
|
});
|
2017-10-18 14:13:09 +02:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should have request ip', done => {
|
|
|
|
|
Parse.Cloud.afterSave('MyObject', req => {
|
2017-10-18 14:13:09 +02:00
|
|
|
expect(req.ip).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const MyObject = Parse.Object.extend('MyObject');
|
|
|
|
|
const myObject = new MyObject();
|
|
|
|
|
myObject.save().then(() => done());
|
|
|
|
|
});
|
2017-07-14 19:19:00 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('beforeDelete hooks', () => {
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should have request headers', done => {
|
|
|
|
|
Parse.Cloud.beforeDelete('MyObject', req => {
|
2017-07-14 19:19:00 +02:00
|
|
|
expect(req.headers).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const MyObject = Parse.Object.extend('MyObject');
|
|
|
|
|
const myObject = new MyObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
myObject
|
|
|
|
|
.save()
|
2017-07-14 19:19:00 +02:00
|
|
|
.then(myObj => myObj.destroy())
|
|
|
|
|
.then(() => done());
|
|
|
|
|
});
|
2017-10-18 14:13:09 +02:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should have request ip', done => {
|
|
|
|
|
Parse.Cloud.beforeDelete('MyObject', req => {
|
2017-10-18 14:13:09 +02:00
|
|
|
expect(req.ip).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const MyObject = Parse.Object.extend('MyObject');
|
|
|
|
|
const myObject = new MyObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
myObject
|
|
|
|
|
.save()
|
2017-10-18 14:13:09 +02:00
|
|
|
.then(myObj => myObj.destroy())
|
|
|
|
|
.then(() => done());
|
|
|
|
|
});
|
2017-07-14 19:19:00 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('afterDelete hooks', () => {
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should have request headers', done => {
|
|
|
|
|
Parse.Cloud.afterDelete('MyObject', req => {
|
2017-07-14 19:19:00 +02:00
|
|
|
expect(req.headers).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const MyObject = Parse.Object.extend('MyObject');
|
|
|
|
|
const myObject = new MyObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
myObject
|
|
|
|
|
.save()
|
2017-07-14 19:19:00 +02:00
|
|
|
.then(myObj => myObj.destroy())
|
|
|
|
|
.then(() => done());
|
|
|
|
|
});
|
2017-10-18 14:13:09 +02:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should have request ip', done => {
|
|
|
|
|
Parse.Cloud.afterDelete('MyObject', req => {
|
2017-10-18 14:13:09 +02:00
|
|
|
expect(req.ip).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const MyObject = Parse.Object.extend('MyObject');
|
|
|
|
|
const myObject = new MyObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
myObject
|
|
|
|
|
.save()
|
2017-10-18 14:13:09 +02:00
|
|
|
.then(myObj => myObj.destroy())
|
|
|
|
|
.then(() => done());
|
|
|
|
|
});
|
2017-07-14 19:19:00 +02:00
|
|
|
});
|
|
|
|
|
|
2016-09-17 16:52:35 -04:00
|
|
|
describe('beforeFind hooks', () => {
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should add beforeFind trigger', done => {
|
|
|
|
|
Parse.Cloud.beforeFind('MyObject', req => {
|
2016-12-07 15:17:05 -08:00
|
|
|
const q = req.query;
|
2016-09-17 16:52:35 -04:00
|
|
|
expect(q instanceof Parse.Query).toBe(true);
|
2016-12-07 15:17:05 -08:00
|
|
|
const jsonQuery = q.toJSON();
|
2016-09-17 16:52:35 -04:00
|
|
|
expect(jsonQuery.where.key).toEqual('value');
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(jsonQuery.where.some).toEqual({ $gt: 10 });
|
2016-09-17 16:52:35 -04:00
|
|
|
expect(jsonQuery.include).toEqual('otherKey,otherValue');
|
2019-11-23 11:38:13 -06:00
|
|
|
expect(jsonQuery.excludeKeys).toBe('exclude');
|
2016-09-17 16:52:35 -04:00
|
|
|
expect(jsonQuery.limit).toEqual(100);
|
|
|
|
|
expect(jsonQuery.skip).toBe(undefined);
|
2019-11-23 11:38:13 -06:00
|
|
|
expect(jsonQuery.order).toBe('key');
|
|
|
|
|
expect(jsonQuery.keys).toBe('select');
|
|
|
|
|
expect(jsonQuery.readPreference).toBe('PRIMARY');
|
|
|
|
|
expect(jsonQuery.includeReadPreference).toBe('SECONDARY');
|
|
|
|
|
expect(jsonQuery.subqueryReadPreference).toBe('SECONDARY_PREFERRED');
|
|
|
|
|
|
2017-06-21 09:24:51 -03:00
|
|
|
expect(req.isGet).toEqual(false);
|
2016-09-17 16:52:35 -04:00
|
|
|
});
|
|
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const query = new Parse.Query('MyObject');
|
2016-09-17 16:52:35 -04:00
|
|
|
query.equalTo('key', 'value');
|
|
|
|
|
query.greaterThan('some', 10);
|
|
|
|
|
query.include('otherKey');
|
|
|
|
|
query.include('otherValue');
|
2019-11-23 11:38:13 -06:00
|
|
|
query.ascending('key');
|
|
|
|
|
query.select('select');
|
|
|
|
|
query.exclude('exclude');
|
|
|
|
|
query.readPreference('PRIMARY', 'SECONDARY', 'SECONDARY_PREFERRED');
|
2016-11-24 15:47:41 -05:00
|
|
|
query.find().then(() => {
|
2016-09-17 16:52:35 -04:00
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should use modify', done => {
|
|
|
|
|
Parse.Cloud.beforeFind('MyObject', req => {
|
2016-12-07 15:17:05 -08:00
|
|
|
const q = req.query;
|
2016-09-17 16:52:35 -04:00
|
|
|
q.equalTo('forced', true);
|
|
|
|
|
});
|
|
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj0 = new Parse.Object('MyObject');
|
2016-09-17 16:52:35 -04:00
|
|
|
obj0.set('forced', false);
|
|
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj1 = new Parse.Object('MyObject');
|
2016-09-17 16:52:35 -04:00
|
|
|
obj1.set('forced', true);
|
2016-11-24 15:47:41 -05:00
|
|
|
Parse.Object.saveAll([obj0, obj1]).then(() => {
|
2016-12-07 15:17:05 -08:00
|
|
|
const query = new Parse.Query('MyObject');
|
2016-09-17 16:52:35 -04:00
|
|
|
query.equalTo('forced', false);
|
2018-09-01 13:58:06 -04:00
|
|
|
query.find().then(results => {
|
2016-09-17 16:52:35 -04:00
|
|
|
expect(results.length).toBe(1);
|
2016-12-07 15:17:05 -08:00
|
|
|
const firstResult = results[0];
|
2016-09-17 16:52:35 -04:00
|
|
|
expect(firstResult.get('forced')).toBe(true);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should use the modified the query', done => {
|
|
|
|
|
Parse.Cloud.beforeFind('MyObject', req => {
|
2016-12-07 15:17:05 -08:00
|
|
|
const q = req.query;
|
|
|
|
|
const otherQuery = new Parse.Query('MyObject');
|
2016-09-17 16:52:35 -04:00
|
|
|
otherQuery.equalTo('forced', true);
|
|
|
|
|
return Parse.Query.or(q, otherQuery);
|
|
|
|
|
});
|
|
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj0 = new Parse.Object('MyObject');
|
2016-09-17 16:52:35 -04:00
|
|
|
obj0.set('forced', false);
|
|
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj1 = new Parse.Object('MyObject');
|
2016-09-17 16:52:35 -04:00
|
|
|
obj1.set('forced', true);
|
2016-11-24 15:47:41 -05:00
|
|
|
Parse.Object.saveAll([obj0, obj1]).then(() => {
|
2016-12-07 15:17:05 -08:00
|
|
|
const query = new Parse.Query('MyObject');
|
2016-09-17 16:52:35 -04:00
|
|
|
query.equalTo('forced', false);
|
2018-09-01 13:58:06 -04:00
|
|
|
query.find().then(results => {
|
2016-09-17 16:52:35 -04:00
|
|
|
expect(results.length).toBe(2);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2019-11-23 11:38:13 -06:00
|
|
|
it('should use the modified exclude query', async () => {
|
|
|
|
|
Parse.Cloud.beforeFind('MyObject', req => {
|
|
|
|
|
const q = req.query;
|
|
|
|
|
q.exclude('number');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const obj = new Parse.Object('MyObject');
|
|
|
|
|
obj.set('number', 100);
|
|
|
|
|
obj.set('string', 'hello');
|
|
|
|
|
await obj.save();
|
|
|
|
|
|
|
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
query.equalTo('objectId', obj.id);
|
|
|
|
|
const results = await query.find();
|
|
|
|
|
expect(results.length).toBe(1);
|
|
|
|
|
expect(results[0].get('number')).toBeUndefined();
|
|
|
|
|
expect(results[0].get('string')).toBe('hello');
|
|
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should reject queries', done => {
|
2016-11-24 15:47:41 -05:00
|
|
|
Parse.Cloud.beforeFind('MyObject', () => {
|
2016-09-17 16:52:35 -04:00
|
|
|
return Promise.reject('Do not run that query');
|
|
|
|
|
});
|
|
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const query = new Parse.Query('MyObject');
|
2018-09-01 13:58:06 -04:00
|
|
|
query.find().then(
|
|
|
|
|
() => {
|
|
|
|
|
fail('should not succeed');
|
|
|
|
|
done();
|
|
|
|
|
},
|
|
|
|
|
err => {
|
2020-10-27 06:49:30 +11:00
|
|
|
expect(err.code).toBe(141);
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(err.message).toEqual('Do not run that query');
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-09-17 16:52:35 -04:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should handle empty where', done => {
|
|
|
|
|
Parse.Cloud.beforeFind('MyObject', req => {
|
2016-12-07 15:17:05 -08:00
|
|
|
const otherQuery = new Parse.Query('MyObject');
|
2016-09-17 16:52:35 -04:00
|
|
|
otherQuery.equalTo('some', true);
|
|
|
|
|
return Parse.Query.or(req.query, otherQuery);
|
|
|
|
|
});
|
|
|
|
|
|
2018-09-24 17:07:51 -04:00
|
|
|
request({
|
2016-09-17 16:52:35 -04:00
|
|
|
url: 'http://localhost:8378/1/classes/MyObject',
|
|
|
|
|
headers: {
|
|
|
|
|
'X-Parse-Application-Id': Parse.applicationId,
|
|
|
|
|
'X-Parse-REST-API-Key': 'rest',
|
|
|
|
|
},
|
2018-09-01 13:58:06 -04:00
|
|
|
}).then(
|
|
|
|
|
() => {
|
|
|
|
|
done();
|
|
|
|
|
},
|
|
|
|
|
err => {
|
|
|
|
|
fail(err);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-09-17 16:52:35 -04:00
|
|
|
});
|
2017-06-21 09:24:51 -03:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should handle sorting where', done => {
|
|
|
|
|
Parse.Cloud.beforeFind('MyObject', req => {
|
2018-01-24 15:15:05 -05:00
|
|
|
const query = req.query;
|
|
|
|
|
query.ascending('score');
|
|
|
|
|
return query;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const count = 20;
|
|
|
|
|
const objects = [];
|
|
|
|
|
while (objects.length != count) {
|
|
|
|
|
const object = new Parse.Object('MyObject');
|
|
|
|
|
object.set('score', Math.floor(Math.random() * 100));
|
|
|
|
|
objects.push(object);
|
|
|
|
|
}
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Object.saveAll(objects)
|
|
|
|
|
.then(() => {
|
|
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
return query.find();
|
|
|
|
|
})
|
|
|
|
|
.then(objects => {
|
|
|
|
|
let lastScore = -1;
|
|
|
|
|
objects.forEach(element => {
|
|
|
|
|
expect(element.get('score') >= lastScore).toBe(true);
|
|
|
|
|
lastScore = element.get('score');
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.then(done)
|
|
|
|
|
.catch(done.fail);
|
2018-01-24 15:15:05 -05:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should add beforeFind trigger using get API', done => {
|
2017-06-21 09:24:51 -03:00
|
|
|
const hook = {
|
2020-10-02 00:19:26 +02:00
|
|
|
method: function (req) {
|
2017-06-21 09:24:51 -03:00
|
|
|
expect(req.isGet).toEqual(true);
|
|
|
|
|
return Promise.resolve();
|
2018-09-01 13:58:06 -04:00
|
|
|
},
|
2017-06-21 09:24:51 -03:00
|
|
|
};
|
|
|
|
|
spyOn(hook, 'method').and.callThrough();
|
|
|
|
|
Parse.Cloud.beforeFind('MyObject', hook.method);
|
|
|
|
|
const obj = new Parse.Object('MyObject');
|
|
|
|
|
obj.set('secretField', 'SSID');
|
2020-10-02 00:19:26 +02:00
|
|
|
obj.save().then(function () {
|
2018-09-24 17:07:51 -04:00
|
|
|
request({
|
2017-06-21 09:24:51 -03:00
|
|
|
method: 'GET',
|
2018-09-24 17:07:51 -04:00
|
|
|
url: 'http://localhost:8378/1/classes/MyObject/' + obj.id,
|
2017-06-21 09:24:51 -03:00
|
|
|
headers: {
|
|
|
|
|
'X-Parse-Application-Id': 'test',
|
2018-09-01 13:58:06 -04:00
|
|
|
'X-Parse-REST-API-Key': 'rest',
|
2017-06-21 09:24:51 -03:00
|
|
|
},
|
|
|
|
|
json: true,
|
2018-09-24 17:07:51 -04:00
|
|
|
}).then(response => {
|
|
|
|
|
const body = response.data;
|
2017-06-21 09:24:51 -03:00
|
|
|
expect(body.secretField).toEqual('SSID');
|
|
|
|
|
expect(hook.method).toHaveBeenCalled();
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
2017-07-14 19:19:00 +02:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should have request headers', done => {
|
|
|
|
|
Parse.Cloud.beforeFind('MyObject', req => {
|
2017-07-14 19:19:00 +02:00
|
|
|
expect(req.headers).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const MyObject = Parse.Object.extend('MyObject');
|
|
|
|
|
const myObject = new MyObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
myObject
|
|
|
|
|
.save()
|
|
|
|
|
.then(myObj => {
|
2017-07-14 19:19:00 +02:00
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
query.equalTo('objectId', myObj.id);
|
2018-09-01 13:58:06 -04:00
|
|
|
return Promise.all([query.get(myObj.id), query.first(), query.find()]);
|
2017-07-14 19:19:00 +02:00
|
|
|
})
|
|
|
|
|
.then(() => done());
|
|
|
|
|
});
|
2017-10-18 14:13:09 +02:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should have request ip', done => {
|
|
|
|
|
Parse.Cloud.beforeFind('MyObject', req => {
|
2017-10-18 14:13:09 +02:00
|
|
|
expect(req.ip).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const MyObject = Parse.Object.extend('MyObject');
|
|
|
|
|
const myObject = new MyObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
myObject
|
|
|
|
|
.save()
|
|
|
|
|
.then(myObj => {
|
2017-10-18 14:13:09 +02:00
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
query.equalTo('objectId', myObj.id);
|
2018-09-01 13:58:06 -04:00
|
|
|
return Promise.all([query.get(myObj.id), query.first(), query.find()]);
|
2017-10-18 14:13:09 +02:00
|
|
|
})
|
|
|
|
|
.then(() => done());
|
|
|
|
|
});
|
2016-11-12 09:35:34 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('afterFind hooks', () => {
|
2020-10-22 13:40:40 +11:00
|
|
|
it('should add afterFind trigger', done => {
|
|
|
|
|
Parse.Cloud.afterFind('MyObject', req => {
|
|
|
|
|
const q = req.query;
|
|
|
|
|
expect(q instanceof Parse.Query).toBe(true);
|
|
|
|
|
const jsonQuery = q.toJSON();
|
|
|
|
|
expect(jsonQuery.where.key).toEqual('value');
|
|
|
|
|
expect(jsonQuery.where.some).toEqual({ $gt: 10 });
|
|
|
|
|
expect(jsonQuery.include).toEqual('otherKey,otherValue');
|
|
|
|
|
expect(jsonQuery.excludeKeys).toBe('exclude');
|
|
|
|
|
expect(jsonQuery.limit).toEqual(100);
|
|
|
|
|
expect(jsonQuery.skip).toBe(undefined);
|
|
|
|
|
expect(jsonQuery.order).toBe('key');
|
|
|
|
|
expect(jsonQuery.keys).toBe('select');
|
|
|
|
|
expect(jsonQuery.readPreference).toBe('PRIMARY');
|
|
|
|
|
expect(jsonQuery.includeReadPreference).toBe('SECONDARY');
|
|
|
|
|
expect(jsonQuery.subqueryReadPreference).toBe('SECONDARY_PREFERRED');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
query.equalTo('key', 'value');
|
|
|
|
|
query.greaterThan('some', 10);
|
|
|
|
|
query.include('otherKey');
|
|
|
|
|
query.include('otherValue');
|
|
|
|
|
query.ascending('key');
|
|
|
|
|
query.select('select');
|
|
|
|
|
query.exclude('exclude');
|
|
|
|
|
query.readPreference('PRIMARY', 'SECONDARY', 'SECONDARY_PREFERRED');
|
|
|
|
|
query.find().then(() => {
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should add afterFind trigger using get', done => {
|
|
|
|
|
Parse.Cloud.afterFind('MyObject', req => {
|
|
|
|
|
for (let i = 0; i < req.objects.length; i++) {
|
|
|
|
|
req.objects[i].set('secretField', '###');
|
2016-11-12 09:35:34 -08:00
|
|
|
}
|
2018-08-06 17:39:38 -04:00
|
|
|
return req.objects;
|
2016-11-12 09:35:34 -08:00
|
|
|
});
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj = new Parse.Object('MyObject');
|
2016-11-12 09:35:34 -08:00
|
|
|
obj.set('secretField', 'SSID');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.save().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
query.get(obj.id).then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function (result) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(result.get('secretField')).toEqual('###');
|
|
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2016-11-12 09:35:34 -08:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
2018-09-01 13:58:06 -04:00
|
|
|
}
|
|
|
|
|
);
|
2016-11-12 09:35:34 -08:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should add afterFind trigger using find', done => {
|
|
|
|
|
Parse.Cloud.afterFind('MyObject', req => {
|
|
|
|
|
for (let i = 0; i < req.objects.length; i++) {
|
|
|
|
|
req.objects[i].set('secretField', '###');
|
2016-11-12 09:35:34 -08:00
|
|
|
}
|
2018-08-06 17:39:38 -04:00
|
|
|
return req.objects;
|
2016-11-12 09:35:34 -08:00
|
|
|
});
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj = new Parse.Object('MyObject');
|
2016-11-12 09:35:34 -08:00
|
|
|
obj.set('secretField', 'SSID');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.save().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
query.equalTo('objectId', obj.id);
|
|
|
|
|
query.find().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function (results) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(results[0].get('secretField')).toEqual('###');
|
|
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2016-11-12 09:35:34 -08:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
2018-09-01 13:58:06 -04:00
|
|
|
}
|
|
|
|
|
);
|
2016-11-12 09:35:34 -08:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should filter out results', done => {
|
|
|
|
|
Parse.Cloud.afterFind('MyObject', req => {
|
2016-12-07 15:17:05 -08:00
|
|
|
const filteredResults = [];
|
2018-09-01 13:58:06 -04:00
|
|
|
for (let i = 0; i < req.objects.length; i++) {
|
|
|
|
|
if (req.objects[i].get('secretField') === 'SSID1') {
|
2016-11-12 09:35:34 -08:00
|
|
|
filteredResults.push(req.objects[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-06 17:39:38 -04:00
|
|
|
return filteredResults;
|
2016-11-12 09:35:34 -08:00
|
|
|
});
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj0 = new Parse.Object('MyObject');
|
2016-11-12 09:35:34 -08:00
|
|
|
obj0.set('secretField', 'SSID1');
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj1 = new Parse.Object('MyObject');
|
2016-11-12 09:35:34 -08:00
|
|
|
obj1.set('secretField', 'SSID2');
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Object.saveAll([obj0, obj1]).then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
query.find().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function (results) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(results[0].get('secretField')).toEqual('SSID1');
|
|
|
|
|
expect(results.length).toEqual(1);
|
|
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2016-11-12 09:35:34 -08:00
|
|
|
fail(error);
|
|
|
|
|
done();
|
2018-09-01 13:58:06 -04:00
|
|
|
}
|
|
|
|
|
);
|
2016-11-12 09:35:34 -08:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should handle failures', done => {
|
2018-08-06 17:39:38 -04:00
|
|
|
Parse.Cloud.afterFind('MyObject', () => {
|
2018-09-01 13:58:06 -04:00
|
|
|
throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail');
|
2016-11-12 09:35:34 -08:00
|
|
|
});
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj = new Parse.Object('MyObject');
|
2016-11-12 09:35:34 -08:00
|
|
|
obj.set('secretField', 'SSID');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.save().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
query.equalTo('objectId', obj.id);
|
|
|
|
|
query.find().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail('AfterFind should handle response failure correctly');
|
|
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
done();
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2016-11-12 09:35:34 -08:00
|
|
|
done();
|
2018-09-01 13:58:06 -04:00
|
|
|
}
|
|
|
|
|
);
|
2016-11-12 09:35:34 -08:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should also work with promise', done => {
|
|
|
|
|
Parse.Cloud.afterFind('MyObject', req => {
|
|
|
|
|
return new Promise(resolve => {
|
2020-10-02 00:19:26 +02:00
|
|
|
setTimeout(function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
for (let i = 0; i < req.objects.length; i++) {
|
|
|
|
|
req.objects[i].set('secretField', '###');
|
2018-08-05 13:58:07 -04:00
|
|
|
}
|
|
|
|
|
resolve(req.objects);
|
|
|
|
|
}, 1000);
|
|
|
|
|
});
|
2016-11-12 09:35:34 -08:00
|
|
|
});
|
2016-12-07 15:17:05 -08:00
|
|
|
const obj = new Parse.Object('MyObject');
|
2016-11-12 09:35:34 -08:00
|
|
|
obj.set('secretField', 'SSID');
|
2018-09-01 13:58:06 -04:00
|
|
|
obj.save().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function () {
|
2018-09-01 13:58:06 -04:00
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
query.equalTo('objectId', obj.id);
|
|
|
|
|
query.find().then(
|
2020-10-02 00:19:26 +02:00
|
|
|
function (results) {
|
2018-09-01 13:58:06 -04:00
|
|
|
expect(results[0].get('secretField')).toEqual('###');
|
|
|
|
|
done();
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2018-09-01 13:58:06 -04:00
|
|
|
fail(error);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
2020-10-02 00:19:26 +02:00
|
|
|
function (error) {
|
2016-11-12 09:35:34 -08:00
|
|
|
fail(error);
|
2018-09-01 13:58:06 -04:00
|
|
|
}
|
|
|
|
|
);
|
2016-11-12 09:35:34 -08:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should alter select', done => {
|
|
|
|
|
Parse.Cloud.beforeFind('MyObject', req => {
|
2016-12-01 07:04:47 -08:00
|
|
|
req.query.select('white');
|
|
|
|
|
return req.query;
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-25 15:06:58 -05:00
|
|
|
const obj0 = new Parse.Object('MyObject').set('white', true).set('black', true);
|
2018-09-01 13:58:06 -04:00
|
|
|
obj0.save().then(() => {
|
|
|
|
|
new Parse.Query('MyObject').first().then(result => {
|
|
|
|
|
expect(result.get('white')).toBe(true);
|
|
|
|
|
expect(result.get('black')).toBe(undefined);
|
|
|
|
|
done();
|
2016-12-01 07:04:47 -08:00
|
|
|
});
|
2018-09-01 13:58:06 -04:00
|
|
|
});
|
2016-12-01 07:04:47 -08:00
|
|
|
});
|
2016-11-12 09:35:34 -08:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should not alter select', done => {
|
2020-10-25 15:06:58 -05:00
|
|
|
const obj0 = new Parse.Object('MyObject').set('white', true).set('black', true);
|
2018-09-01 13:58:06 -04:00
|
|
|
obj0.save().then(() => {
|
|
|
|
|
new Parse.Query('MyObject').first().then(result => {
|
|
|
|
|
expect(result.get('white')).toBe(true);
|
|
|
|
|
expect(result.get('black')).toBe(true);
|
|
|
|
|
done();
|
2016-12-01 07:04:47 -08:00
|
|
|
});
|
2018-09-01 13:58:06 -04:00
|
|
|
});
|
2016-12-01 07:04:47 -08:00
|
|
|
});
|
2017-05-14 21:47:30 -04:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should set count to true on beforeFind hooks if query is count', done => {
|
2017-05-14 21:47:30 -04:00
|
|
|
const hook = {
|
2020-10-02 00:19:26 +02:00
|
|
|
method: function (req) {
|
2017-05-14 21:47:30 -04:00
|
|
|
expect(req.count).toBe(true);
|
|
|
|
|
return Promise.resolve();
|
2018-09-01 13:58:06 -04:00
|
|
|
},
|
2017-05-14 21:47:30 -04:00
|
|
|
};
|
|
|
|
|
spyOn(hook, 'method').and.callThrough();
|
|
|
|
|
Parse.Cloud.beforeFind('Stuff', hook.method);
|
2018-09-01 13:58:06 -04:00
|
|
|
new Parse.Query('Stuff').count().then(count => {
|
2017-05-14 21:47:30 -04:00
|
|
|
expect(count).toBe(0);
|
|
|
|
|
expect(hook.method).toHaveBeenCalled();
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should set count to false on beforeFind hooks if query is not count', done => {
|
2017-05-14 21:47:30 -04:00
|
|
|
const hook = {
|
2020-10-02 00:19:26 +02:00
|
|
|
method: function (req) {
|
2017-05-14 21:47:30 -04:00
|
|
|
expect(req.count).toBe(false);
|
|
|
|
|
return Promise.resolve();
|
2018-09-01 13:58:06 -04:00
|
|
|
},
|
2017-05-14 21:47:30 -04:00
|
|
|
};
|
|
|
|
|
spyOn(hook, 'method').and.callThrough();
|
|
|
|
|
Parse.Cloud.beforeFind('Stuff', hook.method);
|
2018-09-01 13:58:06 -04:00
|
|
|
new Parse.Query('Stuff').find().then(res => {
|
2017-05-14 21:47:30 -04:00
|
|
|
expect(res.length).toBe(0);
|
|
|
|
|
expect(hook.method).toHaveBeenCalled();
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
2017-07-14 19:19:00 +02:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should have request headers', done => {
|
|
|
|
|
Parse.Cloud.afterFind('MyObject', req => {
|
2017-07-14 19:19:00 +02:00
|
|
|
expect(req.headers).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const MyObject = Parse.Object.extend('MyObject');
|
|
|
|
|
const myObject = new MyObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
myObject
|
|
|
|
|
.save()
|
|
|
|
|
.then(myObj => {
|
2017-07-14 19:19:00 +02:00
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
query.equalTo('objectId', myObj.id);
|
2018-09-01 13:58:06 -04:00
|
|
|
return Promise.all([query.get(myObj.id), query.first(), query.find()]);
|
2017-07-14 19:19:00 +02:00
|
|
|
})
|
|
|
|
|
.then(() => done());
|
|
|
|
|
});
|
2017-09-18 15:01:07 -04:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should have request ip', done => {
|
|
|
|
|
Parse.Cloud.afterFind('MyObject', req => {
|
2017-10-18 14:13:09 +02:00
|
|
|
expect(req.ip).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const MyObject = Parse.Object.extend('MyObject');
|
|
|
|
|
const myObject = new MyObject();
|
2018-09-01 13:58:06 -04:00
|
|
|
myObject
|
|
|
|
|
.save()
|
|
|
|
|
.then(myObj => {
|
2017-10-18 14:13:09 +02:00
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
query.equalTo('objectId', myObj.id);
|
2018-09-01 13:58:06 -04:00
|
|
|
return Promise.all([query.get(myObj.id), query.first(), query.find()]);
|
2017-10-18 14:13:09 +02:00
|
|
|
})
|
2018-09-01 13:58:06 -04:00
|
|
|
.then(() => done())
|
|
|
|
|
.catch(done.fail);
|
2017-10-18 14:13:09 +02:00
|
|
|
});
|
|
|
|
|
|
2017-09-18 15:01:07 -04:00
|
|
|
it('should validate triggers correctly', () => {
|
|
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.beforeSave('_Session', () => {});
|
2020-10-25 15:06:58 -05:00
|
|
|
}).toThrow('Only the afterLogout trigger is allowed for the _Session class.');
|
2017-09-18 15:01:07 -04:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.afterSave('_Session', () => {});
|
2020-10-25 15:06:58 -05:00
|
|
|
}).toThrow('Only the afterLogout trigger is allowed for the _Session class.');
|
2017-09-18 15:01:07 -04:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.beforeSave('_PushStatus', () => {});
|
|
|
|
|
}).toThrow('Only afterSave is allowed on _PushStatus');
|
|
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.afterSave('_PushStatus', () => {});
|
|
|
|
|
}).not.toThrow();
|
2019-04-23 16:24:20 +01:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.beforeLogin(() => {});
|
2020-10-25 15:06:58 -05:00
|
|
|
}).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers');
|
2019-04-23 16:24:20 +01:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.beforeLogin('_User', () => {});
|
2020-10-25 15:06:58 -05:00
|
|
|
}).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers');
|
2019-04-23 16:24:20 +01:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.beforeLogin(Parse.User, () => {});
|
2020-10-25 15:06:58 -05:00
|
|
|
}).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers');
|
2019-04-23 16:24:20 +01:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.beforeLogin('SomeClass', () => {});
|
2020-10-25 15:06:58 -05:00
|
|
|
}).toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers');
|
2020-02-11 17:38:14 -06:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.afterLogin(() => {});
|
2020-10-25 15:06:58 -05:00
|
|
|
}).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers');
|
2020-02-11 17:38:14 -06:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.afterLogin('_User', () => {});
|
2020-10-25 15:06:58 -05:00
|
|
|
}).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers');
|
2020-02-11 17:38:14 -06:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.afterLogin(Parse.User, () => {});
|
2020-10-25 15:06:58 -05:00
|
|
|
}).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers');
|
2020-02-11 17:38:14 -06:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.afterLogin('SomeClass', () => {});
|
2020-10-25 15:06:58 -05:00
|
|
|
}).toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers');
|
2019-11-16 04:52:57 +01:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.afterLogout(() => {});
|
|
|
|
|
}).not.toThrow();
|
|
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.afterLogout('_Session', () => {});
|
|
|
|
|
}).not.toThrow();
|
|
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.afterLogout('_User', () => {});
|
2020-10-25 15:06:58 -05:00
|
|
|
}).toThrow('Only the _Session class is allowed for the afterLogout trigger.');
|
2019-11-16 04:52:57 +01:00
|
|
|
expect(() => {
|
|
|
|
|
Parse.Cloud.afterLogout('SomeClass', () => {});
|
2020-10-25 15:06:58 -05:00
|
|
|
}).toThrow('Only the _Session class is allowed for the afterLogout trigger.');
|
2017-09-18 15:01:07 -04:00
|
|
|
});
|
2018-02-28 19:32:01 -06:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should skip afterFind hooks for aggregate', done => {
|
2018-02-28 19:32:01 -06:00
|
|
|
const hook = {
|
2020-10-02 00:19:26 +02:00
|
|
|
method: function () {
|
2018-02-28 19:32:01 -06:00
|
|
|
return Promise.reject();
|
2018-09-01 13:58:06 -04:00
|
|
|
},
|
2018-02-28 19:32:01 -06:00
|
|
|
};
|
|
|
|
|
spyOn(hook, 'method').and.callThrough();
|
|
|
|
|
Parse.Cloud.afterFind('MyObject', hook.method);
|
2018-09-01 13:58:06 -04:00
|
|
|
const obj = new Parse.Object('MyObject');
|
|
|
|
|
const pipeline = [
|
|
|
|
|
{
|
|
|
|
|
group: { objectId: {} },
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
obj
|
|
|
|
|
.save()
|
|
|
|
|
.then(() => {
|
|
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
return query.aggregate(pipeline);
|
|
|
|
|
})
|
|
|
|
|
.then(results => {
|
|
|
|
|
expect(results[0].objectId).toEqual(null);
|
|
|
|
|
expect(hook.method).not.toHaveBeenCalled();
|
|
|
|
|
done();
|
|
|
|
|
});
|
2018-02-28 19:32:01 -06:00
|
|
|
});
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
it('should skip afterFind hooks for distinct', done => {
|
2018-02-28 19:32:01 -06:00
|
|
|
const hook = {
|
2020-10-02 00:19:26 +02:00
|
|
|
method: function () {
|
2018-02-28 19:32:01 -06:00
|
|
|
return Promise.reject();
|
2018-09-01 13:58:06 -04:00
|
|
|
},
|
2018-02-28 19:32:01 -06:00
|
|
|
};
|
|
|
|
|
spyOn(hook, 'method').and.callThrough();
|
|
|
|
|
Parse.Cloud.afterFind('MyObject', hook.method);
|
2018-09-01 13:58:06 -04:00
|
|
|
const obj = new Parse.Object('MyObject');
|
2018-02-28 19:32:01 -06:00
|
|
|
obj.set('score', 10);
|
2018-09-01 13:58:06 -04:00
|
|
|
obj
|
|
|
|
|
.save()
|
|
|
|
|
.then(() => {
|
|
|
|
|
const query = new Parse.Query('MyObject');
|
|
|
|
|
return query.distinct('score');
|
|
|
|
|
})
|
|
|
|
|
.then(results => {
|
|
|
|
|
expect(results[0]).toEqual(10);
|
|
|
|
|
expect(hook.method).not.toHaveBeenCalled();
|
|
|
|
|
done();
|
|
|
|
|
});
|
2018-02-28 19:32:01 -06:00
|
|
|
});
|
2018-08-09 11:08:18 -04:00
|
|
|
|
|
|
|
|
it('should expose context in before and afterSave', async () => {
|
|
|
|
|
let calledBefore = false;
|
|
|
|
|
let calledAfter = false;
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.beforeSave('MyClass', req => {
|
2018-08-09 11:08:18 -04:00
|
|
|
req.context = {
|
|
|
|
|
key: 'value',
|
|
|
|
|
otherKey: 1,
|
2018-09-01 13:58:06 -04:00
|
|
|
};
|
2018-08-09 11:08:18 -04:00
|
|
|
calledBefore = true;
|
|
|
|
|
});
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.afterSave('MyClass', req => {
|
2018-08-09 11:08:18 -04:00
|
|
|
expect(req.context.otherKey).toBe(1);
|
|
|
|
|
expect(req.context.key).toBe('value');
|
|
|
|
|
calledAfter = true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const object = new Parse.Object('MyClass');
|
|
|
|
|
await object.save();
|
|
|
|
|
expect(calledBefore).toBe(true);
|
|
|
|
|
expect(calledAfter).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should expose context in before and afterSave and let keys be set individually', async () => {
|
|
|
|
|
let calledBefore = false;
|
|
|
|
|
let calledAfter = false;
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.beforeSave('MyClass', req => {
|
2018-08-09 11:08:18 -04:00
|
|
|
req.context.some = 'value';
|
|
|
|
|
req.context.yolo = 1;
|
|
|
|
|
calledBefore = true;
|
|
|
|
|
});
|
2018-09-01 13:58:06 -04:00
|
|
|
Parse.Cloud.afterSave('MyClass', req => {
|
2018-08-09 11:08:18 -04:00
|
|
|
expect(req.context.yolo).toBe(1);
|
|
|
|
|
expect(req.context.some).toBe('value');
|
|
|
|
|
calledAfter = true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const object = new Parse.Object('MyClass');
|
|
|
|
|
await object.save();
|
|
|
|
|
expect(calledBefore).toBe(true);
|
|
|
|
|
expect(calledAfter).toBe(true);
|
|
|
|
|
});
|
2016-12-01 07:04:47 -08:00
|
|
|
});
|
2019-04-23 16:24:20 +01:00
|
|
|
|
|
|
|
|
describe('beforeLogin hook', () => {
|
|
|
|
|
it('should run beforeLogin with correct credentials', async done => {
|
|
|
|
|
let hit = 0;
|
|
|
|
|
Parse.Cloud.beforeLogin(req => {
|
|
|
|
|
hit++;
|
|
|
|
|
expect(req.object.get('username')).toEqual('tupac');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await Parse.User.signUp('tupac', 'shakur');
|
|
|
|
|
const user = await Parse.User.logIn('tupac', 'shakur');
|
|
|
|
|
expect(hit).toBe(1);
|
|
|
|
|
expect(user).toBeDefined();
|
|
|
|
|
expect(user.getUsername()).toBe('tupac');
|
|
|
|
|
expect(user.getSessionToken()).toBeDefined();
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should be able to block login if an error is thrown', async done => {
|
|
|
|
|
let hit = 0;
|
|
|
|
|
Parse.Cloud.beforeLogin(req => {
|
|
|
|
|
hit++;
|
|
|
|
|
if (req.object.get('isBanned')) {
|
|
|
|
|
throw new Error('banned account');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const user = await Parse.User.signUp('tupac', 'shakur');
|
|
|
|
|
await user.save({ isBanned: true });
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await Parse.User.logIn('tupac', 'shakur');
|
|
|
|
|
throw new Error('should not have been logged in.');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
expect(e.message).toBe('banned account');
|
|
|
|
|
}
|
|
|
|
|
expect(hit).toBe(1);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
|
2019-08-29 19:07:39 -07:00
|
|
|
it('should be able to block login if an error is thrown even if the user has a attached file', async done => {
|
|
|
|
|
let hit = 0;
|
|
|
|
|
Parse.Cloud.beforeLogin(req => {
|
|
|
|
|
hit++;
|
|
|
|
|
if (req.object.get('isBanned')) {
|
|
|
|
|
throw new Error('banned account');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const user = await Parse.User.signUp('tupac', 'shakur');
|
|
|
|
|
const base64 = 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=';
|
|
|
|
|
const file = new Parse.File('myfile.txt', { base64 });
|
|
|
|
|
await file.save();
|
|
|
|
|
await user.save({ isBanned: true, file });
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await Parse.User.logIn('tupac', 'shakur');
|
|
|
|
|
throw new Error('should not have been logged in.');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
expect(e.message).toBe('banned account');
|
|
|
|
|
}
|
|
|
|
|
expect(hit).toBe(1);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
|
2019-04-23 16:24:20 +01:00
|
|
|
it('should not run beforeLogin with incorrect credentials', async done => {
|
|
|
|
|
let hit = 0;
|
|
|
|
|
Parse.Cloud.beforeLogin(req => {
|
|
|
|
|
hit++;
|
|
|
|
|
expect(req.object.get('username')).toEqual('tupac');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await Parse.User.signUp('tupac', 'shakur');
|
|
|
|
|
try {
|
|
|
|
|
await Parse.User.logIn('tony', 'shakur');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND);
|
|
|
|
|
}
|
|
|
|
|
expect(hit).toBe(0);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should not run beforeLogin on sign up', async done => {
|
|
|
|
|
let hit = 0;
|
|
|
|
|
Parse.Cloud.beforeLogin(req => {
|
|
|
|
|
hit++;
|
|
|
|
|
expect(req.object.get('username')).toEqual('tupac');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const user = await Parse.User.signUp('tupac', 'shakur');
|
|
|
|
|
expect(user).toBeDefined();
|
|
|
|
|
expect(hit).toBe(0);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
|
2019-11-16 04:52:57 +01:00
|
|
|
it('should trigger afterLogout hook on logout', async done => {
|
|
|
|
|
let userId;
|
|
|
|
|
Parse.Cloud.afterLogout(req => {
|
|
|
|
|
expect(req.object.className).toEqual('_Session');
|
|
|
|
|
expect(req.object.id).toBeDefined();
|
|
|
|
|
const user = req.object.get('user');
|
|
|
|
|
expect(user).toBeDefined();
|
|
|
|
|
userId = user.id;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const user = await Parse.User.signUp('user', 'pass');
|
|
|
|
|
await Parse.User.logOut();
|
|
|
|
|
expect(user.id).toBe(userId);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
|
2019-04-23 16:24:20 +01:00
|
|
|
it('should have expected data in request', async done => {
|
|
|
|
|
Parse.Cloud.beforeLogin(req => {
|
|
|
|
|
expect(req.object).toBeDefined();
|
|
|
|
|
expect(req.user).toBeUndefined();
|
|
|
|
|
expect(req.headers).toBeDefined();
|
|
|
|
|
expect(req.ip).toBeDefined();
|
|
|
|
|
expect(req.installationId).toBeDefined();
|
|
|
|
|
expect(req.context).toBeUndefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await Parse.User.signUp('tupac', 'shakur');
|
|
|
|
|
await Parse.User.logIn('tupac', 'shakur');
|
|
|
|
|
done();
|
|
|
|
|
});
|
2019-10-15 15:50:25 -05:00
|
|
|
|
|
|
|
|
it('afterFind should not be triggered when saving an object', async () => {
|
|
|
|
|
let beforeSaves = 0;
|
|
|
|
|
Parse.Cloud.beforeSave('SavingTest', () => {
|
|
|
|
|
beforeSaves++;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let afterSaves = 0;
|
|
|
|
|
Parse.Cloud.afterSave('SavingTest', () => {
|
|
|
|
|
afterSaves++;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let beforeFinds = 0;
|
|
|
|
|
Parse.Cloud.beforeFind('SavingTest', () => {
|
|
|
|
|
beforeFinds++;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let afterFinds = 0;
|
|
|
|
|
Parse.Cloud.afterFind('SavingTest', () => {
|
|
|
|
|
afterFinds++;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const obj = new Parse.Object('SavingTest');
|
|
|
|
|
obj.set('someField', 'some value 1');
|
|
|
|
|
await obj.save();
|
|
|
|
|
|
|
|
|
|
expect(beforeSaves).toEqual(1);
|
|
|
|
|
expect(afterSaves).toEqual(1);
|
|
|
|
|
expect(beforeFinds).toEqual(0);
|
|
|
|
|
expect(afterFinds).toEqual(0);
|
|
|
|
|
|
|
|
|
|
obj.set('someField', 'some value 2');
|
|
|
|
|
await obj.save();
|
|
|
|
|
|
|
|
|
|
expect(beforeSaves).toEqual(2);
|
|
|
|
|
expect(afterSaves).toEqual(2);
|
|
|
|
|
expect(beforeFinds).toEqual(0);
|
|
|
|
|
expect(afterFinds).toEqual(0);
|
|
|
|
|
|
|
|
|
|
await obj.fetch();
|
|
|
|
|
|
|
|
|
|
expect(beforeSaves).toEqual(2);
|
|
|
|
|
expect(afterSaves).toEqual(2);
|
|
|
|
|
expect(beforeFinds).toEqual(1);
|
|
|
|
|
expect(afterFinds).toEqual(1);
|
|
|
|
|
|
|
|
|
|
obj.set('someField', 'some value 3');
|
|
|
|
|
await obj.save();
|
|
|
|
|
|
|
|
|
|
expect(beforeSaves).toEqual(3);
|
|
|
|
|
expect(afterSaves).toEqual(3);
|
|
|
|
|
expect(beforeFinds).toEqual(1);
|
|
|
|
|
expect(afterFinds).toEqual(1);
|
|
|
|
|
});
|
2020-04-02 17:00:15 -04:00
|
|
|
|
|
|
|
|
it('beforeSaveFile should not change file if nothing is returned', async () => {
|
|
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
|
|
|
|
Parse.Cloud.beforeSaveFile(() => {
|
|
|
|
|
return;
|
|
|
|
|
});
|
|
|
|
|
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
|
|
|
|
|
const result = await file.save({ useMasterKey: true });
|
|
|
|
|
expect(result).toBe(file);
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-26 04:36:54 +11:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2020-04-02 17:00:15 -04:00
|
|
|
it('beforeSaveFile should return file that is already saved and not save anything to files adapter', async () => {
|
|
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
|
|
|
|
const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough();
|
|
|
|
|
Parse.Cloud.beforeSaveFile(() => {
|
|
|
|
|
const newFile = new Parse.File('some-file.txt');
|
2020-10-25 15:06:58 -05:00
|
|
|
newFile._url = 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt';
|
2020-04-02 17:00:15 -04:00
|
|
|
return newFile;
|
|
|
|
|
});
|
|
|
|
|
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
|
|
|
|
|
const result = await file.save({ useMasterKey: true });
|
|
|
|
|
expect(result).toBe(file);
|
|
|
|
|
expect(result._name).toBe('some-file.txt');
|
2020-10-25 15:06:58 -05:00
|
|
|
expect(result._url).toBe('http://www.somewhere.com/parse/files/some-app-id/some-file.txt');
|
2020-04-02 17:00:15 -04:00
|
|
|
expect(createFileSpy).not.toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('beforeSaveFile should throw error', async () => {
|
|
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
|
|
|
|
Parse.Cloud.beforeSaveFile(() => {
|
|
|
|
|
throw new Parse.Error(400, 'some-error-message');
|
|
|
|
|
});
|
|
|
|
|
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
|
|
|
|
|
try {
|
|
|
|
|
await file.save({ useMasterKey: true });
|
|
|
|
|
} catch (error) {
|
|
|
|
|
expect(error.message).toBe('some-error-message');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('beforeSaveFile should change values of uploaded file by editing fileObject directly', async () => {
|
|
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
|
|
|
|
const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough();
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSaveFile(async req => {
|
2020-04-02 17:00:15 -04:00
|
|
|
expect(req.triggerName).toEqual('beforeSaveFile');
|
|
|
|
|
expect(req.master).toBe(true);
|
|
|
|
|
req.file.addMetadata('foo', 'bar');
|
|
|
|
|
req.file.addTag('tagA', 'some-tag');
|
|
|
|
|
});
|
|
|
|
|
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
|
|
|
|
|
const result = await file.save({ useMasterKey: true });
|
|
|
|
|
expect(result).toBe(file);
|
|
|
|
|
const newData = new Buffer([1, 2, 3]);
|
|
|
|
|
const newOptions = {
|
|
|
|
|
tags: {
|
|
|
|
|
tagA: 'some-tag',
|
|
|
|
|
},
|
|
|
|
|
metadata: {
|
|
|
|
|
foo: 'bar',
|
|
|
|
|
},
|
|
|
|
|
};
|
2020-10-02 00:19:26 +02:00
|
|
|
expect(createFileSpy).toHaveBeenCalledWith(
|
|
|
|
|
jasmine.any(String),
|
|
|
|
|
newData,
|
|
|
|
|
'text/plain',
|
|
|
|
|
newOptions
|
|
|
|
|
);
|
2020-04-02 17:00:15 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('beforeSaveFile should change values by returning new fileObject', async () => {
|
|
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
|
|
|
|
const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough();
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSaveFile(async req => {
|
2020-04-02 17:00:15 -04:00
|
|
|
expect(req.triggerName).toEqual('beforeSaveFile');
|
|
|
|
|
expect(req.fileSize).toBe(3);
|
2020-10-25 15:06:58 -05:00
|
|
|
const newFile = new Parse.File('donald_duck.pdf', [4, 5, 6], 'application/pdf');
|
2020-04-02 17:00:15 -04:00
|
|
|
newFile.setMetadata({ foo: 'bar' });
|
|
|
|
|
newFile.setTags({ tagA: 'some-tag' });
|
|
|
|
|
return newFile;
|
|
|
|
|
});
|
|
|
|
|
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
|
|
|
|
|
const result = await file.save({ useMasterKey: true });
|
|
|
|
|
expect(result).toBeInstanceOf(Parse.File);
|
|
|
|
|
const newData = new Buffer([4, 5, 6]);
|
|
|
|
|
const newContentType = 'application/pdf';
|
|
|
|
|
const newOptions = {
|
|
|
|
|
tags: {
|
|
|
|
|
tagA: 'some-tag',
|
|
|
|
|
},
|
|
|
|
|
metadata: {
|
|
|
|
|
foo: 'bar',
|
|
|
|
|
},
|
|
|
|
|
};
|
2020-10-02 00:19:26 +02:00
|
|
|
expect(createFileSpy).toHaveBeenCalledWith(
|
|
|
|
|
jasmine.any(String),
|
|
|
|
|
newData,
|
|
|
|
|
newContentType,
|
|
|
|
|
newOptions
|
|
|
|
|
);
|
2020-04-02 17:00:15 -04:00
|
|
|
const expectedFileName = 'donald_duck.pdf';
|
2020-10-25 15:06:58 -05:00
|
|
|
expect(file._name.indexOf(expectedFileName)).toBe(file._name.length - expectedFileName.length);
|
2020-04-02 17:00:15 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('beforeSaveFile should contain metadata and tags saved from client', async () => {
|
|
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
|
|
|
|
const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough();
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSaveFile(async req => {
|
2020-04-02 17:00:15 -04:00
|
|
|
expect(req.triggerName).toEqual('beforeSaveFile');
|
|
|
|
|
expect(req.fileSize).toBe(3);
|
|
|
|
|
expect(req.file).toBeInstanceOf(Parse.File);
|
|
|
|
|
expect(req.file.name()).toBe('popeye.txt');
|
|
|
|
|
expect(req.file.metadata()).toEqual({ foo: 'bar' });
|
|
|
|
|
expect(req.file.tags()).toEqual({ bar: 'foo' });
|
|
|
|
|
});
|
|
|
|
|
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
|
|
|
|
|
file.setMetadata({ foo: 'bar' });
|
|
|
|
|
file.setTags({ bar: 'foo' });
|
|
|
|
|
const result = await file.save({ useMasterKey: true });
|
|
|
|
|
expect(result).toBeInstanceOf(Parse.File);
|
|
|
|
|
const options = {
|
|
|
|
|
metadata: { foo: 'bar' },
|
|
|
|
|
tags: { bar: 'foo' },
|
|
|
|
|
};
|
2020-10-02 00:19:26 +02:00
|
|
|
expect(createFileSpy).toHaveBeenCalledWith(
|
|
|
|
|
jasmine.any(String),
|
|
|
|
|
jasmine.any(Buffer),
|
|
|
|
|
'text/plain',
|
|
|
|
|
options
|
|
|
|
|
);
|
2020-04-02 17:00:15 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('beforeSaveFile should return same file data with new file name', async () => {
|
|
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
|
|
|
|
const config = Config.get('test');
|
|
|
|
|
config.filesController.options.preserveFileName = true;
|
|
|
|
|
Parse.Cloud.beforeSaveFile(async ({ file }) => {
|
|
|
|
|
expect(file.name()).toBe('popeye.txt');
|
|
|
|
|
const fileData = await file.getData();
|
|
|
|
|
const newFile = new Parse.File('2020-04-01.txt', { base64: fileData });
|
|
|
|
|
return newFile;
|
|
|
|
|
});
|
|
|
|
|
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
|
|
|
|
|
const result = await file.save({ useMasterKey: true });
|
|
|
|
|
expect(result.name()).toBe('2020-04-01.txt');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('afterSaveFile should set fileSize to null if beforeSave returns an already saved file', async () => {
|
|
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
|
|
|
|
const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough();
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSaveFile(req => {
|
2020-04-02 17:00:15 -04:00
|
|
|
expect(req.fileSize).toBe(3);
|
|
|
|
|
const newFile = new Parse.File('some-file.txt');
|
2020-10-25 15:06:58 -05:00
|
|
|
newFile._url = 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt';
|
2020-04-02 17:00:15 -04:00
|
|
|
return newFile;
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterSaveFile(req => {
|
2020-04-02 17:00:15 -04:00
|
|
|
expect(req.fileSize).toBe(null);
|
|
|
|
|
});
|
|
|
|
|
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
|
|
|
|
|
const result = await file.save({ useMasterKey: true });
|
|
|
|
|
expect(result).toBe(result);
|
|
|
|
|
expect(result._name).toBe('some-file.txt');
|
2020-10-25 15:06:58 -05:00
|
|
|
expect(result._url).toBe('http://www.somewhere.com/parse/files/some-app-id/some-file.txt');
|
2020-04-02 17:00:15 -04:00
|
|
|
expect(createFileSpy).not.toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('afterSaveFile should throw error', async () => {
|
|
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
|
|
|
|
Parse.Cloud.afterSaveFile(async () => {
|
|
|
|
|
throw new Parse.Error(400, 'some-error-message');
|
|
|
|
|
});
|
|
|
|
|
const filename = 'donald_duck.pdf';
|
|
|
|
|
const file = new Parse.File(filename, [1, 2, 3], 'text/plain');
|
|
|
|
|
try {
|
|
|
|
|
await file.save({ useMasterKey: true });
|
|
|
|
|
} catch (error) {
|
|
|
|
|
expect(error.message).toBe('some-error-message');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('afterSaveFile should call with fileObject', async done => {
|
2020-04-02 17:00:15 -04:00
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSaveFile(async req => {
|
2020-04-02 17:00:15 -04:00
|
|
|
req.file.setTags({ tagA: 'some-tag' });
|
|
|
|
|
req.file.setMetadata({ foo: 'bar' });
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterSaveFile(async req => {
|
2020-04-02 17:00:15 -04:00
|
|
|
expect(req.master).toBe(true);
|
|
|
|
|
expect(req.file._tags).toEqual({ tagA: 'some-tag' });
|
|
|
|
|
expect(req.file._metadata).toEqual({ foo: 'bar' });
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
|
|
|
|
|
await file.save({ useMasterKey: true });
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('afterSaveFile should change fileSize when file data changes', async done => {
|
2020-04-02 17:00:15 -04:00
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSaveFile(async req => {
|
2020-04-02 17:00:15 -04:00
|
|
|
expect(req.fileSize).toBe(3);
|
|
|
|
|
expect(req.master).toBe(true);
|
2020-10-25 15:06:58 -05:00
|
|
|
const newFile = new Parse.File('donald_duck.pdf', [4, 5, 6, 7, 8, 9], 'application/pdf');
|
2020-04-02 17:00:15 -04:00
|
|
|
return newFile;
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterSaveFile(async req => {
|
2020-04-02 17:00:15 -04:00
|
|
|
expect(req.fileSize).toBe(6);
|
|
|
|
|
expect(req.master).toBe(true);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
|
|
|
|
|
await file.save({ useMasterKey: true });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('beforeDeleteFile should call with fileObject', async () => {
|
|
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeDeleteFile(req => {
|
2020-04-02 17:00:15 -04:00
|
|
|
expect(req.file).toBeInstanceOf(Parse.File);
|
|
|
|
|
expect(req.file._name).toEqual('popeye.txt');
|
|
|
|
|
expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt');
|
|
|
|
|
expect(req.fileSize).toBe(null);
|
|
|
|
|
});
|
|
|
|
|
const file = new Parse.File('popeye.txt');
|
|
|
|
|
await file.destroy({ useMasterKey: true });
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('beforeDeleteFile should throw error', async done => {
|
2020-04-02 17:00:15 -04:00
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
|
|
|
|
Parse.Cloud.beforeDeleteFile(() => {
|
|
|
|
|
throw new Error('some error message');
|
|
|
|
|
});
|
|
|
|
|
const file = new Parse.File('popeye.txt');
|
|
|
|
|
try {
|
|
|
|
|
await file.destroy({ useMasterKey: true });
|
|
|
|
|
} catch (error) {
|
|
|
|
|
expect(error.message).toBe('some error message');
|
|
|
|
|
done();
|
|
|
|
|
}
|
2020-10-02 00:19:26 +02:00
|
|
|
});
|
2020-04-02 17:00:15 -04:00
|
|
|
|
2020-10-02 00:19:26 +02:00
|
|
|
it('afterDeleteFile should call with fileObject', async done => {
|
2020-04-02 17:00:15 -04:00
|
|
|
await reconfigureServer({ filesAdapter: mockAdapter });
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeDeleteFile(req => {
|
2020-04-02 17:00:15 -04:00
|
|
|
expect(req.file).toBeInstanceOf(Parse.File);
|
|
|
|
|
expect(req.file._name).toEqual('popeye.txt');
|
|
|
|
|
expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt');
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterDeleteFile(req => {
|
2020-04-02 17:00:15 -04:00
|
|
|
expect(req.file).toBeInstanceOf(Parse.File);
|
|
|
|
|
expect(req.file._name).toEqual('popeye.txt');
|
|
|
|
|
expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt');
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
const file = new Parse.File('popeye.txt');
|
|
|
|
|
await file.destroy({ useMasterKey: true });
|
|
|
|
|
});
|
2019-04-23 16:24:20 +01:00
|
|
|
});
|
2020-02-11 17:38:14 -06:00
|
|
|
|
|
|
|
|
describe('afterLogin hook', () => {
|
|
|
|
|
it('should run afterLogin after successful login', async done => {
|
|
|
|
|
let hit = 0;
|
|
|
|
|
Parse.Cloud.afterLogin(req => {
|
|
|
|
|
hit++;
|
|
|
|
|
expect(req.object.get('username')).toEqual('testuser');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await Parse.User.signUp('testuser', 'p@ssword');
|
|
|
|
|
const user = await Parse.User.logIn('testuser', 'p@ssword');
|
|
|
|
|
expect(hit).toBe(1);
|
|
|
|
|
expect(user).toBeDefined();
|
|
|
|
|
expect(user.getUsername()).toBe('testuser');
|
|
|
|
|
expect(user.getSessionToken()).toBeDefined();
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should not run afterLogin after unsuccessful login', async done => {
|
|
|
|
|
let hit = 0;
|
|
|
|
|
Parse.Cloud.afterLogin(req => {
|
|
|
|
|
hit++;
|
|
|
|
|
expect(req.object.get('username')).toEqual('testuser');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await Parse.User.signUp('testuser', 'p@ssword');
|
|
|
|
|
try {
|
|
|
|
|
await Parse.User.logIn('testuser', 'badpassword');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND);
|
|
|
|
|
}
|
|
|
|
|
expect(hit).toBe(0);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should not run afterLogin on sign up', async done => {
|
|
|
|
|
let hit = 0;
|
|
|
|
|
Parse.Cloud.afterLogin(req => {
|
|
|
|
|
hit++;
|
|
|
|
|
expect(req.object.get('username')).toEqual('testuser');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const user = await Parse.User.signUp('testuser', 'p@ssword');
|
|
|
|
|
expect(user).toBeDefined();
|
|
|
|
|
expect(hit).toBe(0);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should have expected data in request', async done => {
|
|
|
|
|
Parse.Cloud.afterLogin(req => {
|
|
|
|
|
expect(req.object).toBeDefined();
|
|
|
|
|
expect(req.user).toBeDefined();
|
|
|
|
|
expect(req.headers).toBeDefined();
|
|
|
|
|
expect(req.ip).toBeDefined();
|
|
|
|
|
expect(req.installationId).toBeDefined();
|
|
|
|
|
expect(req.context).toBeUndefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await Parse.User.signUp('testuser', 'p@ssword');
|
|
|
|
|
await Parse.User.logIn('testuser', 'p@ssword');
|
|
|
|
|
done();
|
|
|
|
|
});
|
2020-04-28 20:36:46 +02:00
|
|
|
|
2020-07-02 21:37:41 +02:00
|
|
|
it('should have access to context when saving a new object', async () => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('TestObject', req => {
|
2020-04-28 20:36:46 +02:00
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterSave('TestObject', req => {
|
2020-04-28 20:36:46 +02:00
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
|
|
|
|
const obj = new TestObject();
|
|
|
|
|
await obj.save(null, { context: { a: 'a' } });
|
|
|
|
|
});
|
2020-07-02 21:37:41 +02:00
|
|
|
|
|
|
|
|
it('should have access to context when saving an existing object', async () => {
|
|
|
|
|
const obj = new TestObject();
|
|
|
|
|
await obj.save(null);
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('TestObject', req => {
|
2020-07-02 21:37:41 +02:00
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterSave('TestObject', req => {
|
2020-07-02 21:37:41 +02:00
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
|
|
|
|
await obj.save(null, { context: { a: 'a' } });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should have access to context when saving a new object in a trigger', async () => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('TestObject', req => {
|
2020-07-02 21:37:41 +02:00
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterSave('TestObject', req => {
|
2020-07-02 21:37:41 +02:00
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
|
|
|
|
Parse.Cloud.afterSave('TriggerObject', async () => {
|
|
|
|
|
const obj = new TestObject();
|
|
|
|
|
await obj.save(null, { context: { a: 'a' } });
|
|
|
|
|
});
|
|
|
|
|
const obj = new Parse.Object('TriggerObject');
|
|
|
|
|
await obj.save(null);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should have access to context when cascade-saving objects', async () => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('TestObject', req => {
|
2020-07-02 21:37:41 +02:00
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterSave('TestObject', req => {
|
2020-07-02 21:37:41 +02:00
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('TestObject2', req => {
|
2020-07-02 21:37:41 +02:00
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterSave('TestObject2', req => {
|
2020-07-02 21:37:41 +02:00
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
const obj = new Parse.Object('TestObject');
|
|
|
|
|
const obj2 = new Parse.Object('TestObject2');
|
|
|
|
|
obj.set('obj2', obj2);
|
2020-07-02 21:37:41 +02:00
|
|
|
await obj.save(null, { context: { a: 'a' } });
|
|
|
|
|
});
|
2020-07-11 02:17:27 +05:30
|
|
|
|
|
|
|
|
it('should have access to context as saveAll argument', async () => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeSave('TestObject', req => {
|
2020-07-11 02:17:27 +05:30
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterSave('TestObject', req => {
|
2020-07-11 02:17:27 +05:30
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
|
|
|
|
const obj1 = new TestObject();
|
|
|
|
|
const obj2 = new TestObject();
|
2020-10-02 00:19:26 +02:00
|
|
|
await Parse.Object.saveAll([obj1, obj2], { context: { a: 'a' } });
|
2020-07-11 02:17:27 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should have access to context as destroyAll argument', async () => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeDelete('TestObject', req => {
|
2020-07-11 02:17:27 +05:30
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterDelete('TestObject', req => {
|
2020-07-11 02:17:27 +05:30
|
|
|
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 () => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeDelete('TestObject', req => {
|
2020-07-11 02:17:27 +05:30
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.afterDelete('TestObject', req => {
|
2020-07-11 02:17:27 +05:30
|
|
|
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 () => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.beforeFind('TestObject', req => {
|
2020-07-11 02:17:27 +05:30
|
|
|
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 () => {
|
2020-10-02 00:19:26 +02:00
|
|
|
Parse.Cloud.define('contextTest', async req => {
|
2020-07-11 02:17:27 +05:30
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
return {};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await Parse.Cloud.run('contextTest', {}, { context: { a: 'a' } });
|
|
|
|
|
});
|
2020-12-19 00:54:48 +11:00
|
|
|
|
|
|
|
|
it('afterFind should have access to context', async () => {
|
|
|
|
|
Parse.Cloud.afterFind('TestObject', req => {
|
|
|
|
|
expect(req.context.a).toEqual('a');
|
|
|
|
|
});
|
|
|
|
|
const obj = new TestObject();
|
|
|
|
|
await obj.save();
|
|
|
|
|
const query = new Parse.Query(TestObject);
|
|
|
|
|
await query.find({ context: { a: 'a' } });
|
|
|
|
|
});
|
2020-02-11 17:38:14 -06:00
|
|
|
});
|
2020-12-31 03:23:44 +11:00
|
|
|
|
|
|
|
|
describe('sendEmail', () => {
|
|
|
|
|
it('can send email via Parse.Cloud', async done => {
|
|
|
|
|
const emailAdapter = {
|
|
|
|
|
sendMail: mailData => {
|
|
|
|
|
expect(mailData).toBeDefined();
|
|
|
|
|
expect(mailData.to).toBe('test');
|
|
|
|
|
done();
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
await reconfigureServer({
|
|
|
|
|
emailAdapter: emailAdapter,
|
|
|
|
|
});
|
|
|
|
|
const mailData = { to: 'test' };
|
|
|
|
|
await Parse.Cloud.sendEmail(mailData);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('cannot send email without adapter', async () => {
|
|
|
|
|
const logger = require('../lib/logger').logger;
|
|
|
|
|
spyOn(logger, 'error').and.callFake(() => {});
|
|
|
|
|
await Parse.Cloud.sendEmail({});
|
|
|
|
|
expect(logger.error).toHaveBeenCalledWith(
|
|
|
|
|
'Failed to send email because no mail adapter is configured for Parse Server.'
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|