Skip to content

Commit aef2881

Browse files
committed
Merge branch 'master' into liveQueryACLFix
2 parents 9487d9c + ba32dae commit aef2881

File tree

12 files changed

+266
-9
lines changed

12 files changed

+266
-9
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ jobs:
9191
MONGODB_VERSION: 4.4.4
9292
MONGODB_TOPOLOGY: standalone
9393
MONGODB_STORAGE_ENGINE: wiredTiger
94-
NODE_VERSION: 12.21.0
94+
NODE_VERSION: 12.22.0
9595
- name: Node 15
9696
MONGODB_VERSION: 4.4.4
9797
MONGODB_TOPOLOGY: standalone

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ ___
119119
- LDAP: Properly unbind client on group search error (Diamond Lewis) [#7265](https://github.com/parse-community/parse-server/pull/7265)
120120
- Improve data consistency in Push and Job Status update (Diamond Lewis) [#7267](https://github.com/parse-community/parse-server/pull/7267)
121121
- Excluding keys that have trailing edges.node when performing GraphQL resolver (Chris Bland) [#7273](https://github.com/parse-community/parse-server/pull/7273)
122+
- Added centralized feature deprecation with standardized warning logs (Manuel Trezza) [#7303](https://github.com/parse-community/parse-server/pull/7303)
122123
___
123124
## 4.5.0
124125
[Full Changelog](https://github.com/parse-community/parse-server/compare/4.4.0...4.5.0)

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,9 @@ Parse Server is continuously tested with the most recent releases of Node.js to
113113
| Version | Latest Patch Version | End-of-Life Date | Compatibility |
114114
|------------|----------------------|------------------|--------------------|
115115
| Node.js 10 | 10.24.0 | April 2021 | ✅ Fully compatible |
116-
| Node.js 12 | 12.21.0 | April 2022 | ✅ Fully compatible |
116+
| Node.js 12 | 12.22.0 | April 2022 | ✅ Fully compatible |
117117
| Node.js 14 | 14.16.0 | April 2023 | ✅ Fully compatible |
118-
| Node.js 15 | 15.12.0 | June 2021 | ✅ Fully compatible |
118+
| Node.js 15 | 15.12.0 | June 2021 | ✅ Fully compatible |
119119

120120
#### MongoDB
121121
Parse Server is continuously tested with the most recent releases of MongoDB to ensure compatibility. We follow the [MongoDB support schedule](https://www.mongodb.com/support-policy) and only test against versions that are officially supported and have not reached their end-of-life date.
@@ -300,7 +300,7 @@ For the full list of available options, run `parse-server --help` or take a look
300300
* `port` - The default port is 1337, specify this parameter to use a different port.
301301
* `serverURL` - URL to your Parse Server (don't forget to specify http:// or https://). This URL will be used when making requests to Parse Server from Cloud Code.
302302
* `cloud` - The absolute path to your cloud code `main.js` file.
303-
* `push` - Configuration options for APNS and GCM push. See the [Push Notifications quick start](http://docs.parseplatform.org/parse-server/guide/#push-notifications_push-notifications-quick-start).
303+
* `push` - Configuration options for APNS and GCM push. See the [Push Notifications quick start](https://docs.parseplatform.org/parse-server/guide/#push-notifications-quick-start).
304304

305305
## Client Key Options
306306

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"uuid": "8.3.2",
6161
"winston": "3.3.3",
6262
"winston-daily-rotate-file": "4.5.0",
63-
"ws": "7.4.3"
63+
"ws": "7.4.4"
6464
},
6565
"devDependencies": {
6666
"@actions/core": "1.2.6",

spec/Deprecator.spec.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict';
2+
3+
const Deprecator = require('../lib/Deprecator/Deprecator');
4+
5+
describe('Deprecator', () => {
6+
let deprecations = [];
7+
8+
beforeEach(async () => {
9+
deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }];
10+
});
11+
12+
it('deprecations are an array', async () => {
13+
expect(Deprecator._getDeprecations()).toBeInstanceOf(Array);
14+
});
15+
16+
it('logs deprecation for new default', async () => {
17+
deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }];
18+
19+
spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations);
20+
const logger = require('../lib/logger').logger;
21+
const logSpy = spyOn(logger, 'warn').and.callFake(() => {});
22+
23+
await reconfigureServer();
24+
expect(logSpy.calls.all()[0].args[0]).toContain(deprecations[0].optionKey);
25+
expect(logSpy.calls.all()[0].args[0]).toContain(deprecations[0].changeNewDefault);
26+
});
27+
28+
it('does not log deprecation for new default if option is set manually', async () => {
29+
deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }];
30+
31+
spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations);
32+
const logSpy = spyOn(Deprecator, '_log').and.callFake(() => {});
33+
await reconfigureServer({ [deprecations[0].optionKey]: 'manuallySet' });
34+
expect(logSpy).not.toHaveBeenCalled();
35+
});
36+
});

spec/ParseGraphQLServer.spec.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7083,6 +7083,77 @@ describe('ParseGraphQLServer', () => {
70837083
expect(result.data.resetPassword.clientMutationId).toEqual(clientMutationId);
70847084
expect(result.data.resetPassword.ok).toBeTruthy();
70857085
});
7086+
7087+
it('should reset password', async () => {
7088+
const clientMutationId = uuidv4();
7089+
let resetPasswordToken;
7090+
const emailAdapter = {
7091+
sendVerificationEmail: () => {},
7092+
sendPasswordResetEmail: ({ link }) => {
7093+
resetPasswordToken = link.split('token=')[1].split('&')[0];
7094+
},
7095+
sendMail: () => {},
7096+
};
7097+
parseServer = await global.reconfigureServer({
7098+
appName: 'test',
7099+
emailAdapter: emailAdapter,
7100+
publicServerURL: 'http://localhost:13377/parse',
7101+
auth: {
7102+
myAuth: {
7103+
module: global.mockCustomAuthenticator('parse', 'graphql'),
7104+
},
7105+
},
7106+
});
7107+
const user = new Parse.User();
7108+
user.setUsername('user1');
7109+
user.setPassword('user1');
7110+
user.setEmail('user1@user1.user1');
7111+
await user.signUp();
7112+
await Parse.User.logOut();
7113+
await Parse.User.requestPasswordReset('user1@user1.user1');
7114+
await apolloClient.mutate({
7115+
mutation: gql`
7116+
mutation ConfirmResetPassword($input: ConfirmResetPasswordInput!) {
7117+
confirmResetPassword(input: $input) {
7118+
clientMutationId
7119+
ok
7120+
}
7121+
}
7122+
`,
7123+
variables: {
7124+
input: {
7125+
clientMutationId,
7126+
username: 'user1',
7127+
password: 'newPassword',
7128+
token: resetPasswordToken,
7129+
},
7130+
},
7131+
});
7132+
const result = await apolloClient.mutate({
7133+
mutation: gql`
7134+
mutation LogInUser($input: LogInInput!) {
7135+
logIn(input: $input) {
7136+
clientMutationId
7137+
viewer {
7138+
sessionToken
7139+
}
7140+
}
7141+
}
7142+
`,
7143+
variables: {
7144+
input: {
7145+
clientMutationId,
7146+
username: 'user1',
7147+
password: 'newPassword',
7148+
},
7149+
},
7150+
});
7151+
7152+
expect(result.data.logIn.clientMutationId).toEqual(clientMutationId);
7153+
expect(result.data.logIn.viewer.sessionToken).toBeDefined();
7154+
expect(typeof result.data.logIn.viewer.sessionToken).toBe('string');
7155+
});
7156+
70867157
it('should send verification email again', async () => {
70877158
const clientMutationId = uuidv4();
70887159
const emailAdapter = {

src/Deprecator/Deprecations.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* The deprecations.
3+
*
4+
* Add deprecations to the array using the following keys:
5+
* - `optionKey`: The option key incl. its path, e.g. `security.enableCheck`.
6+
* - `envKey`: The environment key, e.g. `PARSE_SERVER_SECURITY`.
7+
* - `changeNewKey`: Set the new key name if the current key will be replaced,
8+
* or set to an empty string if the current key will be removed without replacement.
9+
* - `changeNewDefault`: Set the new default value if the key's default value
10+
* will change in a future version.
11+
*
12+
* If there are no deprecations this must return an empty array anyway.
13+
*/
14+
module.exports = [];

src/Deprecator/Deprecator.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import logger from '../logger';
2+
import Deprecations from './Deprecations';
3+
4+
/**
5+
* The deprecator class.
6+
*/
7+
class Deprecator {
8+
/**
9+
* Scans the Parse Server for deprecated options.
10+
* This needs to be called before setting option defaults, otherwise it
11+
* becomes indistinguishable whether an option has been set manually or
12+
* by default.
13+
* @param {any} options The Parse Server options.
14+
*/
15+
static scanParseServerOptions(options) {
16+
// Scan for deprecations
17+
for (const deprecation of Deprecator._getDeprecations()) {
18+
// Get deprecation properties
19+
const optionKey = deprecation.optionKey;
20+
const changeNewDefault = deprecation.changeNewDefault;
21+
22+
// If default will change, only throw a warning if option is not set
23+
if (changeNewDefault != null && options[optionKey] == null) {
24+
Deprecator._log({ optionKey, changeNewDefault });
25+
}
26+
}
27+
}
28+
29+
/**
30+
* Returns the deprecation definitions.
31+
* @returns {Array<Object>} The deprecations.
32+
*/
33+
static _getDeprecations() {
34+
return Deprecations;
35+
}
36+
37+
/**
38+
* Logs a deprecation warning for a Parse Server option.
39+
* @param {String} optionKey The option key incl. its path, e.g. `security.enableCheck`.
40+
* @param {String} envKey The environment key, e.g. `PARSE_SERVER_SECURITY`.
41+
* @param {String} changeNewKey Set the new key name if the current key will be replaced,
42+
* or set to an empty string if the current key will be removed without replacement.
43+
* @param {String} changeNewDefault Set the new default value if the key's default value
44+
* will change in a future version.
45+
* @param {String} [solution] The instruction to resolve this deprecation warning. This
46+
* message must not include the warning that the parameter is deprecated, that is
47+
* automatically added to the message. It should only contain the instruction on how
48+
* to resolve this warning.
49+
*/
50+
static _log({ optionKey, envKey, changeNewKey, changeNewDefault, solution }) {
51+
const type = optionKey ? 'option' : 'environment key';
52+
const key = optionKey ? optionKey : envKey;
53+
const keyAction =
54+
changeNewKey == null
55+
? undefined
56+
: changeNewKey.length > 0
57+
? `renamed to '${changeNewKey}'`
58+
: `removed`;
59+
60+
// Compose message
61+
let output = `DeprecationWarning: The Parse Server ${type} '${key}' `;
62+
output += changeNewKey ? `is deprecated and will be ${keyAction} in a future version.` : '';
63+
output += changeNewDefault
64+
? `default will change to '${changeNewDefault}' in a future version.`
65+
: '';
66+
output += solution ? ` ${solution}` : '';
67+
logger.warn(output);
68+
}
69+
}
70+
71+
module.exports = Deprecator;

src/GraphQL/loaders/usersMutations.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as objectsMutations from '../helpers/objectsMutations';
55
import { OBJECT } from './defaultGraphQLTypes';
66
import { getUserFromSessionToken } from './usersQueries';
77
import { transformTypes } from '../transformers/mutation';
8+
import Parse from 'parse/node';
89

910
const usersRouter = new UsersRouter();
1011

@@ -250,6 +251,61 @@ const load = parseGraphQLSchema => {
250251
parseGraphQLSchema.addGraphQLType(resetPasswordMutation.type, true, true);
251252
parseGraphQLSchema.addGraphQLMutation('resetPassword', resetPasswordMutation, true, true);
252253

254+
const confirmResetPasswordMutation = mutationWithClientMutationId({
255+
name: 'ConfirmResetPassword',
256+
description:
257+
'The confirmResetPassword mutation can be used to reset the password of an existing user.',
258+
inputFields: {
259+
username: {
260+
descriptions: 'Username of the user that have received the reset email',
261+
type: new GraphQLNonNull(GraphQLString),
262+
},
263+
password: {
264+
descriptions: 'New password of the user',
265+
type: new GraphQLNonNull(GraphQLString),
266+
},
267+
token: {
268+
descriptions: 'Reset token that was emailed to the user',
269+
type: new GraphQLNonNull(GraphQLString),
270+
},
271+
},
272+
outputFields: {
273+
ok: {
274+
description: "It's always true.",
275+
type: new GraphQLNonNull(GraphQLBoolean),
276+
},
277+
},
278+
mutateAndGetPayload: async ({ username, password, token }, context) => {
279+
const { config } = context;
280+
if (!username) {
281+
throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'you must provide a username');
282+
}
283+
if (!password) {
284+
throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'you must provide a password');
285+
}
286+
if (!token) {
287+
throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'you must provide a token');
288+
}
289+
290+
const userController = config.userController;
291+
await userController.updatePassword(username, token, password);
292+
return { ok: true };
293+
},
294+
});
295+
296+
parseGraphQLSchema.addGraphQLType(
297+
confirmResetPasswordMutation.args.input.type.ofType,
298+
true,
299+
true
300+
);
301+
parseGraphQLSchema.addGraphQLType(confirmResetPasswordMutation.type, true, true);
302+
parseGraphQLSchema.addGraphQLMutation(
303+
'confirmResetPassword',
304+
confirmResetPasswordMutation,
305+
true,
306+
true
307+
);
308+
253309
const sendVerificationEmailMutation = mutationWithClientMutationId({
254310
name: 'SendVerificationEmail',
255311
description:

src/ParseServer.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import * as controllers from './Controllers';
4343
import { ParseGraphQLServer } from './GraphQL/ParseGraphQLServer';
4444
import { SecurityRouter } from './Routers/SecurityRouter';
4545
import CheckRunner from './Security/CheckRunner';
46+
import Deprecator from './Deprecator/Deprecator';
4647

4748
// Mutate the Parse object to add the Cloud Code handlers
4849
addParseCloud();
@@ -55,6 +56,9 @@ class ParseServer {
5556
* @param {ParseServerOptions} options the parse server initialization options
5657
*/
5758
constructor(options: ParseServerOptions) {
59+
// Scan for deprecated Parse Server options
60+
Deprecator.scanParseServerOptions(options);
61+
// Set option defaults
5862
injectDefaults(options);
5963
const {
6064
appId = requiredParameter('You must provide an appId!'),

src/cli/utils/commander.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* eslint-disable no-console */
22
import { Command } from 'commander';
33
import path from 'path';
4+
import Deprecator from '../../Deprecator/Deprecator';
5+
46
let _definitions;
57
let _reverseDefinitions;
68
let _defaults;
@@ -40,7 +42,7 @@ Command.prototype.loadDefinitions = function (definitions) {
4042
}, {});
4143

4244
_defaults = Object.keys(definitions).reduce((defs, opt) => {
43-
if (_definitions[opt].default) {
45+
if (_definitions[opt].default !== undefined) {
4446
defs[opt] = _definitions[opt].default;
4547
}
4648
return defs;
@@ -119,6 +121,8 @@ Command.prototype.parse = function (args, env) {
119121
this.setValuesIfNeeded(envOptions);
120122
// Load from file to override
121123
this.setValuesIfNeeded(fromFile);
124+
// Scan for deprecated Parse Server options
125+
Deprecator.scanParseServerOptions(this);
122126
// Last set the defaults
123127
this.setValuesIfNeeded(_defaults);
124128
};

0 commit comments

Comments
 (0)