Skip to content

Commit

Permalink
refactor: Add option to convert Parse.Object to instance in Cloud F…
Browse files Browse the repository at this point in the history
…unction payload (#8656)
  • Loading branch information
mtrezza committed Jun 23, 2023
1 parent 4ad0800 commit e212eb5
Show file tree
Hide file tree
Showing 24 changed files with 466 additions and 157 deletions.
1 change: 1 addition & 0 deletions DEPRECATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The following is a list of deprecations, according to the [Deprecation Policy](h
| 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 | - |
| 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 | - |
| 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 | - |
| 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 | - |

[i_deprecation]: ## "The version and date of the deprecation."
[i_removal]: ## "The version and date of the planned removal."
Expand Down
32 changes: 32 additions & 0 deletions changelogs/CHANGELOG_alpha.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,35 @@
# [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)


### Features

* 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))

# [6.3.0-alpha.1](https://github.com/parse-community/parse-server/compare/6.2.0...6.3.0-alpha.1) (2023-06-18)


### Bug Fixes

* 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))
* 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))
* 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))
* 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))
* 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))
* 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))

### Features

* `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))
* 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))
* 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))
* 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))
* 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))
* 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))

### Reverts

* fix: Inaccurate table total row count for PostgreSQL ([6722110](https://github.com/parse-community/parse-server/commit/6722110f203bc5fdcaa68cdf091cf9e7b48d1cff))

# [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)


Expand Down
2 changes: 1 addition & 1 deletion jsdoc-conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"template": "./node_modules/clean-jsdoc-theme",
"theme_opts": {
"default_theme": "dark",
"title": "Parse Server",
"title": "<img src='../.github/parse-server-logo.png' class='logo'/>",
"create_style": "header, .sidebar-section-title, .sidebar-title { color: #139cee !important } .logo { margin-left : 40px; margin-right: 40px }"
}
},
Expand Down
23 changes: 14 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "parse-server",
"version": "6.3.0-beta.1",
"version": "6.3.0-alpha.2",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
Expand Down Expand Up @@ -55,7 +55,7 @@
"pluralize": "8.0.0",
"rate-limit-redis": "3.0.2",
"redis": "4.6.6",
"semver": "7.5.1",
"semver": "7.5.2",
"subscriptions-transport-ws": "0.11.0",
"tv4": "1.3.0",
"uuid": "9.0.0",
Expand Down
2 changes: 1 addition & 1 deletion postinstall.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const pkg = require('./package.json');

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

module.exports = function () {
Expand Down
5 changes: 5 additions & 0 deletions release_docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,8 @@ npm run docs

mkdir -p "docs/api/${DEST}"
cp -R out/* "docs/api/${DEST}"

# Copy other resources
RESOURCE_DIR=".github"
mkdir -p "docs/${RESOURCE_DIR}"
cp "./.github/parse-server-logo.png" "docs/${RESOURCE_DIR}/"
11 changes: 10 additions & 1 deletion resources/buildConfigDefinitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,16 @@ function inject(t, list) {
props.push(t.objectProperty(t.stringLiteral('action'), action));
}
if (elt.defaultValue) {
const parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
let parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
if (!parsedValue) {
for (const type of elt.typeAnnotation.types) {
elt.type = type.type;
parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
if (parsedValue) {
break;
}
}
}
if (parsedValue) {
props.push(t.objectProperty(t.stringLiteral('default'), parsedValue));
} else {
Expand Down
20 changes: 20 additions & 0 deletions spec/CloudCode.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1353,7 +1353,27 @@ describe('Cloud Code', () => {
});
});

it('should not encode Parse Objects', async () => {
const user = new Parse.User();
user.setUsername('username');
user.setPassword('password');
user.set('deleted', false);
await user.signUp();
Parse.Cloud.define(
'deleteAccount',
async req => {
expect(req.params.object instanceof Parse.Object).not.toBeTrue();
return 'Object deleted';
},
{
requireMaster: true,
}
);
await Parse.Cloud.run('deleteAccount', { object: user.toPointer() }, { useMasterKey: true });
});

it('allow cloud to encode Parse Objects', async () => {
await reconfigureServer({ encodeParseObjectInCloudFunction: true });
const user = new Parse.User();
user.setUsername('username');
user.setPassword('password');
Expand Down
178 changes: 178 additions & 0 deletions spec/EmailVerificationToken.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,184 @@ describe('Email Verification Token Expiration: ', () => {
});
});

it('can conditionally send emails', async () => {
let sendEmailOptions;
const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
},
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
};
const verifyUserEmails = {
method(req) {
expect(Object.keys(req)).toEqual(['original', 'object', 'master', 'ip']);
return false;
},
};
const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough();
await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: verifyUserEmails.method,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 5, // 5 seconds
publicServerURL: 'http://localhost:8378/1',
});
const beforeSave = {
method(req) {
req.object.set('emailVerified', true);
},
};
const saveSpy = spyOn(beforeSave, 'method').and.callThrough();
const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough();
Parse.Cloud.beforeSave(Parse.User, beforeSave.method);
const user = new Parse.User();
user.setUsername('sets_email_verify_token_expires_at');
user.setPassword('expiringToken');
user.set('email', 'user@example.com');
await user.signUp();

const config = Config.get('test');
const results = await config.database.find(
'_User',
{
username: 'sets_email_verify_token_expires_at',
},
{},
Auth.maintenance(config)
);

expect(results.length).toBe(1);
const user_data = results[0];
expect(typeof user_data).toBe('object');
expect(user_data.emailVerified).toEqual(true);
expect(user_data._email_verify_token).toBeUndefined();
expect(user_data._email_verify_token_expires_at).toBeUndefined();
expect(emailSpy).not.toHaveBeenCalled();
expect(saveSpy).toHaveBeenCalled();
expect(sendEmailOptions).toBeUndefined();
expect(verifySpy).toHaveBeenCalled();
});

it('can conditionally send emails and allow conditional login', async () => {
let sendEmailOptions;
const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
},
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
};
const verifyUserEmails = {
method(req) {
expect(Object.keys(req)).toEqual(['original', 'object', 'master', 'ip']);
if (req.object.get('username') === 'no_email') {
return false;
}
return true;
},
};
const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough();
await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: verifyUserEmails.method,
preventLoginWithUnverifiedEmail: verifyUserEmails.method,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 5, // 5 seconds
publicServerURL: 'http://localhost:8378/1',
});
const user = new Parse.User();
user.setUsername('no_email');
user.setPassword('expiringToken');
user.set('email', 'user@example.com');
await user.signUp();
expect(sendEmailOptions).toBeUndefined();
expect(user.getSessionToken()).toBeDefined();
expect(verifySpy).toHaveBeenCalledTimes(2);
const user2 = new Parse.User();
user2.setUsername('email');
user2.setPassword('expiringToken');
user2.set('email', 'user2@example.com');
await user2.signUp();
expect(user2.getSessionToken()).toBeUndefined();
expect(sendEmailOptions).toBeDefined();
expect(verifySpy).toHaveBeenCalledTimes(4);
});

it('can conditionally send user email verification', async () => {
const emailAdapter = {
sendVerificationEmail: () => {},
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
};
const sendVerificationEmail = {
method(req) {
expect(req.user).toBeDefined();
expect(req.master).toBeDefined();
return false;
},
};
const sendSpy = spyOn(sendVerificationEmail, 'method').and.callThrough();
await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 5, // 5 seconds
publicServerURL: 'http://localhost:8378/1',
sendUserEmailVerification: sendVerificationEmail.method,
});
const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough();
const newUser = new Parse.User();
newUser.setUsername('unsets_email_verify_token_expires_at');
newUser.setPassword('expiringToken');
newUser.set('email', 'user@example.com');
await newUser.signUp();
await Parse.User.requestEmailVerification('user@example.com');
expect(sendSpy).toHaveBeenCalledTimes(2);
expect(emailSpy).toHaveBeenCalledTimes(0);
});

it('beforeSave options do not change existing behaviour', async () => {
let sendEmailOptions;
const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
},
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
};
await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 5, // 5 seconds
publicServerURL: 'http://localhost:8378/1',
});
const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough();
const newUser = new Parse.User();
newUser.setUsername('unsets_email_verify_token_expires_at');
newUser.setPassword('expiringToken');
newUser.set('email', 'user@parse.com');
await newUser.signUp();
const response = await request({
url: sendEmailOptions.link,
followRedirects: false,
});
expect(response.status).toEqual(302);
const config = Config.get('test');
const results = await config.database.find('_User', {
username: 'unsets_email_verify_token_expires_at',
});

expect(results.length).toBe(1);
const user = results[0];
expect(typeof user).toBe('object');
expect(user.emailVerified).toEqual(true);
expect(typeof user._email_verify_token).toBe('undefined');
expect(typeof user._email_verify_token_expires_at).toBe('undefined');
expect(emailSpy).toHaveBeenCalled();
});

it('unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful', done => {
const user = new Parse.User();
let sendEmailOptions;
Expand Down

0 comments on commit e212eb5

Please sign in to comment.