Skip to content

Commit e212eb5

Browse files
authored
refactor: Add option to convert Parse.Object to instance in Cloud Function payload (#8656)
1 parent 4ad0800 commit e212eb5

24 files changed

+466
-157
lines changed

DEPRECATIONS.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ The following is a list of deprecations, according to the [Deprecation Policy](h
1313
| DEPPS7 | Remove file trigger syntax `Parse.Cloud.beforeSaveFile((request) => {})` | [#7966](https://github.com/parse-community/parse-server/pull/7966) | 5.3.0 (2022) | 7.0.0 (2024) | deprecated | - |
1414
| DEPPS8 | Login with expired 3rd party authentication token defaults to `false` | [#7079](https://github.com/parse-community/parse-server/pull/7079) | 5.3.0 (2022) | 7.0.0 (2024) | deprecated | - |
1515
| DEPPS9 | Rename LiveQuery `fields` option to `keys` | [#8389](https://github.com/parse-community/parse-server/issues/8389) | 6.0.0 (2023) | 7.0.0 (2024) | deprecated | - |
16+
| DEPPS10 | Config option `encodeParseObjectInCloudFunction` defaults to `true` | [#8634](https://github.com/parse-community/parse-server/issues/8634) | 6.2.0 (2023) | 7.0.0 (2024) | deprecated | - |
1617

1718
[i_deprecation]: ## "The version and date of the deprecation."
1819
[i_removal]: ## "The version and date of the planned removal."

changelogs/CHANGELOG_alpha.md

+32
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,35 @@
1+
# [6.3.0-alpha.2](https://github.com/parse-community/parse-server/compare/6.3.0-alpha.1...6.3.0-alpha.2) (2023-06-20)
2+
3+
4+
### Features
5+
6+
* Add conditional email verification via dynamic Parse Server options `verifyUserEmails`, `sendUserEmailVerification` that now accept functions ([#8425](https://github.com/parse-community/parse-server/issues/8425)) ([44acd6d](https://github.com/parse-community/parse-server/commit/44acd6d9ed157ad4842200c9d01f9c77a05fec3a))
7+
8+
# [6.3.0-alpha.1](https://github.com/parse-community/parse-server/compare/6.2.0...6.3.0-alpha.1) (2023-06-18)
9+
10+
11+
### Bug Fixes
12+
13+
* Cloud Code Trigger `afterSave` executes even if not set ([#8520](https://github.com/parse-community/parse-server/issues/8520)) ([afd0515](https://github.com/parse-community/parse-server/commit/afd0515e207bd947840579d3f245980dffa6f804))
14+
* GridFS file storage doesn't work with certain `enableSchemaHooks` settings ([#8467](https://github.com/parse-community/parse-server/issues/8467)) ([d4cda4b](https://github.com/parse-community/parse-server/commit/d4cda4b26c9bde8c812549b8780bea1cfabdb394))
15+
* Inaccurate table total row count for PostgreSQL ([#8511](https://github.com/parse-community/parse-server/issues/8511)) ([0823a02](https://github.com/parse-community/parse-server/commit/0823a02fbf80bc88dc403bc47e9f5c6597ea78b4))
16+
* LiveQuery server is not shut down properly when `handleShutdown` is called ([#8491](https://github.com/parse-community/parse-server/issues/8491)) ([967700b](https://github.com/parse-community/parse-server/commit/967700bdbc94c74f75ba84d2b3f4b9f3fd2dca0b))
17+
* Rate limit feature is incompatible with Node 14 ([#8578](https://github.com/parse-community/parse-server/issues/8578)) ([f911f2c](https://github.com/parse-community/parse-server/commit/f911f2cd3a8c45cd326272dcd681532764a3761e))
18+
* Unnecessary log entries by `extendSessionOnUse` ([#8562](https://github.com/parse-community/parse-server/issues/8562)) ([fd6a007](https://github.com/parse-community/parse-server/commit/fd6a0077f2e5cf83d65e52172ae5a950ab0f1eae))
19+
20+
### Features
21+
22+
* `extendSessionOnUse` to automatically renew Parse Sessions ([#8505](https://github.com/parse-community/parse-server/issues/8505)) ([6f885d3](https://github.com/parse-community/parse-server/commit/6f885d36b94902fdfea873fc554dee83589e6029))
23+
* Add new Parse Server option `preventSignupWithUnverifiedEmail` to prevent returning a user without session token on sign-up with unverified email address ([#8451](https://github.com/parse-community/parse-server/issues/8451)) ([82da308](https://github.com/parse-community/parse-server/commit/82da30842a55980aa90cb7680fbf6db37ee16dab))
24+
* Add option to change the log level of logs emitted by Cloud Functions ([#8530](https://github.com/parse-community/parse-server/issues/8530)) ([2caea31](https://github.com/parse-community/parse-server/commit/2caea310be412d82b04a85716bc769ccc410316d))
25+
* Add support for `$eq` query constraint in LiveQuery ([#8614](https://github.com/parse-community/parse-server/issues/8614)) ([656d673](https://github.com/parse-community/parse-server/commit/656d673cf5dea354e4f2b3d4dc2b29a41d311b3e))
26+
* Add zones for rate limiting by `ip`, `user`, `session`, `global` ([#8508](https://github.com/parse-community/parse-server/issues/8508)) ([03fba97](https://github.com/parse-community/parse-server/commit/03fba97e0549bfcaeee9f2fa4c9905dbcc91840e))
27+
* Allow `Parse.Object` pointers in Cloud Code arguments ([#8490](https://github.com/parse-community/parse-server/issues/8490)) ([28aeda3](https://github.com/parse-community/parse-server/commit/28aeda3f160efcbbcf85a85484a8d26567fa9761))
28+
29+
### Reverts
30+
31+
* fix: Inaccurate table total row count for PostgreSQL ([6722110](https://github.com/parse-community/parse-server/commit/6722110f203bc5fdcaa68cdf091cf9e7b48d1cff))
32+
133
# [6.1.0-alpha.20](https://github.com/parse-community/parse-server/compare/6.1.0-alpha.19...6.1.0-alpha.20) (2023-06-09)
234

335

jsdoc-conf.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"template": "./node_modules/clean-jsdoc-theme",
3030
"theme_opts": {
3131
"default_theme": "dark",
32-
"title": "Parse Server",
32+
"title": "<img src='../.github/parse-server-logo.png' class='logo'/>",
3333
"create_style": "header, .sidebar-section-title, .sidebar-title { color: #139cee !important } .logo { margin-left : 40px; margin-right: 40px }"
3434
}
3535
},

package-lock.json

+14-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "parse-server",
3-
"version": "6.3.0-beta.1",
3+
"version": "6.3.0-alpha.2",
44
"description": "An express module providing a Parse-compatible API server",
55
"main": "lib/index.js",
66
"repository": {
@@ -55,7 +55,7 @@
5555
"pluralize": "8.0.0",
5656
"rate-limit-redis": "3.0.2",
5757
"redis": "4.6.6",
58-
"semver": "7.5.1",
58+
"semver": "7.5.2",
5959
"subscriptions-transport-ws": "0.11.0",
6060
"tv4": "1.3.0",
6161
"uuid": "9.0.0",

postinstall.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const pkg = require('./package.json');
22

3-
const version = parseFloat(process.version.substr(1));
3+
const version = parseFloat(process.version.substring(1));
44
const minimum = parseFloat(pkg.engines.node.match(/\d+/g).join('.'));
55

66
module.exports = function () {

release_docs.sh

+5
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,8 @@ npm run docs
2727

2828
mkdir -p "docs/api/${DEST}"
2929
cp -R out/* "docs/api/${DEST}"
30+
31+
# Copy other resources
32+
RESOURCE_DIR=".github"
33+
mkdir -p "docs/${RESOURCE_DIR}"
34+
cp "./.github/parse-server-logo.png" "docs/${RESOURCE_DIR}/"

resources/buildConfigDefinitions.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,16 @@ function inject(t, list) {
255255
props.push(t.objectProperty(t.stringLiteral('action'), action));
256256
}
257257
if (elt.defaultValue) {
258-
const parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
258+
let parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
259+
if (!parsedValue) {
260+
for (const type of elt.typeAnnotation.types) {
261+
elt.type = type.type;
262+
parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
263+
if (parsedValue) {
264+
break;
265+
}
266+
}
267+
}
259268
if (parsedValue) {
260269
props.push(t.objectProperty(t.stringLiteral('default'), parsedValue));
261270
} else {

spec/CloudCode.spec.js

+20
Original file line numberDiff line numberDiff line change
@@ -1353,7 +1353,27 @@ describe('Cloud Code', () => {
13531353
});
13541354
});
13551355

1356+
it('should not encode Parse Objects', async () => {
1357+
const user = new Parse.User();
1358+
user.setUsername('username');
1359+
user.setPassword('password');
1360+
user.set('deleted', false);
1361+
await user.signUp();
1362+
Parse.Cloud.define(
1363+
'deleteAccount',
1364+
async req => {
1365+
expect(req.params.object instanceof Parse.Object).not.toBeTrue();
1366+
return 'Object deleted';
1367+
},
1368+
{
1369+
requireMaster: true,
1370+
}
1371+
);
1372+
await Parse.Cloud.run('deleteAccount', { object: user.toPointer() }, { useMasterKey: true });
1373+
});
1374+
13561375
it('allow cloud to encode Parse Objects', async () => {
1376+
await reconfigureServer({ encodeParseObjectInCloudFunction: true });
13571377
const user = new Parse.User();
13581378
user.setUsername('username');
13591379
user.setPassword('password');

spec/EmailVerificationToken.spec.js

+178
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,184 @@ describe('Email Verification Token Expiration: ', () => {
288288
});
289289
});
290290

291+
it('can conditionally send emails', async () => {
292+
let sendEmailOptions;
293+
const emailAdapter = {
294+
sendVerificationEmail: options => {
295+
sendEmailOptions = options;
296+
},
297+
sendPasswordResetEmail: () => Promise.resolve(),
298+
sendMail: () => {},
299+
};
300+
const verifyUserEmails = {
301+
method(req) {
302+
expect(Object.keys(req)).toEqual(['original', 'object', 'master', 'ip']);
303+
return false;
304+
},
305+
};
306+
const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough();
307+
await reconfigureServer({
308+
appName: 'emailVerifyToken',
309+
verifyUserEmails: verifyUserEmails.method,
310+
emailAdapter: emailAdapter,
311+
emailVerifyTokenValidityDuration: 5, // 5 seconds
312+
publicServerURL: 'http://localhost:8378/1',
313+
});
314+
const beforeSave = {
315+
method(req) {
316+
req.object.set('emailVerified', true);
317+
},
318+
};
319+
const saveSpy = spyOn(beforeSave, 'method').and.callThrough();
320+
const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough();
321+
Parse.Cloud.beforeSave(Parse.User, beforeSave.method);
322+
const user = new Parse.User();
323+
user.setUsername('sets_email_verify_token_expires_at');
324+
user.setPassword('expiringToken');
325+
user.set('email', '[email protected]');
326+
await user.signUp();
327+
328+
const config = Config.get('test');
329+
const results = await config.database.find(
330+
'_User',
331+
{
332+
username: 'sets_email_verify_token_expires_at',
333+
},
334+
{},
335+
Auth.maintenance(config)
336+
);
337+
338+
expect(results.length).toBe(1);
339+
const user_data = results[0];
340+
expect(typeof user_data).toBe('object');
341+
expect(user_data.emailVerified).toEqual(true);
342+
expect(user_data._email_verify_token).toBeUndefined();
343+
expect(user_data._email_verify_token_expires_at).toBeUndefined();
344+
expect(emailSpy).not.toHaveBeenCalled();
345+
expect(saveSpy).toHaveBeenCalled();
346+
expect(sendEmailOptions).toBeUndefined();
347+
expect(verifySpy).toHaveBeenCalled();
348+
});
349+
350+
it('can conditionally send emails and allow conditional login', async () => {
351+
let sendEmailOptions;
352+
const emailAdapter = {
353+
sendVerificationEmail: options => {
354+
sendEmailOptions = options;
355+
},
356+
sendPasswordResetEmail: () => Promise.resolve(),
357+
sendMail: () => {},
358+
};
359+
const verifyUserEmails = {
360+
method(req) {
361+
expect(Object.keys(req)).toEqual(['original', 'object', 'master', 'ip']);
362+
if (req.object.get('username') === 'no_email') {
363+
return false;
364+
}
365+
return true;
366+
},
367+
};
368+
const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough();
369+
await reconfigureServer({
370+
appName: 'emailVerifyToken',
371+
verifyUserEmails: verifyUserEmails.method,
372+
preventLoginWithUnverifiedEmail: verifyUserEmails.method,
373+
emailAdapter: emailAdapter,
374+
emailVerifyTokenValidityDuration: 5, // 5 seconds
375+
publicServerURL: 'http://localhost:8378/1',
376+
});
377+
const user = new Parse.User();
378+
user.setUsername('no_email');
379+
user.setPassword('expiringToken');
380+
user.set('email', '[email protected]');
381+
await user.signUp();
382+
expect(sendEmailOptions).toBeUndefined();
383+
expect(user.getSessionToken()).toBeDefined();
384+
expect(verifySpy).toHaveBeenCalledTimes(2);
385+
const user2 = new Parse.User();
386+
user2.setUsername('email');
387+
user2.setPassword('expiringToken');
388+
user2.set('email', '[email protected]');
389+
await user2.signUp();
390+
expect(user2.getSessionToken()).toBeUndefined();
391+
expect(sendEmailOptions).toBeDefined();
392+
expect(verifySpy).toHaveBeenCalledTimes(4);
393+
});
394+
395+
it('can conditionally send user email verification', async () => {
396+
const emailAdapter = {
397+
sendVerificationEmail: () => {},
398+
sendPasswordResetEmail: () => Promise.resolve(),
399+
sendMail: () => {},
400+
};
401+
const sendVerificationEmail = {
402+
method(req) {
403+
expect(req.user).toBeDefined();
404+
expect(req.master).toBeDefined();
405+
return false;
406+
},
407+
};
408+
const sendSpy = spyOn(sendVerificationEmail, 'method').and.callThrough();
409+
await reconfigureServer({
410+
appName: 'emailVerifyToken',
411+
verifyUserEmails: true,
412+
emailAdapter: emailAdapter,
413+
emailVerifyTokenValidityDuration: 5, // 5 seconds
414+
publicServerURL: 'http://localhost:8378/1',
415+
sendUserEmailVerification: sendVerificationEmail.method,
416+
});
417+
const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough();
418+
const newUser = new Parse.User();
419+
newUser.setUsername('unsets_email_verify_token_expires_at');
420+
newUser.setPassword('expiringToken');
421+
newUser.set('email', '[email protected]');
422+
await newUser.signUp();
423+
await Parse.User.requestEmailVerification('[email protected]');
424+
expect(sendSpy).toHaveBeenCalledTimes(2);
425+
expect(emailSpy).toHaveBeenCalledTimes(0);
426+
});
427+
428+
it('beforeSave options do not change existing behaviour', async () => {
429+
let sendEmailOptions;
430+
const emailAdapter = {
431+
sendVerificationEmail: options => {
432+
sendEmailOptions = options;
433+
},
434+
sendPasswordResetEmail: () => Promise.resolve(),
435+
sendMail: () => {},
436+
};
437+
await reconfigureServer({
438+
appName: 'emailVerifyToken',
439+
verifyUserEmails: true,
440+
emailAdapter: emailAdapter,
441+
emailVerifyTokenValidityDuration: 5, // 5 seconds
442+
publicServerURL: 'http://localhost:8378/1',
443+
});
444+
const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough();
445+
const newUser = new Parse.User();
446+
newUser.setUsername('unsets_email_verify_token_expires_at');
447+
newUser.setPassword('expiringToken');
448+
newUser.set('email', '[email protected]');
449+
await newUser.signUp();
450+
const response = await request({
451+
url: sendEmailOptions.link,
452+
followRedirects: false,
453+
});
454+
expect(response.status).toEqual(302);
455+
const config = Config.get('test');
456+
const results = await config.database.find('_User', {
457+
username: 'unsets_email_verify_token_expires_at',
458+
});
459+
460+
expect(results.length).toBe(1);
461+
const user = results[0];
462+
expect(typeof user).toBe('object');
463+
expect(user.emailVerified).toEqual(true);
464+
expect(typeof user._email_verify_token).toBe('undefined');
465+
expect(typeof user._email_verify_token_expires_at).toBe('undefined');
466+
expect(emailSpy).toHaveBeenCalled();
467+
});
468+
291469
it('unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful', done => {
292470
const user = new Parse.User();
293471
let sendEmailOptions;

0 commit comments

Comments
 (0)