From a391ebd6be8934b4c707fbe2893d8457b1600c85 Mon Sep 17 00:00:00 2001 From: Alexandre Ruiz Date: Mon, 21 Nov 2022 13:17:31 +0100 Subject: [PATCH 01/21] feat: add option to change the log level of the logs emitted by triggers --- spec/CloudCodeLogger.spec.js | 27 ++++++++++++++++ src/Config.js | 24 ++++++++++++++ src/Controllers/LoggerController.js | 2 +- src/Options/Definitions.js | 15 +++++++++ src/triggers.js | 49 +++++++++++++++++++++-------- 5 files changed, 103 insertions(+), 14 deletions(-) diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index 162a3390b7..8cf01f6628 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -182,6 +182,33 @@ describe('Cloud Code Logger', () => { }); }); + it('should log cloud function triggers using the custom log level', done => { + reconfigureServer({ + // useful to flip to false for fine tuning :). + silent: true, + logLevel: 'silly', + logLevelTriggerAfterHook: 'debug', + logLevelTriggerSuccessBeforeHook: 'silly', + logLevelTriggerErrorBeforeHook: 'warn', + }).then(async () => { + spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); + + Parse.Cloud.beforeSave('TestClass', () => {}); + Parse.Cloud.afterSave('TestClass', () => {}); + const obj = new Parse.Object('TestClass'); + await obj.save(); + + let log = spy.calls.argsFor(1); + expect(log[0]).toEqual('silly'); + expect(log[1]).toMatch(/beforeSave triggered for TestClass for user .*/); + + log = spy.calls.argsFor(2); + expect(log[0]).toEqual('debug'); + expect(log[1]).toMatch(/afterSave triggered for TestClass for user .*/); + done(); + }); + }); + it('should log cloud function failure', done => { Parse.Cloud.define('aFunction', () => { throw 'it failed!'; diff --git a/src/Config.js b/src/Config.js index d2cd3b94f8..63d63dddd9 100644 --- a/src/Config.js +++ b/src/Config.js @@ -15,6 +15,7 @@ import { ParseServerOptions, } from './Options/Definitions'; import { isBoolean, isString } from 'lodash'; +import { logLevels } from './Controllers/LoggerController'; function removeTrailingSlash(str) { if (!str) { @@ -82,6 +83,9 @@ export class Config { schema, requestKeywordDenylist, allowExpiredAuthDataToken, + logLevelTriggerAfterHook, + logLevelTriggerSuccessBeforeHook, + logLevelTriggerErrorBeforeHook, }) { if (masterKey === readOnlyMasterKey) { throw new Error('masterKey and readOnlyMasterKey should be different'); @@ -123,6 +127,9 @@ export class Config { this.validateEnforcePrivateUsers(enforcePrivateUsers); this.validateAllowExpiredAuthDataToken(allowExpiredAuthDataToken); this.validateRequestKeywordDenylist(requestKeywordDenylist); + this.validateLogLevelTriggerAfterHook(logLevelTriggerAfterHook); + this.validateLogLevelTriggerSuccessBeforeHook(logLevelTriggerSuccessBeforeHook); + this.validateLogLevelTriggerErrorBeforeHook(logLevelTriggerErrorBeforeHook); } static validateRequestKeywordDenylist(requestKeywordDenylist) { @@ -501,6 +508,23 @@ export class Config { } } + static validateLogLevelTriggerAfterHook(logLevelTriggerAfterHook) { + const values = ['none', ...logLevels]; + if (values.indexOf(logLevelTriggerAfterHook) === -1) { + throw 'LogLevelTriggerAfterHook must be one of theses ' + values; + } + } + + static validateLogLevelTriggerSuccessBeforeHook(logLevelTriggerSuccessBeforeHook) { + // Same as validateLogLevelTriggerAfterHook for now + return this.validateLogLevelTriggerAfterHook(logLevelTriggerSuccessBeforeHook); + } + + static validateLogLevelTriggerErrorBeforeHook(logLevelTriggerErrorBeforeHook) { + // Same as validateLogLevelTriggerAfterHook for now + return this.validateLogLevelTriggerAfterHook(logLevelTriggerErrorBeforeHook); + } + generateEmailVerifyTokenExpiresAt() { if (!this.verifyUserEmails || !this.emailVerifyTokenValidityDuration) { return undefined; diff --git a/src/Controllers/LoggerController.js b/src/Controllers/LoggerController.js index 8ee492cf4b..8466e5459a 100644 --- a/src/Controllers/LoggerController.js +++ b/src/Controllers/LoggerController.js @@ -16,7 +16,7 @@ export const LogOrder = { ASCENDING: 'asc', }; -const logLevels = ['error', 'warn', 'info', 'debug', 'verbose', 'silly']; +export const logLevels = ['error', 'warn', 'info', 'debug', 'verbose', 'silly']; export class LoggerController extends AdaptableController { constructor(adapter, appId, options = { logLevel: 'info' }) { diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index 4a1dee55ad..9c72739f59 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -290,6 +290,21 @@ module.exports.ParseServerOptions = { env: 'PARSE_SERVER_LOG_LEVEL', help: 'Sets the level for logs', }, + logLevelTriggerAfterHook: { + env: 'PARSE_SERVER_LOG_LEVEL_TRIGGER_AFTER_HOOK', + help: 'Set the level used when logging when a after hook is triggered', + default: 'info', + }, + logLevelTriggerSuccessBeforeHook: { + env: 'PARSE_SERVER_LOG_LEVEL_TRIGGER_SUCCESS_BEFORE_HOOK', + help: 'Set the level used when logging when a before hook was successfully triggered', + default: 'info', + }, + logLevelTriggerErrorBeforeHook: { + env: 'PARSE_SERVER_LOG_LEVEL_TRIGGER_ERROR_BEFORE_HOOK', + help: 'Set the level used when logging when a before hook was triggered with error', + default: 'error', + }, logsFolder: { env: 'PARSE_SERVER_LOGS_FOLDER', help: "Folder for the logs (defaults to './logs'); set to null to disable file based logging", diff --git a/src/triggers.js b/src/triggers.js index 4ba21b32ea..9ab77104ca 100644 --- a/src/triggers.js +++ b/src/triggers.js @@ -373,9 +373,9 @@ function userIdForLog(auth) { return auth && auth.user ? auth.user.id : undefined; } -function logTriggerAfterHook(triggerType, className, input, auth) { +function logTriggerAfterHook(triggerType, className, input, auth, logLevel) { const cleanInput = logger.truncateLogMessage(JSON.stringify(input)); - logger.info( + logger[logLevel]( `${triggerType} triggered for ${className} for user ${userIdForLog( auth )}:\n Input: ${cleanInput}`, @@ -387,10 +387,10 @@ function logTriggerAfterHook(triggerType, className, input, auth) { ); } -function logTriggerSuccessBeforeHook(triggerType, className, input, result, auth) { +function logTriggerSuccessBeforeHook(triggerType, className, input, result, auth, logLevel) { const cleanInput = logger.truncateLogMessage(JSON.stringify(input)); const cleanResult = logger.truncateLogMessage(JSON.stringify(result)); - logger.info( + logger[logLevel]( `${triggerType} triggered for ${className} for user ${userIdForLog( auth )}:\n Input: ${cleanInput}\n Result: ${cleanResult}`, @@ -402,9 +402,9 @@ function logTriggerSuccessBeforeHook(triggerType, className, input, result, auth ); } -function logTriggerErrorBeforeHook(triggerType, className, input, auth, error) { +function logTriggerErrorBeforeHook(triggerType, className, input, auth, error, logLevel) { const cleanInput = logger.truncateLogMessage(JSON.stringify(input)); - logger.error( + logger[logLevel]( `${triggerType} failed for ${className} for user ${userIdForLog( auth )}:\n Input: ${cleanInput}\n Error: ${JSON.stringify(error)}`, @@ -444,7 +444,14 @@ export function maybeRunAfterFindTrigger( reject(error); } ); - logTriggerSuccessBeforeHook(triggerType, className, 'AfterFind', JSON.stringify(objects), auth); + logTriggerSuccessBeforeHook( + triggerType, + className, + 'AfterFind', + JSON.stringify(objects), + auth, + config.logLevelTriggerSuccessBeforeHook + ); request.objects = objects.map(object => { //setting the class name to transform into parse object object.className = className; @@ -468,7 +475,13 @@ export function maybeRunAfterFindTrigger( }) .then(success, error); }).then(results => { - logTriggerAfterHook(triggerType, className, JSON.stringify(results), auth); + logTriggerAfterHook( + triggerType, + className, + JSON.stringify(results), + auth, + config.logLevelTriggerAfterHook + ); return results; }); } @@ -842,7 +855,8 @@ export function maybeRunTrigger( parseObject.className, parseObject.toJSON(), object, - auth + auth, + config.logLevelTriggerSuccessBeforeHook ); if ( triggerType === Types.beforeSave || @@ -860,7 +874,8 @@ export function maybeRunTrigger( parseObject.className, parseObject.toJSON(), auth, - error + error, + config.logLevelTriggerErrorBeforeHook ); reject(error); } @@ -885,7 +900,13 @@ export function maybeRunTrigger( triggerType === Types.afterDelete || triggerType === Types.afterLogin ) { - logTriggerAfterHook(triggerType, parseObject.className, parseObject.toJSON(), auth); + logTriggerAfterHook( + triggerType, + parseObject.className, + parseObject.toJSON(), + auth, + config.logLevelTriggerAfterHook + ); } // beforeSave is expected to return null (nothing) if (triggerType === Types.beforeSave) { @@ -965,7 +986,8 @@ export async function maybeRunFileTrigger(triggerType, fileObject, config, auth) 'Parse.File', { ...fileObject.file.toJSON(), fileSize: fileObject.fileSize }, result, - auth + auth, + config.logLevelTriggerSuccessBeforeHook ); return result || fileObject; } catch (error) { @@ -974,7 +996,8 @@ export async function maybeRunFileTrigger(triggerType, fileObject, config, auth) 'Parse.File', { ...fileObject.file.toJSON(), fileSize: fileObject.fileSize }, auth, - error + error, + config.logLevelTriggerErrorBeforeHook ); throw error; } From b0102fd2f174ef20d6de039ff302cadb09e1c4f0 Mon Sep 17 00:00:00 2001 From: alljinx Date: Tue, 22 Nov 2022 14:24:26 +0100 Subject: [PATCH 02/21] feat: add option to change the log level of the logs emitted by triggers --- spec/CloudCodeLogger.spec.js | 53 +++++++++++++++++------ src/Config.js | 28 ++++-------- src/Options/Definitions.js | 23 ++++------ src/Options/docs.js | 8 ++++ src/Options/index.js | 18 ++++++++ src/triggers.js | 82 +++++++++++++++++++----------------- 6 files changed, 127 insertions(+), 85 deletions(-) diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index 8cf01f6628..057e798c65 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -182,19 +182,22 @@ describe('Cloud Code Logger', () => { }); }); - it('should log cloud function triggers using the custom log level', done => { - reconfigureServer({ - // useful to flip to false for fine tuning :). - silent: true, - logLevel: 'silly', - logLevelTriggerAfterHook: 'debug', - logLevelTriggerSuccessBeforeHook: 'silly', - logLevelTriggerErrorBeforeHook: 'warn', - }).then(async () => { + it('should log cloud function triggers using the custom log level', async done => { + Parse.Cloud.beforeSave('TestClass', () => {}); + Parse.Cloud.afterSave('TestClass', () => {}); + + { + await reconfigureServer({ + silent: true, + logLevel: 'silly', + logLevelUses: { + triggerAfterHook: 'debug', + triggerSuccessBeforeHook: 'silly', + }, + }); + spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); - Parse.Cloud.beforeSave('TestClass', () => {}); - Parse.Cloud.afterSave('TestClass', () => {}); const obj = new Parse.Object('TestClass'); await obj.save(); @@ -205,8 +208,32 @@ describe('Cloud Code Logger', () => { log = spy.calls.argsFor(2); expect(log[0]).toEqual('debug'); expect(log[1]).toMatch(/afterSave triggered for TestClass for user .*/); - done(); - }); + } + + { + await reconfigureServer({ + silent: true, + logLevel: 'info', + logLevelUses: { + triggerSuccessBeforeHook: 'none', + triggerErrorBeforeHook: 'warn', + }, + }); + + spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); + + const obj = new Parse.Object('TestClass'); + await obj.save(); + + let log = spy.calls.argsFor(1); + expect(log[0]).toEqual('warn'); + expect(log[1]).toMatch('afterSave caught an error'); + + log = spy.calls.argsFor(2); + expect(log).toEqual([]); + } + + done(); }); it('should log cloud function failure', done => { diff --git a/src/Config.js b/src/Config.js index 63d63dddd9..a3ce70dfda 100644 --- a/src/Config.js +++ b/src/Config.js @@ -83,9 +83,7 @@ export class Config { schema, requestKeywordDenylist, allowExpiredAuthDataToken, - logLevelTriggerAfterHook, - logLevelTriggerSuccessBeforeHook, - logLevelTriggerErrorBeforeHook, + logLevelUses, }) { if (masterKey === readOnlyMasterKey) { throw new Error('masterKey and readOnlyMasterKey should be different'); @@ -127,9 +125,7 @@ export class Config { this.validateEnforcePrivateUsers(enforcePrivateUsers); this.validateAllowExpiredAuthDataToken(allowExpiredAuthDataToken); this.validateRequestKeywordDenylist(requestKeywordDenylist); - this.validateLogLevelTriggerAfterHook(logLevelTriggerAfterHook); - this.validateLogLevelTriggerSuccessBeforeHook(logLevelTriggerSuccessBeforeHook); - this.validateLogLevelTriggerErrorBeforeHook(logLevelTriggerErrorBeforeHook); + this.validateLogLevelUses(logLevelUses); } static validateRequestKeywordDenylist(requestKeywordDenylist) { @@ -508,23 +504,15 @@ export class Config { } } - static validateLogLevelTriggerAfterHook(logLevelTriggerAfterHook) { - const values = ['none', ...logLevels]; - if (values.indexOf(logLevelTriggerAfterHook) === -1) { - throw 'LogLevelTriggerAfterHook must be one of theses ' + values; + static validateLogLevelUses(logLevelUses) { + const possibleValues = ['none', ...logLevels]; + for (const entry of Object.entries(logLevelUses)) { + if (possibleValues.indexOf(entry[1]) === -1) { + throw entry[0] + ' must be one of theses ' + possibleValues; + } } } - static validateLogLevelTriggerSuccessBeforeHook(logLevelTriggerSuccessBeforeHook) { - // Same as validateLogLevelTriggerAfterHook for now - return this.validateLogLevelTriggerAfterHook(logLevelTriggerSuccessBeforeHook); - } - - static validateLogLevelTriggerErrorBeforeHook(logLevelTriggerErrorBeforeHook) { - // Same as validateLogLevelTriggerAfterHook for now - return this.validateLogLevelTriggerAfterHook(logLevelTriggerErrorBeforeHook); - } - generateEmailVerifyTokenExpiresAt() { if (!this.verifyUserEmails || !this.emailVerifyTokenValidityDuration) { return undefined; diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index 9c72739f59..f845336167 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -290,20 +290,15 @@ module.exports.ParseServerOptions = { env: 'PARSE_SERVER_LOG_LEVEL', help: 'Sets the level for logs', }, - logLevelTriggerAfterHook: { - env: 'PARSE_SERVER_LOG_LEVEL_TRIGGER_AFTER_HOOK', - help: 'Set the level used when logging when a after hook is triggered', - default: 'info', - }, - logLevelTriggerSuccessBeforeHook: { - env: 'PARSE_SERVER_LOG_LEVEL_TRIGGER_SUCCESS_BEFORE_HOOK', - help: 'Set the level used when logging when a before hook was successfully triggered', - default: 'info', - }, - logLevelTriggerErrorBeforeHook: { - env: 'PARSE_SERVER_LOG_LEVEL_TRIGGER_ERROR_BEFORE_HOOK', - help: 'Set the level used when logging when a before hook was triggered with error', - default: 'error', + logLevelUses: { + env: 'PARSE_SERVER_LOG_LEVEL_USES', + help: 'Set the level used internally by Parse Server features', + action: parsers.objectParser, + default: { + triggerAfterHook: 'info', + triggerSuccessBeforeHook: 'info', + triggerErrorBeforeHook: 'error', + }, }, logsFolder: { env: 'PARSE_SERVER_LOGS_FOLDER', diff --git a/src/Options/docs.js b/src/Options/docs.js index 6c22e91e2e..b480030160 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -56,6 +56,7 @@ * @property {LiveQueryServerOptions} liveQueryServerOptions Live query server configuration options (will start the liveQuery server) * @property {Adapter} loggerAdapter Adapter module for the logging sub-system * @property {String} logLevel Sets the level for logs + * @property {LogLevelUses} logLevelUses Set the level used internally by Parse Server features * @property {String} logsFolder Folder for the logs (defaults to './logs'); set to null to disable file based logging * @property {String} masterKey Your Parse Master Key * @property {String[]} masterKeyIps (Optional) Restricts the use of master key permissions to a list of IP addresses.

This option accepts a list of single IP addresses, for example:
`['10.0.0.1', '10.0.0.2']`

You can also use CIDR notation to specify an IP address range, for example:
`['10.0.1.0/24']`

Special cases:
- Setting an empty array `[]` means that `masterKey`` cannot be used even in Parse Server Cloud Code.
- Setting `['0.0.0.0/0']` means disabling the filter and the master key can be used from any IP address.

To connect Parse Dashboard from a different server requires to add the IP address of the server that hosts Parse Dashboard because Parse Dashboard uses the master key.

Defaults to `['127.0.0.1']` which means that only `localhost`, the server itself, is allowed to use the master key. @@ -215,3 +216,10 @@ * @interface AuthAdapter * @property {Boolean} enabled Is `true` if the auth adapter is enabled, `false` otherwise. */ + +/** + * @interface LogLevelUses + * @property {Boolean} triggerAfterHook Log level used by the after hook trigger, default is 'info'. + * @property {Boolean} triggerSuccessBeforeHook Log level used by the success before hook trigger, default is 'info. + * @property {Boolean} triggerErrorBeforeHook Log level used by the error before hook trigger, default is 'error. + */ diff --git a/src/Options/index.js b/src/Options/index.js index 61ca21e4b5..17c798b1af 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -81,6 +81,9 @@ export interface ParseServerOptions { verbose: ?boolean; /* Sets the level for logs */ logLevel: ?string; + /* Set the level used internally by Parse Server features + :ENV: PARSE_SERVER_LOG_LEVEL_USES */ + logLevelUses: ?LogLevelUses; /* Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null) */ maxLogFiles: ?NumberOrString; /* Disables console output @@ -520,3 +523,18 @@ export interface AuthAdapter { */ enabled: ?boolean; } + +export interface LogLevelUses { + /* Log level used by the after hook trigger, default is 'info'. + :DEFAULT: 'info' + */ + triggerAfterHook: ?string; + /* Log level used by the success before hook trigger, default is 'info. + :DEFAULT: 'info' + */ + triggerSuccessBeforeHook: ?string; + /* Log level used by the error before hook trigger, default is 'error. + :DEFAULT: 'error' + */ + triggerErrorBeforeHook: ?string; +} diff --git a/src/triggers.js b/src/triggers.js index 9ab77104ca..cf2fb30be8 100644 --- a/src/triggers.js +++ b/src/triggers.js @@ -375,46 +375,52 @@ function userIdForLog(auth) { function logTriggerAfterHook(triggerType, className, input, auth, logLevel) { const cleanInput = logger.truncateLogMessage(JSON.stringify(input)); - logger[logLevel]( - `${triggerType} triggered for ${className} for user ${userIdForLog( - auth - )}:\n Input: ${cleanInput}`, - { - className, - triggerType, - user: userIdForLog(auth), - } - ); + if (logLevel !== 'none') { + logger[logLevel]( + `${triggerType} triggered for ${className} for user ${userIdForLog( + auth + )}:\n Input: ${cleanInput}`, + { + className, + triggerType, + user: userIdForLog(auth), + } + ); + } } function logTriggerSuccessBeforeHook(triggerType, className, input, result, auth, logLevel) { const cleanInput = logger.truncateLogMessage(JSON.stringify(input)); const cleanResult = logger.truncateLogMessage(JSON.stringify(result)); - logger[logLevel]( - `${triggerType} triggered for ${className} for user ${userIdForLog( - auth - )}:\n Input: ${cleanInput}\n Result: ${cleanResult}`, - { - className, - triggerType, - user: userIdForLog(auth), - } - ); + if (logLevel !== 'none') { + logger[logLevel]( + `${triggerType} triggered for ${className} for user ${userIdForLog( + auth + )}:\n Input: ${cleanInput}\n Result: ${cleanResult}`, + { + className, + triggerType, + user: userIdForLog(auth), + } + ); + } } function logTriggerErrorBeforeHook(triggerType, className, input, auth, error, logLevel) { const cleanInput = logger.truncateLogMessage(JSON.stringify(input)); - logger[logLevel]( - `${triggerType} failed for ${className} for user ${userIdForLog( - auth - )}:\n Input: ${cleanInput}\n Error: ${JSON.stringify(error)}`, - { - className, - triggerType, - error, - user: userIdForLog(auth), - } - ); + if (logLevel !== 'none') { + logger[logLevel]( + `${triggerType} failed for ${className} for user ${userIdForLog( + auth + )}:\n Input: ${cleanInput}\n Error: ${JSON.stringify(error)}`, + { + className, + triggerType, + error, + user: userIdForLog(auth), + } + ); + } } export function maybeRunAfterFindTrigger( @@ -450,7 +456,7 @@ export function maybeRunAfterFindTrigger( 'AfterFind', JSON.stringify(objects), auth, - config.logLevelTriggerSuccessBeforeHook + config.logLevelUses.triggerSuccessBeforeHook ); request.objects = objects.map(object => { //setting the class name to transform into parse object @@ -480,7 +486,7 @@ export function maybeRunAfterFindTrigger( className, JSON.stringify(results), auth, - config.logLevelTriggerAfterHook + config.logLevelUses.triggerAfterHook ); return results; }); @@ -856,7 +862,7 @@ export function maybeRunTrigger( parseObject.toJSON(), object, auth, - config.logLevelTriggerSuccessBeforeHook + config.logLevelUses.triggerSuccessBeforeHook ); if ( triggerType === Types.beforeSave || @@ -875,7 +881,7 @@ export function maybeRunTrigger( parseObject.toJSON(), auth, error, - config.logLevelTriggerErrorBeforeHook + config.logLevelUses.triggerErrorBeforeHook ); reject(error); } @@ -905,7 +911,7 @@ export function maybeRunTrigger( parseObject.className, parseObject.toJSON(), auth, - config.logLevelTriggerAfterHook + config.logLevelUses.triggerAfterHook ); } // beforeSave is expected to return null (nothing) @@ -987,7 +993,7 @@ export async function maybeRunFileTrigger(triggerType, fileObject, config, auth) { ...fileObject.file.toJSON(), fileSize: fileObject.fileSize }, result, auth, - config.logLevelTriggerSuccessBeforeHook + config.logLevelUses.triggerSuccessBeforeHook ); return result || fileObject; } catch (error) { @@ -997,7 +1003,7 @@ export async function maybeRunFileTrigger(triggerType, fileObject, config, auth) { ...fileObject.file.toJSON(), fileSize: fileObject.fileSize }, auth, error, - config.logLevelTriggerErrorBeforeHook + config.logLevelUses.triggerErrorBeforeHook ); throw error; } From c792e51125cb240ee687fbb89411896ee811d4cb Mon Sep 17 00:00:00 2001 From: alljinx <42472198+alljinx@users.noreply.github.com> Date: Wed, 23 Nov 2022 07:41:05 +0100 Subject: [PATCH 03/21] Update src/Options/index.js Co-authored-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- src/Options/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Options/index.js b/src/Options/index.js index 17c798b1af..b9710dca4c 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -81,9 +81,8 @@ export interface ParseServerOptions { verbose: ?boolean; /* Sets the level for logs */ logLevel: ?string; - /* Set the level used internally by Parse Server features - :ENV: PARSE_SERVER_LOG_LEVEL_USES */ - logLevelUses: ?LogLevelUses; + /* (Optional) Overrides the log levels used internally by Parse Server to log events. */ + logLevels: ?LogLevels; /* Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null) */ maxLogFiles: ?NumberOrString; /* Disables console output From 6a4b83b8a4aee53b09be733974547f34763cf99b Mon Sep 17 00:00:00 2001 From: alljinx Date: Wed, 23 Nov 2022 14:14:46 +0100 Subject: [PATCH 04/21] Multiples fixes suggested by mtrezza, including renaming logLevels options and others... --- resources/buildConfigDefinitions.js | 237 +++++++++++++++------------- spec/CloudCodeLogger.spec.js | 18 +-- src/Config.js | 33 ++-- src/Options/Definitions.js | 29 +++- src/Options/docs.js | 10 +- src/Options/index.js | 23 +-- src/triggers.js | 16 +- 7 files changed, 203 insertions(+), 163 deletions(-) diff --git a/resources/buildConfigDefinitions.js b/resources/buildConfigDefinitions.js index b41c53f07c..0b736561cc 100644 --- a/resources/buildConfigDefinitions.js +++ b/resources/buildConfigDefinitions.js @@ -24,31 +24,32 @@ const nestedOptionTypes = [ 'PasswordPolicyOptions', 'SecurityOptions', 'SchemaOptions', + 'LogLevels', ]; /** The prefix of environment variables for nested options. */ const nestedOptionEnvPrefix = { - 'AccountLockoutOptions': 'PARSE_SERVER_ACCOUNT_LOCKOUT_', - 'CustomPagesOptions': 'PARSE_SERVER_CUSTOM_PAGES_', - 'DatabaseOptions': 'PARSE_SERVER_DATABASE_', - 'FileUploadOptions': 'PARSE_SERVER_FILE_UPLOAD_', - 'IdempotencyOptions': 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_', - 'LiveQueryOptions': 'PARSE_SERVER_LIVEQUERY_', - 'LiveQueryServerOptions': 'PARSE_LIVE_QUERY_SERVER_', - 'PagesCustomUrlsOptions': 'PARSE_SERVER_PAGES_CUSTOM_URL_', - 'PagesOptions': 'PARSE_SERVER_PAGES_', - 'PagesRoute': 'PARSE_SERVER_PAGES_ROUTE_', - 'ParseServerOptions': 'PARSE_SERVER_', - 'PasswordPolicyOptions': 'PARSE_SERVER_PASSWORD_POLICY_', - 'SecurityOptions': 'PARSE_SERVER_SECURITY_', - 'SchemaOptions': 'PARSE_SERVER_SCHEMA_', + AccountLockoutOptions: 'PARSE_SERVER_ACCOUNT_LOCKOUT_', + CustomPagesOptions: 'PARSE_SERVER_CUSTOM_PAGES_', + DatabaseOptions: 'PARSE_SERVER_DATABASE_', + FileUploadOptions: 'PARSE_SERVER_FILE_UPLOAD_', + IdempotencyOptions: 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_', + LiveQueryOptions: 'PARSE_SERVER_LIVEQUERY_', + LiveQueryServerOptions: 'PARSE_LIVE_QUERY_SERVER_', + PagesCustomUrlsOptions: 'PARSE_SERVER_PAGES_CUSTOM_URL_', + PagesOptions: 'PARSE_SERVER_PAGES_', + PagesRoute: 'PARSE_SERVER_PAGES_ROUTE_', + ParseServerOptions: 'PARSE_SERVER_', + PasswordPolicyOptions: 'PARSE_SERVER_PASSWORD_POLICY_', + SecurityOptions: 'PARSE_SERVER_SECURITY_', + SchemaOptions: 'PARSE_SERVER_SCHEMA_', }; function last(array) { return array[array.length - 1]; } -const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; function toENV(key) { let str = ''; let previousIsUpper = false; @@ -68,13 +69,15 @@ function toENV(key) { } function getCommentValue(comment) { - if (!comment) { return } + if (!comment) { + return; + } return comment.value.trim(); } function getENVPrefix(iface) { if (nestedOptionEnvPrefix[iface.id.name]) { - return nestedOptionEnvPrefix[iface.id.name] + return nestedOptionEnvPrefix[iface.id.name]; } } @@ -86,11 +89,11 @@ function processProperty(property, iface) { if (!firstComment) { return; } - const lines = firstComment.split('\n').map((line) => line.trim()); + const lines = firstComment.split('\n').map(line => line.trim()); let help = ''; let envLine; let defaultLine; - lines.forEach((line) => { + lines.forEach(line => { if (line.indexOf(':ENV:') === 0) { envLine = line; } else if (line.indexOf(':DEFAULT:') === 0) { @@ -103,7 +106,7 @@ function processProperty(property, iface) { if (envLine) { env = envLine.split(' ')[1]; } else { - env = (prefix + toENV(name)); + env = prefix + toENV(name); } let defaultValue; if (defaultLine) { @@ -123,21 +126,20 @@ function processProperty(property, iface) { defaultValue, types: property.value.types, typeAnnotation: property.value.typeAnnotation, - required: isRequired + required: isRequired, }; } - function doInterface(iface) { return iface.body.properties .sort((a, b) => a.key.name.localeCompare(b.key.name)) - .map((prop) => processProperty(prop, iface)) - .filter((e) => e !== undefined); + .map(prop => processProperty(prop, iface)) + .filter(e => e !== undefined); } function mapperFor(elt, t) { const p = t.identifier('parsers'); - const wrap = (identifier) => t.memberExpression(p, identifier); + const wrap = identifier => t.memberExpression(p, identifier); if (t.isNumberTypeAnnotation(elt)) { return t.callExpression(wrap(t.identifier('numberParser')), [t.stringLiteral(elt.name)]); @@ -171,27 +173,29 @@ function parseDefaultValue(elt, value, t) { literalValue = t.numericLiteral(parsers.numberOrBoolParser('')(value)); } else if (t.isArrayTypeAnnotation(elt)) { const array = parsers.objectParser(value); - literalValue = t.arrayExpression(array.map((value) => { - if (typeof value == 'string') { - return t.stringLiteral(value); - } else if (typeof value == 'number') { - return t.numericLiteral(value); - } else if (typeof value == 'object') { - const object = parsers.objectParser(value); - const props = Object.entries(object).map(([k, v]) => { - if (typeof v == 'string') { - return t.objectProperty(t.identifier(k), t.stringLiteral(v)); - } else if (typeof v == 'number') { - return t.objectProperty(t.identifier(k), t.numericLiteral(v)); - } else if (typeof v == 'boolean') { - return t.objectProperty(t.identifier(k), t.booleanLiteral(v)); - } - }); - return t.objectExpression(props); - } else { - throw new Error('Unable to parse array'); - } - })); + literalValue = t.arrayExpression( + array.map(value => { + if (typeof value == 'string') { + return t.stringLiteral(value); + } else if (typeof value == 'number') { + return t.numericLiteral(value); + } else if (typeof value == 'object') { + const object = parsers.objectParser(value); + const props = Object.entries(object).map(([k, v]) => { + if (typeof v == 'string') { + return t.objectProperty(t.identifier(k), t.stringLiteral(v)); + } else if (typeof v == 'number') { + return t.objectProperty(t.identifier(k), t.numericLiteral(v)); + } else if (typeof v == 'boolean') { + return t.objectProperty(t.identifier(k), t.booleanLiteral(v)); + } + }); + return t.objectExpression(props); + } else { + throw new Error('Unable to parse array'); + } + }) + ); } else if (t.isAnyTypeAnnotation(elt)) { literalValue = t.arrayExpression([]); } else if (t.isBooleanTypeAnnotation(elt)) { @@ -204,15 +208,16 @@ function parseDefaultValue(elt, value, t) { if (nestedOptionTypes.includes(type)) { const object = parsers.objectParser(value); - const props = Object.keys(object).map((key) => { + const props = Object.keys(object).map(key => { return t.objectProperty(key, object[value]); }); literalValue = t.objectExpression(props); } if (type == 'ProtectedFields') { const prop = t.objectProperty( - t.stringLiteral('_User'), t.objectPattern([ - t.objectProperty(t.stringLiteral('*'), t.arrayExpression([t.stringLiteral('email')])) + t.stringLiteral('_User'), + t.objectPattern([ + t.objectProperty(t.stringLiteral('*'), t.arrayExpression([t.stringLiteral('email')])), ]) ); literalValue = t.objectExpression([prop]); @@ -223,62 +228,69 @@ function parseDefaultValue(elt, value, t) { function inject(t, list) { let comments = ''; - const results = list.map((elt) => { - if (!elt.name) { - return; - } - const props = ['env', 'help'].map((key) => { - if (elt[key]) { - return t.objectProperty(t.stringLiteral(key), t.stringLiteral(elt[key])); + const results = list + .map(elt => { + if (!elt.name) { + return; } - }).filter((e) => e !== undefined); - if (elt.required) { - props.push(t.objectProperty(t.stringLiteral('required'), t.booleanLiteral(true))) - } - const action = mapperFor(elt, t); - if (action) { - props.push(t.objectProperty(t.stringLiteral('action'), action)) - } - if (elt.defaultValue) { - const parsedValue = parseDefaultValue(elt, elt.defaultValue, t); - if (parsedValue) { - props.push(t.objectProperty(t.stringLiteral('default'), parsedValue)); - } else { - throw new Error(`Unable to parse value for ${elt.name} `); + const props = ['env', 'help'] + .map(key => { + if (elt[key]) { + return t.objectProperty(t.stringLiteral(key), t.stringLiteral(elt[key])); + } + }) + .filter(e => e !== undefined); + if (elt.required) { + props.push(t.objectProperty(t.stringLiteral('required'), t.booleanLiteral(true))); } - } - let type = elt.type.replace('TypeAnnotation', ''); - if (type === 'Generic') { - type = elt.typeAnnotation.id.name; - } - if (type === 'Array') { - type = elt.typeAnnotation.elementType.id - ? `${elt.typeAnnotation.elementType.id.name}[]` - : `${elt.typeAnnotation.elementType.type.replace('TypeAnnotation', '')}[]`; - } - if (type === 'NumberOrBoolean') { - type = 'Number|Boolean'; - } - if (type === 'NumberOrString') { - type = 'Number|String'; - } - if (type === 'Adapter') { - const adapterType = elt.typeAnnotation.typeParameters.params[0].id.name; - type = `Adapter<${adapterType}>`; - } - comments += ` * @property {${type}} ${elt.name} ${elt.help}\n`; - const obj = t.objectExpression(props); - return t.objectProperty(t.stringLiteral(elt.name), obj); - }).filter((elt) => { - return elt != undefined; - }); + const action = mapperFor(elt, t); + if (action) { + props.push(t.objectProperty(t.stringLiteral('action'), action)); + } + if (elt.defaultValue) { + const parsedValue = parseDefaultValue(elt, elt.defaultValue, t); + if (parsedValue) { + props.push(t.objectProperty(t.stringLiteral('default'), parsedValue)); + } else { + throw new Error(`Unable to parse value for ${elt.name} `); + } + } + let type = elt.type.replace('TypeAnnotation', ''); + if (type === 'Generic') { + type = elt.typeAnnotation.id.name; + } + if (type === 'Array') { + type = elt.typeAnnotation.elementType.id + ? `${elt.typeAnnotation.elementType.id.name}[]` + : `${elt.typeAnnotation.elementType.type.replace('TypeAnnotation', '')}[]`; + } + if (type === 'NumberOrBoolean') { + type = 'Number|Boolean'; + } + if (type === 'NumberOrString') { + type = 'Number|String'; + } + if (type === 'Adapter') { + const adapterType = elt.typeAnnotation.typeParameters.params[0].id.name; + type = `Adapter<${adapterType}>`; + } + comments += ` * @property {${type}} ${elt.name} ${elt.help}\n`; + const obj = t.objectExpression(props); + return t.objectProperty(t.stringLiteral(elt.name), obj); + }) + .filter(elt => { + return elt != undefined; + }); return { results, comments }; } const makeRequire = function (variableName, module, t) { - const decl = t.variableDeclarator(t.identifier(variableName), t.callExpression(t.identifier('require'), [t.stringLiteral(module)])); - return t.variableDeclaration('var', [decl]) -} + const decl = t.variableDeclarator( + t.identifier(variableName), + t.callExpression(t.identifier('require'), [t.stringLiteral(module)]) + ); + return t.variableDeclaration('var', [decl]); +}; let docs = ``; const plugin = function (babel) { const t = babel.types; @@ -294,27 +306,34 @@ const plugin = function (babel) { }, ExportDeclaration: function (path) { // Export declaration on an interface - if (path.node && path.node.declaration && path.node.declaration.type == 'InterfaceDeclaration') { + if ( + path.node && + path.node.declaration && + path.node.declaration.type == 'InterfaceDeclaration' + ) { const { results, comments } = inject(t, doInterface(path.node.declaration)); const id = path.node.declaration.id.name; const exports = t.memberExpression(moduleExports, t.identifier(id)); docs += `/**\n * @interface ${id}\n${comments} */\n\n`; - path.replaceWith( - t.assignmentExpression('=', exports, t.objectExpression(results)) - ) + path.replaceWith(t.assignmentExpression('=', exports, t.objectExpression(results))); } - } - } - } + }, + }, + }; }; const auxiliaryCommentBefore = ` **** GENERATED CODE **** This code has been generated by resources/buildConfigDefinitions.js Do not edit manually, but update Options/index.js -` +`; -const babel = require("@babel/core"); -const res = babel.transformFileSync('./src/Options/index.js', { plugins: [plugin, '@babel/transform-flow-strip-types'], babelrc: false, auxiliaryCommentBefore, sourceMaps: false }); +const babel = require('@babel/core'); +const res = babel.transformFileSync('./src/Options/index.js', { + plugins: [plugin, '@babel/transform-flow-strip-types'], + babelrc: false, + auxiliaryCommentBefore, + sourceMaps: false, +}); require('fs').writeFileSync('./src/Options/Definitions.js', res.code + '\n'); require('fs').writeFileSync('./src/Options/docs.js', docs); diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index 057e798c65..8cb6a4c7f4 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -190,9 +190,9 @@ describe('Cloud Code Logger', () => { await reconfigureServer({ silent: true, logLevel: 'silly', - logLevelUses: { - triggerAfterHook: 'debug', - triggerSuccessBeforeHook: 'silly', + logLevels: { + triggerAfter: 'debug', + triggerBeforeSuccess: 'silly', }, }); @@ -214,9 +214,9 @@ describe('Cloud Code Logger', () => { await reconfigureServer({ silent: true, logLevel: 'info', - logLevelUses: { - triggerSuccessBeforeHook: 'none', - triggerErrorBeforeHook: 'warn', + logLevels: { + triggerAfter: 'none', + triggerBeforeSuccess: 'warn', }, }); @@ -225,11 +225,11 @@ describe('Cloud Code Logger', () => { const obj = new Parse.Object('TestClass'); await obj.save(); - let log = spy.calls.argsFor(1); + let log = spy.calls.argsFor(0); expect(log[0]).toEqual('warn'); - expect(log[1]).toMatch('afterSave caught an error'); + expect(log[1]).toMatch(/beforeSave triggered for TestClass for user .*/); - log = spy.calls.argsFor(2); + log = spy.calls.argsFor(1); expect(log).toEqual([]); } diff --git a/src/Config.js b/src/Config.js index a3ce70dfda..5848189eb9 100644 --- a/src/Config.js +++ b/src/Config.js @@ -2,20 +2,21 @@ // configured. // mount is the URL for the root of the API; includes http, domain, etc. +import { isBoolean, isString } from 'lodash'; +import net from 'net'; import AppCache from './cache'; import DatabaseController from './Controllers/DatabaseController'; -import net from 'net'; +import { logLevels as ctrlLogLevels } from './Controllers/LoggerController'; import { - IdempotencyOptions, - FileUploadOptions, AccountLockoutOptions, + FileUploadOptions, + IdempotencyOptions, + LogLevels, PagesOptions, - SecurityOptions, - SchemaOptions, ParseServerOptions, + SchemaOptions, + SecurityOptions, } from './Options/Definitions'; -import { isBoolean, isString } from 'lodash'; -import { logLevels } from './Controllers/LoggerController'; function removeTrailingSlash(str) { if (!str) { @@ -83,7 +84,7 @@ export class Config { schema, requestKeywordDenylist, allowExpiredAuthDataToken, - logLevelUses, + logLevels, }) { if (masterKey === readOnlyMasterKey) { throw new Error('masterKey and readOnlyMasterKey should be different'); @@ -125,7 +126,7 @@ export class Config { this.validateEnforcePrivateUsers(enforcePrivateUsers); this.validateAllowExpiredAuthDataToken(allowExpiredAuthDataToken); this.validateRequestKeywordDenylist(requestKeywordDenylist); - this.validateLogLevelUses(logLevelUses); + this.validateLogLevels(logLevels); } static validateRequestKeywordDenylist(requestKeywordDenylist) { @@ -504,11 +505,15 @@ export class Config { } } - static validateLogLevelUses(logLevelUses) { - const possibleValues = ['none', ...logLevels]; - for (const entry of Object.entries(logLevelUses)) { - if (possibleValues.indexOf(entry[1]) === -1) { - throw entry[0] + ' must be one of theses ' + possibleValues; + static validateLogLevels(logLevels) { + const possibleValues = ['none', ...ctrlLogLevels]; + for (const key of Object.keys(LogLevels)) { + if (logLevels[key]) { + if (possibleValues.indexOf(logLevels[key]) === -1) { + throw key + ' must be one of theses ' + possibleValues; + } + } else { + logLevels[key] = LogLevels[key].default; } } } diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index f845336167..fd4015b225 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -290,15 +290,11 @@ module.exports.ParseServerOptions = { env: 'PARSE_SERVER_LOG_LEVEL', help: 'Sets the level for logs', }, - logLevelUses: { - env: 'PARSE_SERVER_LOG_LEVEL_USES', - help: 'Set the level used internally by Parse Server features', + logLevels: { + env: 'PARSE_SERVER_LOG_LEVELS', + help: '(Optional) Overrides the log levels used internally by Parse Server to log events.', action: parsers.objectParser, - default: { - triggerAfterHook: 'info', - triggerSuccessBeforeHook: 'info', - triggerErrorBeforeHook: 'error', - }, + default: {}, }, logsFolder: { env: 'PARSE_SERVER_LOGS_FOLDER', @@ -908,3 +904,20 @@ module.exports.AuthAdapter = { default: true, }, }; +module.exports.LogLevels = { + triggerAfter: { + env: 'undefinedTRIGGER_AFTER', + help: "Log level used by the after hook trigger, default is 'info'.", + default: 'info', + }, + triggerBeforeError: { + env: 'undefinedTRIGGER_BEFORE_ERROR', + help: "Log level used by the error before hook trigger, default is 'error.", + default: 'error', + }, + triggerBeforeSuccess: { + env: 'undefinedTRIGGER_BEFORE_SUCCESS', + help: "Log level used by the success before hook trigger, default is 'info.", + default: 'info', + }, +}; diff --git a/src/Options/docs.js b/src/Options/docs.js index b480030160..4d2de3c1b1 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -56,7 +56,7 @@ * @property {LiveQueryServerOptions} liveQueryServerOptions Live query server configuration options (will start the liveQuery server) * @property {Adapter} loggerAdapter Adapter module for the logging sub-system * @property {String} logLevel Sets the level for logs - * @property {LogLevelUses} logLevelUses Set the level used internally by Parse Server features + * @property {LogLevels} logLevels (Optional) Overrides the log levels used internally by Parse Server to log events. * @property {String} logsFolder Folder for the logs (defaults to './logs'); set to null to disable file based logging * @property {String} masterKey Your Parse Master Key * @property {String[]} masterKeyIps (Optional) Restricts the use of master key permissions to a list of IP addresses.

This option accepts a list of single IP addresses, for example:
`['10.0.0.1', '10.0.0.2']`

You can also use CIDR notation to specify an IP address range, for example:
`['10.0.1.0/24']`

Special cases:
- Setting an empty array `[]` means that `masterKey`` cannot be used even in Parse Server Cloud Code.
- Setting `['0.0.0.0/0']` means disabling the filter and the master key can be used from any IP address.

To connect Parse Dashboard from a different server requires to add the IP address of the server that hosts Parse Dashboard because Parse Dashboard uses the master key.

Defaults to `['127.0.0.1']` which means that only `localhost`, the server itself, is allowed to use the master key. @@ -218,8 +218,8 @@ */ /** - * @interface LogLevelUses - * @property {Boolean} triggerAfterHook Log level used by the after hook trigger, default is 'info'. - * @property {Boolean} triggerSuccessBeforeHook Log level used by the success before hook trigger, default is 'info. - * @property {Boolean} triggerErrorBeforeHook Log level used by the error before hook trigger, default is 'error. + * @interface LogLevels + * @property {String} triggerAfter Log level used by the after hook trigger, default is 'info'. + * @property {String} triggerBeforeError Log level used by the error before hook trigger, default is 'error. + * @property {String} triggerBeforeSuccess Log level used by the success before hook trigger, default is 'info. */ diff --git a/src/Options/index.js b/src/Options/index.js index b9710dca4c..74ed271306 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -1,11 +1,11 @@ // @flow import { AnalyticsAdapter } from '../Adapters/Analytics/AnalyticsAdapter'; -import { FilesAdapter } from '../Adapters/Files/FilesAdapter'; -import { LoggerAdapter } from '../Adapters/Logger/LoggerAdapter'; -import { StorageAdapter } from '../Adapters/Storage/StorageAdapter'; import { CacheAdapter } from '../Adapters/Cache/CacheAdapter'; import { MailAdapter } from '../Adapters/Email/MailAdapter'; +import { FilesAdapter } from '../Adapters/Files/FilesAdapter'; +import { LoggerAdapter } from '../Adapters/Logger/LoggerAdapter'; import { PubSubAdapter } from '../Adapters/PubSub/PubSubAdapter'; +import { StorageAdapter } from '../Adapters/Storage/StorageAdapter'; import { WSSAdapter } from '../Adapters/WebSocketServer/WSSAdapter'; import { CheckGroup } from '../Security/CheckGroup'; @@ -81,7 +81,8 @@ export interface ParseServerOptions { verbose: ?boolean; /* Sets the level for logs */ logLevel: ?string; - /* (Optional) Overrides the log levels used internally by Parse Server to log events. */ + /* (Optional) Overrides the log levels used internally by Parse Server to log events. + :DEFAULT: {} */ logLevels: ?LogLevels; /* Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null) */ maxLogFiles: ?NumberOrString; @@ -523,17 +524,17 @@ export interface AuthAdapter { enabled: ?boolean; } -export interface LogLevelUses { +export interface LogLevels { /* Log level used by the after hook trigger, default is 'info'. - :DEFAULT: 'info' + :DEFAULT: info */ - triggerAfterHook: ?string; + triggerAfter: ?string; /* Log level used by the success before hook trigger, default is 'info. - :DEFAULT: 'info' + :DEFAULT: info */ - triggerSuccessBeforeHook: ?string; + triggerBeforeSuccess: ?string; /* Log level used by the error before hook trigger, default is 'error. - :DEFAULT: 'error' + :DEFAULT: error */ - triggerErrorBeforeHook: ?string; + triggerBeforeError: ?string; } diff --git a/src/triggers.js b/src/triggers.js index cf2fb30be8..70490c8de2 100644 --- a/src/triggers.js +++ b/src/triggers.js @@ -456,7 +456,7 @@ export function maybeRunAfterFindTrigger( 'AfterFind', JSON.stringify(objects), auth, - config.logLevelUses.triggerSuccessBeforeHook + config.logLevels.triggerBeforeSuccess ); request.objects = objects.map(object => { //setting the class name to transform into parse object @@ -486,7 +486,7 @@ export function maybeRunAfterFindTrigger( className, JSON.stringify(results), auth, - config.logLevelUses.triggerAfterHook + config.logLevels.triggerAfter ); return results; }); @@ -862,7 +862,9 @@ export function maybeRunTrigger( parseObject.toJSON(), object, auth, - config.logLevelUses.triggerSuccessBeforeHook + triggerType.startsWith('after') + ? config.logLevels.triggerAfter + : config.logLevels.triggerBeforeSuccess ); if ( triggerType === Types.beforeSave || @@ -881,7 +883,7 @@ export function maybeRunTrigger( parseObject.toJSON(), auth, error, - config.logLevelUses.triggerErrorBeforeHook + config.logLevels.triggerBeforeError ); reject(error); } @@ -911,7 +913,7 @@ export function maybeRunTrigger( parseObject.className, parseObject.toJSON(), auth, - config.logLevelUses.triggerAfterHook + config.logLevels.triggerAfter ); } // beforeSave is expected to return null (nothing) @@ -993,7 +995,7 @@ export async function maybeRunFileTrigger(triggerType, fileObject, config, auth) { ...fileObject.file.toJSON(), fileSize: fileObject.fileSize }, result, auth, - config.logLevelUses.triggerSuccessBeforeHook + config.logLevels.triggerBeforeSuccess ); return result || fileObject; } catch (error) { @@ -1003,7 +1005,7 @@ export async function maybeRunFileTrigger(triggerType, fileObject, config, auth) { ...fileObject.file.toJSON(), fileSize: fileObject.fileSize }, auth, error, - config.logLevelUses.triggerErrorBeforeHook + config.logLevels.triggerBeforeError ); throw error; } From 2c167d3de13967d34c6904a52a768497912ad9fd Mon Sep 17 00:00:00 2001 From: alljinx Date: Wed, 23 Nov 2022 14:34:45 +0100 Subject: [PATCH 05/21] Refactoring test. --- spec/CloudCodeLogger.spec.js | 56 ++++++++++++++---------------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index 8cb6a4c7f4..43f7136ae7 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -186,13 +186,13 @@ describe('Cloud Code Logger', () => { Parse.Cloud.beforeSave('TestClass', () => {}); Parse.Cloud.afterSave('TestClass', () => {}); - { + const execTest = async (logLevel, triggerAfter, triggerBeforeSuccess) => { await reconfigureServer({ silent: true, - logLevel: 'silly', + logLevel, logLevels: { - triggerAfter: 'debug', - triggerBeforeSuccess: 'silly', + triggerAfter, + triggerBeforeSuccess, }, }); @@ -201,37 +201,23 @@ describe('Cloud Code Logger', () => { const obj = new Parse.Object('TestClass'); await obj.save(); - let log = spy.calls.argsFor(1); - expect(log[0]).toEqual('silly'); - expect(log[1]).toMatch(/beforeSave triggered for TestClass for user .*/); - - log = spy.calls.argsFor(2); - expect(log[0]).toEqual('debug'); - expect(log[1]).toMatch(/afterSave triggered for TestClass for user .*/); - } - - { - await reconfigureServer({ - silent: true, - logLevel: 'info', - logLevels: { - triggerAfter: 'none', - triggerBeforeSuccess: 'warn', - }, - }); - - spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); - - const obj = new Parse.Object('TestClass'); - await obj.save(); - - let log = spy.calls.argsFor(0); - expect(log[0]).toEqual('warn'); - expect(log[1]).toMatch(/beforeSave triggered for TestClass for user .*/); - - log = spy.calls.argsFor(1); - expect(log).toEqual([]); - } + return spy; + }; + + spy = await execTest('silly', 'debug', 'silly'); + let log = spy.calls.argsFor(1); + expect(log[0]).toEqual('silly'); + expect(log[1]).toMatch(/beforeSave triggered for TestClass for user .*/); + log = spy.calls.argsFor(2); + expect(log[0]).toEqual('debug'); + expect(log[1]).toMatch(/afterSave triggered for TestClass for user .*/); + + spy = await execTest('info', 'none', 'warn'); + log = spy.calls.argsFor(0); + expect(log[0]).toEqual('warn'); + expect(log[1]).toMatch(/beforeSave triggered for TestClass for user .*/); + log = spy.calls.argsFor(1); + expect(log).toEqual([]); done(); }); From 30e21eb5a949620a6a646ddbf650bbe48bb7dc3c Mon Sep 17 00:00:00 2001 From: alljinx <42472198+alljinx@users.noreply.github.com> Date: Wed, 23 Nov 2022 14:56:13 +0100 Subject: [PATCH 06/21] Update src/Options/index.js Co-authored-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- src/Options/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Options/index.js b/src/Options/index.js index 74ed271306..249f229607 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -525,7 +525,7 @@ export interface AuthAdapter { } export interface LogLevels { - /* Log level used by the after hook trigger, default is 'info'. + /* Log level used by the Cloud Code Triggers `afterSave`, `afterDelete`, `afterSaveFile`, `afterDeleteFile`, `afterFind`, `afterLogout`. Default is `info`. :DEFAULT: info */ triggerAfter: ?string; From d99974095c3f7e00812a53084ce3b580d080c569 Mon Sep 17 00:00:00 2001 From: alljinx <42472198+alljinx@users.noreply.github.com> Date: Wed, 23 Nov 2022 14:56:29 +0100 Subject: [PATCH 07/21] Update src/Options/index.js Co-authored-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- src/Options/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Options/index.js b/src/Options/index.js index 249f229607..b852ba5efe 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -533,7 +533,7 @@ export interface LogLevels { :DEFAULT: info */ triggerBeforeSuccess: ?string; - /* Log level used by the error before hook trigger, default is 'error. + /* Log level used by the Cloud Code Triggers `beforeSave`, `beforeSaveFile`, `beforeDeleteFile`, `beforeFind`, `beforeLogin` on success. Default is `error `. :DEFAULT: error */ triggerBeforeError: ?string; From 3bada1284a34b7c8348a204cbdb4e7d20ceb5843 Mon Sep 17 00:00:00 2001 From: alljinx <42472198+alljinx@users.noreply.github.com> Date: Wed, 23 Nov 2022 14:56:49 +0100 Subject: [PATCH 08/21] Update src/Options/index.js Co-authored-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- src/Options/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Options/index.js b/src/Options/index.js index b852ba5efe..43acf4927a 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -529,7 +529,7 @@ export interface LogLevels { :DEFAULT: info */ triggerAfter: ?string; - /* Log level used by the success before hook trigger, default is 'info. + /* Log level used by the Cloud Code Triggers `beforeSave`, `beforeSaveFile`, `beforeDeleteFile`, `beforeFind`, `beforeLogin` on success. Default is `info`. :DEFAULT: info */ triggerBeforeSuccess: ?string; From 1b61a2e80477a655d01aad876e27404555eea4d3 Mon Sep 17 00:00:00 2001 From: alljinx Date: Wed, 23 Nov 2022 14:58:09 +0100 Subject: [PATCH 09/21] Minor correction on doc and regeneration. --- src/Options/Definitions.js | 9 ++++++--- src/Options/docs.js | 6 +++--- src/Options/index.js | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index fd4015b225..604aa58162 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -907,17 +907,20 @@ module.exports.AuthAdapter = { module.exports.LogLevels = { triggerAfter: { env: 'undefinedTRIGGER_AFTER', - help: "Log level used by the after hook trigger, default is 'info'.", + help: + 'Log level used by the Cloud Code Triggers `afterSave`, `afterDelete`, `afterSaveFile`, `afterDeleteFile`, `afterFind`, `afterLogout`. Default is `info`.', default: 'info', }, triggerBeforeError: { env: 'undefinedTRIGGER_BEFORE_ERROR', - help: "Log level used by the error before hook trigger, default is 'error.", + help: + 'Log level used by the Cloud Code Triggers `beforeSave`, `beforeSaveFile`, `beforeDeleteFile`, `beforeFind`, `beforeLogin` on error. Default is `error `.', default: 'error', }, triggerBeforeSuccess: { env: 'undefinedTRIGGER_BEFORE_SUCCESS', - help: "Log level used by the success before hook trigger, default is 'info.", + help: + 'Log level used by the Cloud Code Triggers `beforeSave`, `beforeSaveFile`, `beforeDeleteFile`, `beforeFind`, `beforeLogin` on success. Default is `info`.', default: 'info', }, }; diff --git a/src/Options/docs.js b/src/Options/docs.js index 4d2de3c1b1..97811a6225 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -219,7 +219,7 @@ /** * @interface LogLevels - * @property {String} triggerAfter Log level used by the after hook trigger, default is 'info'. - * @property {String} triggerBeforeError Log level used by the error before hook trigger, default is 'error. - * @property {String} triggerBeforeSuccess Log level used by the success before hook trigger, default is 'info. + * @property {String} triggerAfter Log level used by the Cloud Code Triggers `afterSave`, `afterDelete`, `afterSaveFile`, `afterDeleteFile`, `afterFind`, `afterLogout`. Default is `info`. + * @property {String} triggerBeforeError Log level used by the Cloud Code Triggers `beforeSave`, `beforeSaveFile`, `beforeDeleteFile`, `beforeFind`, `beforeLogin` on error. Default is `error `. + * @property {String} triggerBeforeSuccess Log level used by the Cloud Code Triggers `beforeSave`, `beforeSaveFile`, `beforeDeleteFile`, `beforeFind`, `beforeLogin` on success. Default is `info`. */ diff --git a/src/Options/index.js b/src/Options/index.js index 43acf4927a..53b1b1e710 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -533,7 +533,7 @@ export interface LogLevels { :DEFAULT: info */ triggerBeforeSuccess: ?string; - /* Log level used by the Cloud Code Triggers `beforeSave`, `beforeSaveFile`, `beforeDeleteFile`, `beforeFind`, `beforeLogin` on success. Default is `error `. + /* Log level used by the Cloud Code Triggers `beforeSave`, `beforeSaveFile`, `beforeDeleteFile`, `beforeFind`, `beforeLogin` on error. Default is `error `. :DEFAULT: error */ triggerBeforeError: ?string; From 6b4b787e5f465f20902d7e175ae364db076fcced Mon Sep 17 00:00:00 2001 From: Alexandre Ruiz Date: Wed, 23 Nov 2022 19:19:06 +0100 Subject: [PATCH 10/21] prettier && lint-fix --- src/Adapters/Files/GridFSBucketAdapter.js | 2 +- src/Security/CheckGroups/CheckGroupDatabase.js | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Adapters/Files/GridFSBucketAdapter.js b/src/Adapters/Files/GridFSBucketAdapter.js index 8ff4d2b8eb..f2b9c48fad 100644 --- a/src/Adapters/Files/GridFSBucketAdapter.js +++ b/src/Adapters/Files/GridFSBucketAdapter.js @@ -254,7 +254,7 @@ export class GridFSBucketAdapter extends FilesAdapter { stream.on('data', chunk => { res.write(chunk); }); - stream.on('error', (e) => { + stream.on('error', e => { res.status(404); res.send(e.message); }); diff --git a/src/Security/CheckGroups/CheckGroupDatabase.js b/src/Security/CheckGroups/CheckGroupDatabase.js index d0da79a4ba..f9b9340eb1 100644 --- a/src/Security/CheckGroups/CheckGroupDatabase.js +++ b/src/Security/CheckGroups/CheckGroupDatabase.js @@ -8,9 +8,9 @@ import Config from '../../Config'; import Parse from 'parse/node'; /** -* The security checks group for Parse Server configuration. -* Checks common Parse Server parameters such as access keys. -*/ + * The security checks group for Parse Server configuration. + * Checks common Parse Server parameters such as access keys. + */ class CheckGroupDatabase extends CheckGroup { setName() { return 'Database'; @@ -23,7 +23,8 @@ class CheckGroupDatabase extends CheckGroup { new Check({ title: 'Secure database password', warning: 'The database password is insecure and vulnerable to brute force attacks.', - solution: 'Choose a longer and/or more complex password with a combination of upper- and lowercase characters, numbers and special characters.', + solution: + 'Choose a longer and/or more complex password with a combination of upper- and lowercase characters, numbers and special characters.', check: () => { const password = databaseUrl.match(/\/\/\S+:(\S+)@/)[1]; const hasUpperCase = /[A-Z]/.test(password); From dfae832b1d537016e6493ed1ab4d5eca2557de9f Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Wed, 23 Nov 2022 23:31:06 +0100 Subject: [PATCH 11/21] Update spec/CloudCodeLogger.spec.js --- spec/CloudCodeLogger.spec.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index 43f7136ae7..d61ae2f1ef 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -197,10 +197,8 @@ describe('Cloud Code Logger', () => { }); spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); - const obj = new Parse.Object('TestClass'); await obj.save(); - return spy; }; From 4f466a62cdc15ba16de1b7af98c706148640e07d Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Wed, 23 Nov 2022 23:31:17 +0100 Subject: [PATCH 12/21] Update spec/CloudCodeLogger.spec.js --- spec/CloudCodeLogger.spec.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index d61ae2f1ef..f0934f7dce 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -216,8 +216,6 @@ describe('Cloud Code Logger', () => { expect(log[1]).toMatch(/beforeSave triggered for TestClass for user .*/); log = spy.calls.argsFor(1); expect(log).toEqual([]); - - done(); }); it('should log cloud function failure', done => { From d0bed5d1603797685759ef39ffd337ea8da3ba3e Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Wed, 23 Nov 2022 23:31:28 +0100 Subject: [PATCH 13/21] Update spec/CloudCodeLogger.spec.js --- spec/CloudCodeLogger.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index f0934f7dce..ce41628aed 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -182,7 +182,7 @@ describe('Cloud Code Logger', () => { }); }); - it('should log cloud function triggers using the custom log level', async done => { + it('should log cloud function triggers using the custom log level', async () => { Parse.Cloud.beforeSave('TestClass', () => {}); Parse.Cloud.afterSave('TestClass', () => {}); From 5fa9f72d113ee1934ed1e14b84d040da5ecfb580 Mon Sep 17 00:00:00 2001 From: alljinx <42472198+alljinx@users.noreply.github.com> Date: Thu, 24 Nov 2022 07:53:31 +0100 Subject: [PATCH 14/21] Update src/Config.js Co-authored-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- src/Config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Config.js b/src/Config.js index 5848189eb9..dc5a3b1d1e 100644 --- a/src/Config.js +++ b/src/Config.js @@ -6,7 +6,7 @@ import { isBoolean, isString } from 'lodash'; import net from 'net'; import AppCache from './cache'; import DatabaseController from './Controllers/DatabaseController'; -import { logLevels as ctrlLogLevels } from './Controllers/LoggerController'; +import LoggerController from './Controllers/LoggerController'; import { AccountLockoutOptions, FileUploadOptions, From 856d49f5f29fb81b40fef77de19d2b754058fd2b Mon Sep 17 00:00:00 2001 From: alljinx Date: Thu, 24 Nov 2022 08:05:59 +0100 Subject: [PATCH 15/21] Revert "Update src/Config.js" This reverts commit 5fa9f72d113ee1934ed1e14b84d040da5ecfb580. --- src/Config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Config.js b/src/Config.js index dc5a3b1d1e..5848189eb9 100644 --- a/src/Config.js +++ b/src/Config.js @@ -6,7 +6,7 @@ import { isBoolean, isString } from 'lodash'; import net from 'net'; import AppCache from './cache'; import DatabaseController from './Controllers/DatabaseController'; -import LoggerController from './Controllers/LoggerController'; +import { logLevels as ctrlLogLevels } from './Controllers/LoggerController'; import { AccountLockoutOptions, FileUploadOptions, From 41d47cef6897085d7356a9ffecef19b635b91874 Mon Sep 17 00:00:00 2001 From: alljinx Date: Thu, 24 Nov 2022 12:54:35 +0100 Subject: [PATCH 16/21] Fix undefinedTRIGGER_AFTER in Definitions.js --- resources/buildConfigDefinitions.js | 1 + src/Options/Definitions.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/buildConfigDefinitions.js b/resources/buildConfigDefinitions.js index 0b736561cc..7387d1eeba 100644 --- a/resources/buildConfigDefinitions.js +++ b/resources/buildConfigDefinitions.js @@ -43,6 +43,7 @@ const nestedOptionEnvPrefix = { PasswordPolicyOptions: 'PARSE_SERVER_PASSWORD_POLICY_', SecurityOptions: 'PARSE_SERVER_SECURITY_', SchemaOptions: 'PARSE_SERVER_SCHEMA_', + LogLevels: 'PARSE_SERVER_LOG_LEVELS_', }; function last(array) { diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index 604aa58162..a0b3c3b8ea 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -906,19 +906,19 @@ module.exports.AuthAdapter = { }; module.exports.LogLevels = { triggerAfter: { - env: 'undefinedTRIGGER_AFTER', + env: 'PARSE_SERVER_LOG_LEVELS_TRIGGER_AFTER', help: 'Log level used by the Cloud Code Triggers `afterSave`, `afterDelete`, `afterSaveFile`, `afterDeleteFile`, `afterFind`, `afterLogout`. Default is `info`.', default: 'info', }, triggerBeforeError: { - env: 'undefinedTRIGGER_BEFORE_ERROR', + env: 'PARSE_SERVER_LOG_LEVELS_TRIGGER_BEFORE_ERROR', help: 'Log level used by the Cloud Code Triggers `beforeSave`, `beforeSaveFile`, `beforeDeleteFile`, `beforeFind`, `beforeLogin` on error. Default is `error `.', default: 'error', }, triggerBeforeSuccess: { - env: 'undefinedTRIGGER_BEFORE_SUCCESS', + env: 'PARSE_SERVER_LOG_LEVELS_TRIGGER_BEFORE_SUCCESS', help: 'Log level used by the Cloud Code Triggers `beforeSave`, `beforeSaveFile`, `beforeDeleteFile`, `beforeFind`, `beforeLogin` on success. Default is `info`.', default: 'info', From 693389414c016152793ba6b84a1cfe8d4457914f Mon Sep 17 00:00:00 2001 From: alljinx Date: Thu, 24 Nov 2022 13:05:29 +0100 Subject: [PATCH 17/21] Remove the 'none' option from LogLevels. --- spec/CloudCodeLogger.spec.js | 2 +- src/Config.js | 5 ++- src/triggers.js | 68 ++++++++++++++++-------------------- 3 files changed, 34 insertions(+), 41 deletions(-) diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index ce41628aed..3a6bee9a10 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -210,7 +210,7 @@ describe('Cloud Code Logger', () => { expect(log[0]).toEqual('debug'); expect(log[1]).toMatch(/afterSave triggered for TestClass for user .*/); - spy = await execTest('info', 'none', 'warn'); + spy = await execTest('info', 'silly', 'warn'); log = spy.calls.argsFor(0); expect(log[0]).toEqual('warn'); expect(log[1]).toMatch(/beforeSave triggered for TestClass for user .*/); diff --git a/src/Config.js b/src/Config.js index 5848189eb9..be89b9c789 100644 --- a/src/Config.js +++ b/src/Config.js @@ -506,11 +506,10 @@ export class Config { } static validateLogLevels(logLevels) { - const possibleValues = ['none', ...ctrlLogLevels]; for (const key of Object.keys(LogLevels)) { if (logLevels[key]) { - if (possibleValues.indexOf(logLevels[key]) === -1) { - throw key + ' must be one of theses ' + possibleValues; + if (ctrlLogLevels.indexOf(logLevels[key]) === -1) { + throw key + ' must be one of theses ' + ctrlLogLevels; } } else { logLevels[key] = LogLevels[key].default; diff --git a/src/triggers.js b/src/triggers.js index 70490c8de2..b5f11435df 100644 --- a/src/triggers.js +++ b/src/triggers.js @@ -375,52 +375,46 @@ function userIdForLog(auth) { function logTriggerAfterHook(triggerType, className, input, auth, logLevel) { const cleanInput = logger.truncateLogMessage(JSON.stringify(input)); - if (logLevel !== 'none') { - logger[logLevel]( - `${triggerType} triggered for ${className} for user ${userIdForLog( - auth - )}:\n Input: ${cleanInput}`, - { - className, - triggerType, - user: userIdForLog(auth), - } - ); - } + logger[logLevel]( + `${triggerType} triggered for ${className} for user ${userIdForLog( + auth + )}:\n Input: ${cleanInput}`, + { + className, + triggerType, + user: userIdForLog(auth), + } + ); } function logTriggerSuccessBeforeHook(triggerType, className, input, result, auth, logLevel) { const cleanInput = logger.truncateLogMessage(JSON.stringify(input)); const cleanResult = logger.truncateLogMessage(JSON.stringify(result)); - if (logLevel !== 'none') { - logger[logLevel]( - `${triggerType} triggered for ${className} for user ${userIdForLog( - auth - )}:\n Input: ${cleanInput}\n Result: ${cleanResult}`, - { - className, - triggerType, - user: userIdForLog(auth), - } - ); - } + logger[logLevel]( + `${triggerType} triggered for ${className} for user ${userIdForLog( + auth + )}:\n Input: ${cleanInput}\n Result: ${cleanResult}`, + { + className, + triggerType, + user: userIdForLog(auth), + } + ); } function logTriggerErrorBeforeHook(triggerType, className, input, auth, error, logLevel) { const cleanInput = logger.truncateLogMessage(JSON.stringify(input)); - if (logLevel !== 'none') { - logger[logLevel]( - `${triggerType} failed for ${className} for user ${userIdForLog( - auth - )}:\n Input: ${cleanInput}\n Error: ${JSON.stringify(error)}`, - { - className, - triggerType, - error, - user: userIdForLog(auth), - } - ); - } + logger[logLevel]( + `${triggerType} failed for ${className} for user ${userIdForLog( + auth + )}:\n Input: ${cleanInput}\n Error: ${JSON.stringify(error)}`, + { + className, + triggerType, + error, + user: userIdForLog(auth), + } + ); } export function maybeRunAfterFindTrigger( From c768f8f5019a23c204a9b4cf52afe0c536aa9406 Mon Sep 17 00:00:00 2001 From: alljinx <42472198+alljinx@users.noreply.github.com> Date: Fri, 25 Nov 2022 07:05:43 +0100 Subject: [PATCH 18/21] Update src/Config.js Co-authored-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- src/Config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Config.js b/src/Config.js index be89b9c789..8f9510dcae 100644 --- a/src/Config.js +++ b/src/Config.js @@ -6,7 +6,7 @@ import { isBoolean, isString } from 'lodash'; import net from 'net'; import AppCache from './cache'; import DatabaseController from './Controllers/DatabaseController'; -import { logLevels as ctrlLogLevels } from './Controllers/LoggerController'; +import { logLevels as validLogLevels } from './Controllers/LoggerController'; import { AccountLockoutOptions, FileUploadOptions, From 25c0f6136799a586663896d195a7bb0196ac7b7c Mon Sep 17 00:00:00 2001 From: alljinx <42472198+alljinx@users.noreply.github.com> Date: Fri, 25 Nov 2022 07:09:30 +0100 Subject: [PATCH 19/21] Update src/Config.js Co-authored-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- src/Config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Config.js b/src/Config.js index 8f9510dcae..11aedc802c 100644 --- a/src/Config.js +++ b/src/Config.js @@ -509,7 +509,7 @@ export class Config { for (const key of Object.keys(LogLevels)) { if (logLevels[key]) { if (ctrlLogLevels.indexOf(logLevels[key]) === -1) { - throw key + ' must be one of theses ' + ctrlLogLevels; + throw `'${key}' must be one of ${JSON.stringify(validLogLevels)}`; } } else { logLevels[key] = LogLevels[key].default; From db15883035e22be577eee5306a09533a4d25fae7 Mon Sep 17 00:00:00 2001 From: alljinx Date: Fri, 25 Nov 2022 07:11:22 +0100 Subject: [PATCH 20/21] Fix ctrValidLogLevels -> validLogLevels --- src/Config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Config.js b/src/Config.js index 11aedc802c..f7deb7bcdf 100644 --- a/src/Config.js +++ b/src/Config.js @@ -508,7 +508,7 @@ export class Config { static validateLogLevels(logLevels) { for (const key of Object.keys(LogLevels)) { if (logLevels[key]) { - if (ctrlLogLevels.indexOf(logLevels[key]) === -1) { + if (validLogLevels.indexOf(logLevels[key]) === -1) { throw `'${key}' must be one of ${JSON.stringify(validLogLevels)}`; } } else { From 477b03eaba060f4723e2867b5fcfd1ec051b4920 Mon Sep 17 00:00:00 2001 From: alljinx Date: Sat, 26 Nov 2022 15:16:10 +0100 Subject: [PATCH 21/21] Fix postgres unit tests --- spec/CloudCodeLogger.spec.js | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index 3a6bee9a10..2cde9de640 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -186,7 +186,7 @@ describe('Cloud Code Logger', () => { Parse.Cloud.beforeSave('TestClass', () => {}); Parse.Cloud.afterSave('TestClass', () => {}); - const execTest = async (logLevel, triggerAfter, triggerBeforeSuccess) => { + const execTest = async (logLevel, triggerBeforeSuccess, triggerAfter) => { await reconfigureServer({ silent: true, logLevel, @@ -199,23 +199,22 @@ describe('Cloud Code Logger', () => { spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); const obj = new Parse.Object('TestClass'); await obj.save(); - return spy; + + return { + beforeSave: spy.calls + .allArgs() + .find(log => log[1].startsWith('beforeSave triggered for TestClass for user '))?.[0], + afterSave: spy.calls + .allArgs() + .find(log => log[1].startsWith('afterSave triggered for TestClass for user '))?.[0], + }; }; - spy = await execTest('silly', 'debug', 'silly'); - let log = spy.calls.argsFor(1); - expect(log[0]).toEqual('silly'); - expect(log[1]).toMatch(/beforeSave triggered for TestClass for user .*/); - log = spy.calls.argsFor(2); - expect(log[0]).toEqual('debug'); - expect(log[1]).toMatch(/afterSave triggered for TestClass for user .*/); - - spy = await execTest('info', 'silly', 'warn'); - log = spy.calls.argsFor(0); - expect(log[0]).toEqual('warn'); - expect(log[1]).toMatch(/beforeSave triggered for TestClass for user .*/); - log = spy.calls.argsFor(1); - expect(log).toEqual([]); + let calls = await execTest('silly', 'silly', 'debug'); + expect(calls).toEqual({ beforeSave: 'silly', afterSave: 'debug' }); + + calls = await execTest('info', 'warn', 'debug'); + expect(calls).toEqual({ beforeSave: 'warn', afterSave: undefined }); }); it('should log cloud function failure', done => {