From 607b01954152af46948f528e9e8785d320d7e600 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:46:02 +0200 Subject: [PATCH 01/15] Update ParseConfigKey.spec.js --- spec/ParseConfigKey.spec.js | 125 +++++++++++++++++++++++------------- 1 file changed, 80 insertions(+), 45 deletions(-) diff --git a/spec/ParseConfigKey.spec.js b/spec/ParseConfigKey.spec.js index 84b2fc6e2f..5ddaaa92cd 100644 --- a/spec/ParseConfigKey.spec.js +++ b/spec/ParseConfigKey.spec.js @@ -1,52 +1,87 @@ const Config = require('../lib/Config'); -const ParseServer = require('../lib/index').ParseServer; describe('Config Keys', () => { - const tests = [ - { - name: 'Invalid Root Keys', - options: { unknow: 'val', masterKeyIPs: '' }, - error: 'unknow, masterKeyIPs', - }, - { name: 'Invalid Schema Keys', options: { schema: { Strict: 'val' } }, error: 'schema.Strict' }, - { - name: 'Invalid Pages Keys', - options: { pages: { customUrls: { EmailVerificationSendFail: 'val' } } }, - error: 'pages.customUrls.EmailVerificationSendFail', - }, - { - name: 'Invalid LiveQueryServerOptions Keys', - options: { liveQueryServerOptions: { MasterKey: 'value' } }, - error: 'liveQueryServerOptions.MasterKey', - }, - { - name: 'Invalid RateLimit Keys - Array Item', - options: { rateLimit: [{ RequestPath: '' }, { RequestTimeWindow: '' }] }, - error: 'rateLimit[0].RequestPath, rateLimit[1].RequestTimeWindow', - }, - ]; - - tests.forEach(test => { - it(test.name, async () => { - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callThrough(); - spyOn(Config, 'validateOptions').and.callFake(() => {}); - - new ParseServer({ - ...defaultConfiguration, - ...test.options, - }); - expect(logger.error).toHaveBeenCalledWith(`Invalid Option Keys Found: ${test.error}`); - }); + let loggerErrorSpy; + + beforeEach(async () => { + const logger = require('../lib/logger').logger; + loggerErrorSpy = spyOn(logger, 'error').and.callThrough(); + spyOn(Config, 'validateOptions').and.callFake(() => {}); + }); + + it('recognizes invalid keys in root', async () => { + await expectAsync(reconfigureServer({ + ...defaultConfiguration, + invalidKey: 1, + })).toBeResolved(); + const error = loggerErrorSpy.calls.all().reduce((string, call) => string = call.args[0], ''); + expect(error).toMatch(`Invalid Option Keys Found`); + }); + + it('recognizes invalid keys in pages.customUrls', async () => { + await expectAsync(reconfigureServer({ + ...defaultConfiguration, + pages: { + customUrls: { + invalidKey: 1, + EmailVerificationSendFail: 1, + } + } + })).toBeResolved(); + const error = loggerErrorSpy.calls.all().reduce((string, call) => string = call.args[0], ''); + expect(error).toMatch(`Invalid Option Keys Found`); + expect(error).toMatch(`invalidKey`); + expect(error).toMatch(`EmailVerificationSendFail`); + }); + + it('recognizes invalid keys in liveQueryServerOptions', async () => { + await expectAsync(reconfigureServer({ + ...defaultConfiguration, + liveQueryServerOptions: { + invalidKey: 1, + MasterKey: 1, + } + })).toBeResolved(); + const error = loggerErrorSpy.calls.all().reduce((string, call) => string = call.args[0], ''); + expect(error).toMatch(`Invalid Option Keys Found`); + expect(error).toMatch(`MasterKey`); + }); + + it('recognizes invalid keys in rateLimit', async () => { + await expectAsync(reconfigureServer({ + ...defaultConfiguration, + rateLimit: [ + { invalidKey: 1 }, + { RequestPath: 1 }, + { RequestTimeWindow: 1 }, + ] + })).toBeRejected(); + const error = loggerErrorSpy.calls.all().reduce((string, call) => string = call.args[0], ''); + expect(error).toMatch('Invalid Option Keys Found'); + expect(error).toMatch('rateLimit\\[0\\]\\.invalidKey'); + expect(error).toMatch('rateLimit\\[1\\]\\.RequestPath'); + expect(error).toMatch('rateLimit\\[2\\]\\.RequestTimeWindow'); + }); + + it('recognizes valid keys in default configuration', async () => { + await expectAsync(reconfigureServer({ + ...defaultConfiguration, + })).toBeResolved(); + expect(loggerErrorSpy.calls.all().reduce((string, call) => string = call.args[0], '')).not.toMatch(`Invalid Option Keys Found`); }); - it('should run fine', async () => { - try { - await reconfigureServer({ - ...defaultConfiguration, - }); - } catch (err) { - fail('Should run without error'); - } + it('recognizes valid keys in databaseOptions', async () => { + await expectAsync(reconfigureServer({ + databaseURI: 'mongodb://localhost:27017/parse', + filesAdapter: null, + databaseAdapter: null, + databaseOptions: { + retryWrites: true, + maxTimeMS: 1000, + maxStalenessSeconds: 10, + maxPoolSize: 10, + }, + })).toBeResolved(); + expect(loggerErrorSpy.calls.all().reduce((string, call) => string = call.args[0], '')).not.toMatch(`Invalid Option Keys Found`); }); }); From b5f7b87087b6e375c51c3f4c58420fcae6b0c683 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:50:23 +0200 Subject: [PATCH 02/15] fix error string concat --- spec/ParseConfigKey.spec.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/ParseConfigKey.spec.js b/spec/ParseConfigKey.spec.js index 5ddaaa92cd..73e327d0d9 100644 --- a/spec/ParseConfigKey.spec.js +++ b/spec/ParseConfigKey.spec.js @@ -14,7 +14,7 @@ describe('Config Keys', () => { ...defaultConfiguration, invalidKey: 1, })).toBeResolved(); - const error = loggerErrorSpy.calls.all().reduce((string, call) => string = call.args[0], ''); + const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); expect(error).toMatch(`Invalid Option Keys Found`); }); @@ -28,7 +28,7 @@ describe('Config Keys', () => { } } })).toBeResolved(); - const error = loggerErrorSpy.calls.all().reduce((string, call) => string = call.args[0], ''); + const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); expect(error).toMatch(`Invalid Option Keys Found`); expect(error).toMatch(`invalidKey`); expect(error).toMatch(`EmailVerificationSendFail`); @@ -42,7 +42,7 @@ describe('Config Keys', () => { MasterKey: 1, } })).toBeResolved(); - const error = loggerErrorSpy.calls.all().reduce((string, call) => string = call.args[0], ''); + const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); expect(error).toMatch(`Invalid Option Keys Found`); expect(error).toMatch(`MasterKey`); }); @@ -56,7 +56,7 @@ describe('Config Keys', () => { { RequestTimeWindow: 1 }, ] })).toBeRejected(); - const error = loggerErrorSpy.calls.all().reduce((string, call) => string = call.args[0], ''); + const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); expect(error).toMatch('Invalid Option Keys Found'); expect(error).toMatch('rateLimit\\[0\\]\\.invalidKey'); expect(error).toMatch('rateLimit\\[1\\]\\.RequestPath'); @@ -67,7 +67,7 @@ describe('Config Keys', () => { await expectAsync(reconfigureServer({ ...defaultConfiguration, })).toBeResolved(); - expect(loggerErrorSpy.calls.all().reduce((string, call) => string = call.args[0], '')).not.toMatch(`Invalid Option Keys Found`); + expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(`Invalid Option Keys Found`); }); it('recognizes valid keys in databaseOptions', async () => { @@ -82,6 +82,6 @@ describe('Config Keys', () => { maxPoolSize: 10, }, })).toBeResolved(); - expect(loggerErrorSpy.calls.all().reduce((string, call) => string = call.args[0], '')).not.toMatch(`Invalid Option Keys Found`); + expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(`Invalid Option Keys Found`); }); }); From 1c9957de20802d7072b65ecdadba123cdf2fcd8c Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:50:31 +0200 Subject: [PATCH 03/15] fit --- spec/ParseConfigKey.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/ParseConfigKey.spec.js b/spec/ParseConfigKey.spec.js index 73e327d0d9..2f7417384d 100644 --- a/spec/ParseConfigKey.spec.js +++ b/spec/ParseConfigKey.spec.js @@ -1,6 +1,6 @@ const Config = require('../lib/Config'); -describe('Config Keys', () => { +fdescribe('Config Keys', () => { let loggerErrorSpy; beforeEach(async () => { From 287a6960df7fe21caf0162bc6742d7843c84af46 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:52:41 +0200 Subject: [PATCH 04/15] rephrase error log --- spec/ParseConfigKey.spec.js | 14 +++++++------- src/ParseServer.js | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/ParseConfigKey.spec.js b/spec/ParseConfigKey.spec.js index 2f7417384d..e6a9b81f6d 100644 --- a/spec/ParseConfigKey.spec.js +++ b/spec/ParseConfigKey.spec.js @@ -15,7 +15,7 @@ fdescribe('Config Keys', () => { invalidKey: 1, })).toBeResolved(); const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); - expect(error).toMatch(`Invalid Option Keys Found`); + expect(error).toMatch(`Unknown key(s) found in Parse Server configuration`); }); it('recognizes invalid keys in pages.customUrls', async () => { @@ -29,7 +29,7 @@ fdescribe('Config Keys', () => { } })).toBeResolved(); const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); - expect(error).toMatch(`Invalid Option Keys Found`); + expect(error).toMatch(`Unknown key(s) found in Parse Server configuration`); expect(error).toMatch(`invalidKey`); expect(error).toMatch(`EmailVerificationSendFail`); }); @@ -43,7 +43,7 @@ fdescribe('Config Keys', () => { } })).toBeResolved(); const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); - expect(error).toMatch(`Invalid Option Keys Found`); + expect(error).toMatch(`Unknown key(s) found in Parse Server configuration`); expect(error).toMatch(`MasterKey`); }); @@ -57,7 +57,7 @@ fdescribe('Config Keys', () => { ] })).toBeRejected(); const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); - expect(error).toMatch('Invalid Option Keys Found'); + expect(error).toMatch('Unknown key(s) found in Parse Server configuration'); expect(error).toMatch('rateLimit\\[0\\]\\.invalidKey'); expect(error).toMatch('rateLimit\\[1\\]\\.RequestPath'); expect(error).toMatch('rateLimit\\[2\\]\\.RequestTimeWindow'); @@ -67,10 +67,10 @@ fdescribe('Config Keys', () => { await expectAsync(reconfigureServer({ ...defaultConfiguration, })).toBeResolved(); - expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(`Invalid Option Keys Found`); + expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(`Unknown key(s) found in Parse Server configuration`); }); - it('recognizes valid keys in databaseOptions', async () => { + fit('recognizes valid keys in databaseOptions', async () => { await expectAsync(reconfigureServer({ databaseURI: 'mongodb://localhost:27017/parse', filesAdapter: null, @@ -82,6 +82,6 @@ fdescribe('Config Keys', () => { maxPoolSize: 10, }, })).toBeResolved(); - expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(`Invalid Option Keys Found`); + expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(`Unknown key(s) found in Parse Server configuration`); }); }); diff --git a/src/ParseServer.js b/src/ParseServer.js index c9aa19af3b..49c178d219 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -109,7 +109,7 @@ class ParseServer { const diff = validateKeyNames(options, optionsBlueprint); if (diff.length > 0) { const logger = logging.logger; - logger.error(`Invalid Option Keys Found: ${diff.join(', ')}`); + logger.error(`Unknown key(s) found in Parse Server configuration: ${diff.join(', ')}`); } // Set option defaults From 48570c399c96edd071bbd3a6456380004835c5ca Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:53:45 +0200 Subject: [PATCH 05/15] rephrase 2 --- spec/ParseConfigKey.spec.js | 12 ++++++------ src/ParseServer.js | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/ParseConfigKey.spec.js b/spec/ParseConfigKey.spec.js index e6a9b81f6d..d9bbd143a7 100644 --- a/spec/ParseConfigKey.spec.js +++ b/spec/ParseConfigKey.spec.js @@ -15,7 +15,7 @@ fdescribe('Config Keys', () => { invalidKey: 1, })).toBeResolved(); const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); - expect(error).toMatch(`Unknown key(s) found in Parse Server configuration`); + expect(error).toMatch(`Invalid key(s) found in Parse Server configuration`); }); it('recognizes invalid keys in pages.customUrls', async () => { @@ -29,7 +29,7 @@ fdescribe('Config Keys', () => { } })).toBeResolved(); const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); - expect(error).toMatch(`Unknown key(s) found in Parse Server configuration`); + expect(error).toMatch(`Invalid key(s) found in Parse Server configuration`); expect(error).toMatch(`invalidKey`); expect(error).toMatch(`EmailVerificationSendFail`); }); @@ -43,7 +43,7 @@ fdescribe('Config Keys', () => { } })).toBeResolved(); const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); - expect(error).toMatch(`Unknown key(s) found in Parse Server configuration`); + expect(error).toMatch(`Invalid key(s) found in Parse Server configuration`); expect(error).toMatch(`MasterKey`); }); @@ -57,7 +57,7 @@ fdescribe('Config Keys', () => { ] })).toBeRejected(); const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); - expect(error).toMatch('Unknown key(s) found in Parse Server configuration'); + expect(error).toMatch('Invalid key(s) found in Parse Server configuration'); expect(error).toMatch('rateLimit\\[0\\]\\.invalidKey'); expect(error).toMatch('rateLimit\\[1\\]\\.RequestPath'); expect(error).toMatch('rateLimit\\[2\\]\\.RequestTimeWindow'); @@ -67,7 +67,7 @@ fdescribe('Config Keys', () => { await expectAsync(reconfigureServer({ ...defaultConfiguration, })).toBeResolved(); - expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(`Unknown key(s) found in Parse Server configuration`); + expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(`Invalid key(s) found in Parse Server configuration`); }); fit('recognizes valid keys in databaseOptions', async () => { @@ -82,6 +82,6 @@ fdescribe('Config Keys', () => { maxPoolSize: 10, }, })).toBeResolved(); - expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(`Unknown key(s) found in Parse Server configuration`); + expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(`Invalid key(s) found in Parse Server configuration`); }); }); diff --git a/src/ParseServer.js b/src/ParseServer.js index 49c178d219..712b7ab3eb 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -109,7 +109,7 @@ class ParseServer { const diff = validateKeyNames(options, optionsBlueprint); if (diff.length > 0) { const logger = logging.logger; - logger.error(`Unknown key(s) found in Parse Server configuration: ${diff.join(', ')}`); + logger.error(`Invalid key(s) found in Parse Server configuration: ${diff.join(', ')}`); } // Set option defaults From b5cf3b6af12fdba8e2607956b01ba1b1e1799dda Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:55:48 +0200 Subject: [PATCH 06/15] test refactor --- spec/ParseConfigKey.spec.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/spec/ParseConfigKey.spec.js b/spec/ParseConfigKey.spec.js index d9bbd143a7..47d6914b8d 100644 --- a/spec/ParseConfigKey.spec.js +++ b/spec/ParseConfigKey.spec.js @@ -1,6 +1,7 @@ const Config = require('../lib/Config'); fdescribe('Config Keys', () => { + const invalidKeyErrorMessage = 'Invalid key(s) found in Parse Server configuration'; let loggerErrorSpy; beforeEach(async () => { @@ -15,7 +16,7 @@ fdescribe('Config Keys', () => { invalidKey: 1, })).toBeResolved(); const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); - expect(error).toMatch(`Invalid key(s) found in Parse Server configuration`); + expect(error).toMatch(invalidKeyErrorMessage); }); it('recognizes invalid keys in pages.customUrls', async () => { @@ -29,7 +30,7 @@ fdescribe('Config Keys', () => { } })).toBeResolved(); const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); - expect(error).toMatch(`Invalid key(s) found in Parse Server configuration`); + expect(error).toMatch(invalidKeyErrorMessage); expect(error).toMatch(`invalidKey`); expect(error).toMatch(`EmailVerificationSendFail`); }); @@ -43,7 +44,7 @@ fdescribe('Config Keys', () => { } })).toBeResolved(); const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); - expect(error).toMatch(`Invalid key(s) found in Parse Server configuration`); + expect(error).toMatch(invalidKeyErrorMessage); expect(error).toMatch(`MasterKey`); }); @@ -67,7 +68,7 @@ fdescribe('Config Keys', () => { await expectAsync(reconfigureServer({ ...defaultConfiguration, })).toBeResolved(); - expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(`Invalid key(s) found in Parse Server configuration`); + expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(invalidKeyErrorMessage); }); fit('recognizes valid keys in databaseOptions', async () => { @@ -82,6 +83,6 @@ fdescribe('Config Keys', () => { maxPoolSize: 10, }, })).toBeResolved(); - expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(`Invalid key(s) found in Parse Server configuration`); + expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(invalidKeyErrorMessage); }); }); From ef67380c3ac37f6d24a2f009863df1bef3b26888 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:59:25 +0200 Subject: [PATCH 07/15] fix regex --- spec/ParseConfigKey.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/ParseConfigKey.spec.js b/spec/ParseConfigKey.spec.js index 47d6914b8d..606c24bbaf 100644 --- a/spec/ParseConfigKey.spec.js +++ b/spec/ParseConfigKey.spec.js @@ -1,7 +1,7 @@ const Config = require('../lib/Config'); fdescribe('Config Keys', () => { - const invalidKeyErrorMessage = 'Invalid key(s) found in Parse Server configuration'; + const invalidKeyErrorMessage = 'Invalid key\\(s\\) found in Parse Server configuration'; let loggerErrorSpy; beforeEach(async () => { @@ -58,7 +58,7 @@ fdescribe('Config Keys', () => { ] })).toBeRejected(); const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); - expect(error).toMatch('Invalid key(s) found in Parse Server configuration'); + expect(error).toMatch(invalidKeyErrorMessage); expect(error).toMatch('rateLimit\\[0\\]\\.invalidKey'); expect(error).toMatch('rateLimit\\[1\\]\\.RequestPath'); expect(error).toMatch('rateLimit\\[2\\]\\.RequestTimeWindow'); @@ -71,7 +71,7 @@ fdescribe('Config Keys', () => { expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(invalidKeyErrorMessage); }); - fit('recognizes valid keys in databaseOptions', async () => { + it('recognizes valid keys in databaseOptions', async () => { await expectAsync(reconfigureServer({ databaseURI: 'mongodb://localhost:27017/parse', filesAdapter: null, From 8c3b1004fc2a8ba8ddf507227aeef1fba81c8fdd Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 16:28:19 +0200 Subject: [PATCH 08/15] add utils --- spec/Utils.spec.js | 49 ++++++++++++++++++++++++++++++++++++++++++++++ src/Utils.js | 27 +++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 spec/Utils.spec.js diff --git a/spec/Utils.spec.js b/spec/Utils.spec.js new file mode 100644 index 0000000000..3aa31a74b0 --- /dev/null +++ b/spec/Utils.spec.js @@ -0,0 +1,49 @@ +const Utils = require('../src/Utils'); + +describe('Utils', () => { + describe('addNestedKeysToRoot', () => { + it('should move the nested keys to root of object', async () => { + const obj = { + a: 1, + b: { + c: 2, + d: 3 + }, + e: 4 + }; + Utils.addNestedKeysToRoot(obj, 'b'); + expect(obj).toEqual({ + a: 1, + c: 2, + d: 3, + e: 4 + }); + }); + + it('should not modify the object if the key does not exist', async () => { + const obj = { + a: 1, + e: 4 + }; + Utils.addNestedKeysToRoot(obj, 'b'); + expect(obj).toEqual({ + a: 1, + e: 4 + }); + }); + + it('should not modify the object if the key is not an object', () => { + const obj = { + a: 1, + b: 2, + e: 4 + }; + Utils.addNestedKeysToRoot(obj, 'b'); + expect(obj).toEqual({ + a: 1, + b: 2, + e: 4 + }); + }); + }); +}); diff --git a/src/Utils.js b/src/Utils.js index efeae58f3f..05ff107b38 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -370,6 +370,33 @@ class Utils { } } } + + /** + * Moves the nested keys of a specified key in an object to the root of the object. + * + * @param {object} obj The object to modify. + * @param {String} key The key whose nested keys will be moved to root. + * @example + * const obj = { + * a: 1, + * b: { + * c: 2, + * d: 3 + * }, + * e: 4 + * }; + * addNestedKeysToRoot(obj, 'b'); + * console.log(obj); + * // Output: { a: 1, e: 4, c: 2, d: 3 } + */ + static addNestedKeysToRoot(obj, key) { + if (obj[key] && typeof obj[key] === 'object') { + // Add nested keys to root + Object.assign(obj, { ...obj[key] }); + // Delete original nested key + delete obj[key]; + } + } } module.exports = Utils; From 81ecf51672061a7fa4d9c08d59219b777eeb31c8 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 16:44:07 +0200 Subject: [PATCH 09/15] Update Utils.js --- src/Utils.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Utils.js b/src/Utils.js index 05ff107b38..b77a3d85d7 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -374,8 +374,9 @@ class Utils { /** * Moves the nested keys of a specified key in an object to the root of the object. * - * @param {object} obj The object to modify. + * @param {Object} obj The object to modify. * @param {String} key The key whose nested keys will be moved to root. + * @returns {Object} The modified object, or the original object if no modification happened. * @example * const obj = { * a: 1, @@ -396,6 +397,7 @@ class Utils { // Delete original nested key delete obj[key]; } + return obj; } } From 5cce5e5604b5163c79cf7d4c30267f8cf9412268 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 16:44:24 +0200 Subject: [PATCH 10/15] use unsafe driver options --- src/ParseServer.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ParseServer.js b/src/ParseServer.js index 712b7ab3eb..cf0111df3e 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -46,6 +46,7 @@ import CheckRunner from './Security/CheckRunner'; import Deprecator from './Deprecator/Deprecator'; import { DefinedSchemas } from './SchemaMigrations/DefinedSchemas'; import OptionsDefinitions from './Options/Definitions'; +import Utils from './Utils'; // Mutate the Parse object to add the Cloud Code handlers addParseCloud(); @@ -112,6 +113,11 @@ class ParseServer { logger.error(`Invalid key(s) found in Parse Server configuration: ${diff.join(', ')}`); } + // Move unsafe database driver options + if (options.databaseOptions !== undefined) { + options.databaseOptions = Utils.addNestedKeysToRoot(options.databaseOptions, 'unsafeDriverOptions'); + } + // Set option defaults injectDefaults(options); const { From 76af4e9b1376cd182805a3be369f638ffce43ce8 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 16:55:21 +0200 Subject: [PATCH 11/15] remove unsafeDriverOptions --- src/ParseServer.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ParseServer.js b/src/ParseServer.js index cf0111df3e..9c0596af54 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -113,11 +113,6 @@ class ParseServer { logger.error(`Invalid key(s) found in Parse Server configuration: ${diff.join(', ')}`); } - // Move unsafe database driver options - if (options.databaseOptions !== undefined) { - options.databaseOptions = Utils.addNestedKeysToRoot(options.databaseOptions, 'unsafeDriverOptions'); - } - // Set option defaults injectDefaults(options); const { From eab81088ca13a34982690450358ea0eb45d6b372 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 16:55:25 +0200 Subject: [PATCH 12/15] add missing options --- src/Options/Definitions.js | 23 +++++++++++++++++++++++ src/Options/docs.js | 4 ++++ src/Options/index.js | 8 ++++++++ 3 files changed, 35 insertions(+) diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index 1fd028a81d..8cf076f14d 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -1051,6 +1051,29 @@ module.exports.DatabaseOptions = { action: parsers.booleanParser, default: false, }, + maxPoolSize: { + env: 'PARSE_SERVER_DATABASE_MAX_POOL_SIZE', + help: + 'The MongoDB driver option to set the maximum number of opened, cached, ready-to-use database connections maintained by the driver.', + action: parsers.numberParser('maxPoolSize'), + }, + maxStalenessSeconds: { + env: 'PARSE_SERVER_DATABASE_MAX_STALENESS_SECONDS', + help: + 'The MongoDB driver option to set the maximum replication lag for reads from secondary nodes.', + action: parsers.numberParser('maxStalenessSeconds'), + }, + maxTimeMS: { + env: 'PARSE_SERVER_DATABASE_MAX_TIME_MS', + help: + 'The MongoDB driver option to set a cumulative time limit in milliseconds for processing operations on a cursor.', + action: parsers.numberParser('maxTimeMS'), + }, + retryWrites: { + env: 'PARSE_SERVER_DATABASE_RETRY_WRITES', + help: 'The MongoDB driver option to set whether to retry failed writes.', + action: parsers.booleanParser, + }, schemaCacheTtl: { env: 'PARSE_SERVER_DATABASE_SCHEMA_CACHE_TTL', help: diff --git a/src/Options/docs.js b/src/Options/docs.js index d9d4a4ad26..4c2883adaa 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -235,6 +235,10 @@ /** * @interface DatabaseOptions * @property {Boolean} enableSchemaHooks Enables database real-time hooks to update single schema cache. Set to `true` if using multiple Parse Servers instances connected to the same database. Failing to do so will cause a schema change to not propagate to all instances and re-syncing will only happen when the instances restart. To use this feature with MongoDB, a replica set cluster with [change stream](https://docs.mongodb.com/manual/changeStreams/#availability) support is required. + * @property {Number} maxPoolSize The MongoDB driver option to set the maximum number of opened, cached, ready-to-use database connections maintained by the driver. + * @property {Number} maxStalenessSeconds The MongoDB driver option to set the maximum replication lag for reads from secondary nodes. + * @property {Number} maxTimeMS The MongoDB driver option to set a cumulative time limit in milliseconds for processing operations on a cursor. + * @property {Boolean} retryWrites The MongoDB driver option to set whether to retry failed writes. * @property {Number} schemaCacheTtl The duration in seconds after which the schema cache expires and will be refetched from the database. Use this option if using multiple Parse Servers instances connected to the same database. A low duration will cause the schema cache to be updated too often, causing unnecessary database reads. A high duration will cause the schema to be updated too rarely, increasing the time required until schema changes propagate to all server instances. This feature can be used as an alternative or in conjunction with the option `enableSchemaHooks`. Default is infinite which means the schema cache never expires. */ diff --git a/src/Options/index.js b/src/Options/index.js index f7263e8786..40e15afb27 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -596,6 +596,14 @@ export interface DatabaseOptions { enableSchemaHooks: ?boolean; /* The duration in seconds after which the schema cache expires and will be refetched from the database. Use this option if using multiple Parse Servers instances connected to the same database. A low duration will cause the schema cache to be updated too often, causing unnecessary database reads. A high duration will cause the schema to be updated too rarely, increasing the time required until schema changes propagate to all server instances. This feature can be used as an alternative or in conjunction with the option `enableSchemaHooks`. Default is infinite which means the schema cache never expires. */ schemaCacheTtl: ?number; + /* The MongoDB driver option to set whether to retry failed writes. */ + retryWrites: ?boolean; + /* The MongoDB driver option to set a cumulative time limit in milliseconds for processing operations on a cursor. */ + maxTimeMS: ?number; + /* The MongoDB driver option to set the maximum replication lag for reads from secondary nodes.*/ + maxStalenessSeconds: ?number; + /* The MongoDB driver option to set the maximum number of opened, cached, ready-to-use database connections maintained by the driver. */ + maxPoolSize: ?number; } export interface AuthAdapter { From 3d6fc42e2d203f4641dfd03859d78b67a312fd37 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 17:01:30 +0200 Subject: [PATCH 13/15] remove unsued import --- src/ParseServer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ParseServer.js b/src/ParseServer.js index 9c0596af54..712b7ab3eb 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -46,7 +46,6 @@ import CheckRunner from './Security/CheckRunner'; import Deprecator from './Deprecator/Deprecator'; import { DefinedSchemas } from './SchemaMigrations/DefinedSchemas'; import OptionsDefinitions from './Options/Definitions'; -import Utils from './Utils'; // Mutate the Parse object to add the Cloud Code handlers addParseCloud(); From 3e1cd8d9493d2694d8f6e4dfc08f7896d177318b Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 17:01:51 +0200 Subject: [PATCH 14/15] unfit --- spec/ParseConfigKey.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/ParseConfigKey.spec.js b/spec/ParseConfigKey.spec.js index 606c24bbaf..5b49396d08 100644 --- a/spec/ParseConfigKey.spec.js +++ b/spec/ParseConfigKey.spec.js @@ -1,6 +1,6 @@ const Config = require('../lib/Config'); -fdescribe('Config Keys', () => { +describe('Config Keys', () => { const invalidKeyErrorMessage = 'Invalid key\\(s\\) found in Parse Server configuration'; let loggerErrorSpy; From 431923fffb9766bfd1b900c91a1156051234397c Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 17 Jul 2024 17:30:48 +0200 Subject: [PATCH 15/15] Update ParseConfigKey.spec.js --- spec/ParseConfigKey.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/ParseConfigKey.spec.js b/spec/ParseConfigKey.spec.js index 5b49396d08..a8f62681d9 100644 --- a/spec/ParseConfigKey.spec.js +++ b/spec/ParseConfigKey.spec.js @@ -71,7 +71,7 @@ describe('Config Keys', () => { expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(invalidKeyErrorMessage); }); - it('recognizes valid keys in databaseOptions', async () => { + it_only_db('mongo')('recognizes valid keys in databaseOptions (MongoDB)', async () => { await expectAsync(reconfigureServer({ databaseURI: 'mongodb://localhost:27017/parse', filesAdapter: null,