Skip to content

Commit 597b25c

Browse files
committed
added server option validation; refactored pages server option
1 parent 76fe0ca commit 597b25c

File tree

7 files changed

+91
-16
lines changed

7 files changed

+91
-16
lines changed

resources/buildConfigDefinitions.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,14 @@ function getCommentValue(comment) {
4242
function getENVPrefix(iface) {
4343
const options = {
4444
'ParseServerOptions' : 'PARSE_SERVER_',
45+
'PagesOptions' : 'PARSE_SERVER_PAGES_',
4546
'CustomPagesOptions' : 'PARSE_SERVER_CUSTOM_PAGES_',
4647
'LiveQueryServerOptions' : 'PARSE_LIVE_QUERY_SERVER_',
4748
'LiveQueryOptions' : 'PARSE_SERVER_LIVEQUERY_',
4849
'IdempotencyOptions' : 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_',
4950
'AccountLockoutOptions' : 'PARSE_SERVER_ACCOUNT_LOCKOUT_',
5051
'PasswordPolicyOptions' : 'PARSE_SERVER_PASSWORD_POLICY_',
51-
'FileUploadOptions' : 'PARSE_SERVER_FILE_UPLOAD_'
52+
'FileUploadOptions' : 'PARSE_SERVER_FILE_UPLOAD_',
5253
}
5354
if (options[iface.id.name]) {
5455
return options[iface.id.name]
@@ -164,7 +165,7 @@ function parseDefaultValue(elt, value, t) {
164165
if (type == 'NumberOrBoolean') {
165166
literalValue = t.numericLiteral(parsers.numberOrBoolParser('')(value));
166167
}
167-
const literalTypes = ['IdempotencyOptions','FileUploadOptions','CustomPagesOptions'];
168+
const literalTypes = ['IdempotencyOptions','FileUploadOptions','CustomPagesOptions', 'PagesOptions'];
168169
if (literalTypes.includes(type)) {
169170
const object = parsers.objectParser(value);
170171
const props = Object.keys(object).map((key) => {

spec/PublicAPI.spec.js

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
const request = require('../lib/request');
44
const fs = require('fs').promises;
55
const Utils = require('../lib/Utils');
6+
const Config = require('../lib/Config');
7+
const Definitions = require('../lib/Options/Definitions');
68
const { PublicAPIRouter, pages, pageParams } = require('../lib/Routers/PublicAPIRouter');
79

810
describe('public API', () => {
@@ -191,9 +193,19 @@ describe('public API', () => {
191193
sendMail: () => {},
192194
},
193195
publicServerURL: 'http://localhost:8378/1',
194-
enablePageLocalization: true,
195196
customPages: {},
197+
pages: {
198+
enableLocalization: true,
199+
},
196200
};
201+
async function reconfigureServerWithPageOptions(options) {
202+
await reconfigureServer({
203+
appId: Parse.applicationId,
204+
masterKey: Parse.masterKey,
205+
serverURL: Parse.serverURL,
206+
pages: options,
207+
});
208+
}
197209

198210
beforeEach(async () => {
199211
router = new PublicAPIRouter();
@@ -206,15 +218,40 @@ describe('public API', () => {
206218
appId: 'test',
207219
appName: 'ExampleAppName',
208220
publicServerURL: 'http://localhost:8378/1',
209-
enablePageLocalization: true,
210221
customPages: {},
222+
pages: {
223+
enableLocalization: true,
224+
},
211225
},
212226
query: {
213227
locale: 'de-AT',
214228
},
215229
};
216230
});
217231

232+
describe('server options', () => {
233+
it('uses default configuration when none is set', async () => {
234+
await reconfigureServerWithPageOptions({});
235+
expect(Config.get(Parse.applicationId).pages.enableLocalization).toBe(
236+
Definitions.PagesOptions.enableLocalization.default
237+
);
238+
});
239+
240+
it('throws on invalid configuration', async () => {
241+
const options = [
242+
[],
243+
'a',
244+
0,
245+
{ enableLocalization: 'a' },
246+
{ enableLocalization: 0 },
247+
{ enableLocalization: {} },
248+
];
249+
for (const option of options) {
250+
await expectAsync(reconfigureServerWithPageOptions(option)).toBeRejected();
251+
}
252+
});
253+
});
254+
218255
describe('placeholders', () => {
219256
it('replaces placeholder in response content', async () => {
220257
await expectAsync(router.goToPage(req, pages.invalidLink)).toBeResolved();
@@ -246,7 +283,7 @@ describe('public API', () => {
246283

247284
describe('localization', () => {
248285
it('returns default file if localization is disabled', async () => {
249-
delete req.config.enablePageLocalization;
286+
delete req.config.pages.enableLocalization;
250287

251288
await expectAsync(router.goToPage(req, pages.invalidLink)).toBeResolved();
252289
expect(pageResponse.calls.all()[0].args[0]).toBeDefined();

src/Config.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import net from 'net';
99
import {
1010
IdempotencyOptions,
1111
FileUploadOptions,
12+
PagesOptions,
1213
} from './Options/Definitions';
14+
import { isBoolean } from 'lodash';
1315

1416
function removeTrailingSlash(str) {
1517
if (!str) {
@@ -75,6 +77,7 @@ export class Config {
7577
idempotencyOptions,
7678
emailVerifyTokenReuseIfValid,
7779
fileUpload,
80+
pages,
7881
}) {
7982
if (masterKey === readOnlyMasterKey) {
8083
throw new Error('masterKey and readOnlyMasterKey should be different');
@@ -109,6 +112,21 @@ export class Config {
109112
this.validateMaxLimit(maxLimit);
110113
this.validateAllowHeaders(allowHeaders);
111114
this.validateIdempotencyOptions(idempotencyOptions);
115+
this.validatePagesOptions(pages);
116+
}
117+
118+
static validatePagesOptions(pages) {
119+
if (pages === undefined) {
120+
return;
121+
}
122+
if (Object.prototype.toString.call(pages) !== '[object Object]') {
123+
throw 'Parse Server option pages must be an object.';
124+
}
125+
if (pages.enableLocalization === undefined) {
126+
pages.enableLocalization = PagesOptions.enableLocalization.default;
127+
} else if (!isBoolean(pages.enableLocalization)) {
128+
throw 'Parse Server option pages.enableLocalization must be a boolean.';
129+
}
112130
}
113131

114132
static validateIdempotencyOptions(idempotencyOptions) {

src/Options/Definitions.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,6 @@ module.exports.ParseServerOptions = {
146146
"action": parsers.booleanParser,
147147
"default": false
148148
},
149-
"enablePageLocalization": {
150-
"env": "PARSE_SERVER_ENABLE_PAGE_LOCALIZATION",
151-
"help": "Is true if pages should be localized; customPages must not be set.",
152-
"action": parsers.booleanParser,
153-
"default": false
154-
},
155149
"enableSingleSchemaCache": {
156150
"env": "PARSE_SERVER_ENABLE_SINGLE_SCHEMA_CACHE",
157151
"help": "Use a single schema cache shared across requests. Reduces number of queries made to _SCHEMA, defaults to false, i.e. unique schema cache per request.",
@@ -289,6 +283,12 @@ module.exports.ParseServerOptions = {
289283
"action": parsers.numberParser("objectIdSize"),
290284
"default": 10
291285
},
286+
"pages": {
287+
"env": "PARSE_SERVER_PAGES",
288+
"help": "The options for pages such as password reset and email verification.",
289+
"action": parsers.objectParser,
290+
"default": {}
291+
},
292292
"passwordPolicy": {
293293
"env": "PARSE_SERVER_PASSWORD_POLICY",
294294
"help": "Password policy for enforcing password related rules",
@@ -412,6 +412,14 @@ module.exports.ParseServerOptions = {
412412
"help": "Key sent with outgoing webhook calls"
413413
}
414414
};
415+
module.exports.PagesOptions = {
416+
"enableLocalization": {
417+
"env": "PARSE_SERVER_PAGES_ENABLE_LOCALIZATION",
418+
"help": "Is true if pages should be localized; this has no effect on custom page redirects.",
419+
"action": parsers.booleanParser,
420+
"default": false
421+
}
422+
};
415423
module.exports.CustomPagesOptions = {
416424
"choosePassword": {
417425
"env": "PARSE_SERVER_CUSTOM_PAGES_CHOOSE_PASSWORD",

src/Options/docs.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
* @property {Number} emailVerifyTokenValidityDuration Email verification token validity duration, in seconds
2828
* @property {Boolean} enableAnonymousUsers Enable (or disable) anonymous users, defaults to true
2929
* @property {Boolean} enableExpressErrorHandler Enables the default express error handler for all errors
30-
* @property {Boolean} enablePageLocalization Is true if pages should be localized; customPages must not be set.
3130
* @property {Boolean} enableSingleSchemaCache Use a single schema cache shared across requests. Reduces number of queries made to _SCHEMA, defaults to false, i.e. unique schema cache per request.
3231
* @property {String} encryptionKey Key for encrypting your files
3332
* @property {Boolean} expireInactiveSessions Sets wether we should expire the inactive sessions, defaults to true
@@ -55,6 +54,7 @@
5554
* @property {String} mountPath Mount path for the server, defaults to /parse
5655
* @property {Boolean} mountPlayground Mounts the GraphQL Playground - never use this option in production
5756
* @property {Number} objectIdSize Sets the number of characters in generated object id's, default 10
57+
* @property {PagesOptions} pages The options for pages such as password reset and email verification.
5858
* @property {PasswordPolicyOptions} passwordPolicy Password policy for enforcing password related rules
5959
* @property {String} playgroundPath Mount path for the GraphQL Playground, defaults to /playground
6060
* @property {Number} port The port to run the ParseServer, defaults to 1337.
@@ -80,6 +80,11 @@
8080
* @property {String} webhookKey Key sent with outgoing webhook calls
8181
*/
8282

83+
/**
84+
* @interface PagesOptions
85+
* @property {Boolean} enableLocalization Is true if pages should be localized; this has no effect on custom page redirects.
86+
*/
87+
8388
/**
8489
* @interface CustomPagesOptions
8590
* @property {String} choosePassword choose password page path

src/Options/index.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,12 @@ export interface ParseServerOptions {
138138
/* Public URL to your parse server with http:// or https://.
139139
:ENV: PARSE_PUBLIC_SERVER_URL */
140140
publicServerURL: ?string;
141+
/* The options for pages such as password reset and email verification.
142+
:DEFAULT:{} */
143+
pages: ?PagesOptions;
141144
/* custom pages for password validation and reset
142145
:DEFAULT: {} */
143146
customPages: ?CustomPagesOptions;
144-
/* Is true if pages should be localized; customPages must not be set.
145-
:DEFAULT: false */
146-
enablePageLocalization: ?boolean;
147147
/* parse-server's LiveQuery configuration object */
148148
liveQuery: ?LiveQueryOptions;
149149
/* Session duration, in seconds, defaults to 1 year
@@ -229,6 +229,12 @@ export interface ParseServerOptions {
229229
serverCloseComplete: ?() => void;
230230
}
231231

232+
export interface PagesOptions {
233+
/* Is true if pages should be localized; this has no effect on custom page redirects.
234+
:DEFAULT: false */
235+
enableLocalization: ?boolean;
236+
}
237+
232238
export interface CustomPagesOptions {
233239
/* invalid link page path */
234240
invalidLink: ?string;

src/Routers/PublicAPIRouter.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ export class PublicAPIRouter extends PromiseRouter {
250250
}
251251

252252
// If localization is enabled
253-
if (config.enablePageLocalization && locale) {
253+
if (config.pages.enableLocalization && locale) {
254254
return Utils.getLocalizedPath(defaultPath, locale).then(({ path, subdir }) =>
255255
redirect
256256
? this.redirectResponse(

0 commit comments

Comments
 (0)