From 6c4a154427fd283f07f56f743ed2981d42769e95 Mon Sep 17 00:00:00 2001 From: Michael Auerswald Date: Tue, 21 Mar 2023 14:21:02 +0100 Subject: [PATCH] remove circular refs from code items (and lint fixes) --- packages/cli/src/CommunityNodes/helpers.ts | 2 +- .../MessageEventBusLogWriter.ts | 8 ++--- packages/cli/src/push/abstract.push.ts | 4 +-- .../src/sso/saml/routes/saml.controller.ee.ts | 4 --- packages/nodes-base/nodes/Code/utils.ts | 28 ++++++++++++----- packages/workflow/src/index.ts | 2 +- packages/workflow/src/utils.ts | 30 +++++++++++++++++++ 7 files changed, 59 insertions(+), 19 deletions(-) diff --git a/packages/cli/src/CommunityNodes/helpers.ts b/packages/cli/src/CommunityNodes/helpers.ts index 295f6a1072a0d..6cbe4134c4f75 100644 --- a/packages/cli/src/CommunityNodes/helpers.ts +++ b/packages/cli/src/CommunityNodes/helpers.ts @@ -207,7 +207,7 @@ export function hasPackageLoaded(packageName: string): boolean { export function removePackageFromMissingList(packageName: string): void { try { - const failedPackages = (config.get('nodes.packagesMissing') as string).split(' '); + const failedPackages = config.get('nodes.packagesMissing').split(' '); const packageFailedToLoad = failedPackages.filter( (packageNameAndVersion) => diff --git a/packages/cli/src/eventbus/MessageEventBusWriter/MessageEventBusLogWriter.ts b/packages/cli/src/eventbus/MessageEventBusWriter/MessageEventBusLogWriter.ts index c8dc5f74ba593..c9ea2c3eac9c0 100644 --- a/packages/cli/src/eventbus/MessageEventBusWriter/MessageEventBusLogWriter.ts +++ b/packages/cli/src/eventbus/MessageEventBusWriter/MessageEventBusLogWriter.ts @@ -159,8 +159,8 @@ export class MessageEventBusLogWriter { unfinishedExecutions: {}, }; const logCount = logHistory - ? Math.min(config.get('eventBus.logWriter.keepLogCount') as number, logHistory) - : (config.get('eventBus.logWriter.keepLogCount') as number); + ? Math.min(config.get('eventBus.logWriter.keepLogCount'), logHistory) + : config.get('eventBus.logWriter.keepLogCount'); for (let i = logCount; i >= 0; i--) { const logFileName = this.getLogFileName(i); if (logFileName) { @@ -256,8 +256,8 @@ export class MessageEventBusLogWriter { ): Promise { const result: EventMessageTypes[] = []; const logCount = logHistory - ? Math.min(config.get('eventBus.logWriter.keepLogCount') as number, logHistory) - : (config.get('eventBus.logWriter.keepLogCount') as number); + ? Math.min(config.get('eventBus.logWriter.keepLogCount'), logHistory) + : config.get('eventBus.logWriter.keepLogCount'); for (let i = 0; i < logCount; i++) { const logFileName = this.getLogFileName(i); if (logFileName) { diff --git a/packages/cli/src/push/abstract.push.ts b/packages/cli/src/push/abstract.push.ts index 633cb126a8f0b..a9022bdc5cbd5 100644 --- a/packages/cli/src/push/abstract.push.ts +++ b/packages/cli/src/push/abstract.push.ts @@ -1,4 +1,4 @@ -import { LoggerProxy as Logger } from 'n8n-workflow'; +import { jsonStringify, LoggerProxy as Logger } from 'n8n-workflow'; import type { IPushDataType } from '@/Interfaces'; import { eventBus } from '../eventbus'; @@ -38,7 +38,7 @@ export abstract class AbstractPush { Logger.debug(`Send data of type "${type}" to editor-UI`, { dataType: type, sessionId }); - const sendData = JSON.stringify({ type, data }); + const sendData = jsonStringify({ type, data }, { replaceCircularRefs: true }); if (sessionId === undefined) { // Send to all connected clients diff --git a/packages/cli/src/sso/saml/routes/saml.controller.ee.ts b/packages/cli/src/sso/saml/routes/saml.controller.ee.ts index d5cc9969d2621..d4d8a2343c78d 100644 --- a/packages/cli/src/sso/saml/routes/saml.controller.ee.ts +++ b/packages/cli/src/sso/saml/routes/saml.controller.ee.ts @@ -10,7 +10,6 @@ import { SamlService } from '../saml.service.ee'; import { SamlConfiguration } from '../types/requests'; import { AuthError, BadRequestError } from '../../../ResponseHelper'; import { getInitSSOFormView } from '../views/initSsoPost'; -import { getInitSSOPostView } from '../views/initSsoRedirect'; import { issueCookie } from '../../../auth/jwt'; import { validate } from 'class-validator'; import type { PostBindingContext } from 'samlify/types/src/entity'; @@ -136,9 +135,6 @@ export class SamlController { private async handleInitSSO(res: express.Response) { const result = this.samlService.getLoginRequestUrl(); if (result?.binding === 'redirect') { - // forced client side redirect through the use of a javascript redirect - // return res.send(getInitSSOPostView(result.context)); - // Return the redirect URL directly return res.send(result.context.context); } else if (result?.binding === 'post') { return res.send(getInitSSOFormView(result.context as PostBindingContext)); diff --git a/packages/nodes-base/nodes/Code/utils.ts b/packages/nodes-base/nodes/Code/utils.ts index e68f3cd27ca77..42fea8bebd105 100644 --- a/packages/nodes-base/nodes/Code/utils.ts +++ b/packages/nodes-base/nodes/Code/utils.ts @@ -1,4 +1,5 @@ import type { IDataObject } from 'n8n-workflow'; +import { jsonStringify } from 'n8n-workflow'; export function isObject(maybe: unknown): maybe is { [key: string]: unknown } { return typeof maybe === 'object' && maybe !== null && !Array.isArray(maybe); @@ -12,15 +13,28 @@ function isTraversable(maybe: unknown): maybe is IDataObject { * Stringify any non-standard JS objects (e.g. `Date`, `RegExp`) inside output items at any depth. */ export function standardizeOutput(output: IDataObject) { - for (const [key, value] of Object.entries(output)) { - if (!isTraversable(value)) continue; + const knownObjects = new WeakSet(); - output[key] = - value.constructor.name !== 'Object' - ? JSON.stringify(value) // Date, RegExp, etc. - : standardizeOutput(value); - } + function standardizeOutputRecursive(obj: IDataObject): IDataObject { + for (const [key, value] of Object.entries(obj)) { + if (!isTraversable(value)) continue; + + if (typeof value === 'object' && value !== null) { + if (knownObjects.has(value)) { + // Found circular reference + continue; + } + knownObjects.add(value); + } + obj[key] = + value.constructor.name !== 'Object' + ? JSON.stringify(value) // Date, RegExp, etc. + : standardizeOutputRecursive(value); + } + return obj; + } + standardizeOutputRecursive(output); return output; } diff --git a/packages/workflow/src/index.ts b/packages/workflow/src/index.ts index 117440e417189..63e71425fa225 100644 --- a/packages/workflow/src/index.ts +++ b/packages/workflow/src/index.ts @@ -23,7 +23,7 @@ export * from './WorkflowErrors'; export * from './WorkflowHooks'; export * from './VersionedNodeType'; export { LoggerProxy, NodeHelpers, ObservableObject, TelemetryHelpers }; -export { deepCopy, jsonParse, sleep, fileTypeFromMimeType, assert } from './utils'; +export { deepCopy, jsonParse, jsonStringify, sleep, fileTypeFromMimeType, assert } from './utils'; export { isINodeProperties, isINodePropertyOptions, diff --git a/packages/workflow/src/utils.ts b/packages/workflow/src/utils.ts index 9bb0444339281..74e88f91c298a 100644 --- a/packages/workflow/src/utils.ts +++ b/packages/workflow/src/utils.ts @@ -62,6 +62,36 @@ export const jsonParse = (jsonString: string, options?: JSONParseOptions): } }; +type JSONStringifyOptions = { + replaceCircularRefs?: boolean; + circularRefReplacement?: string; +}; + +const getReplaceCircularReferencesFn = (options: JSONStringifyOptions) => { + const knownObjects = new WeakSet(); + return (key: any, value: any) => { + if (typeof value === 'object' && value !== null) { + if (knownObjects.has(value)) { + return options?.circularRefReplacement ?? '[Circular Reference]'; + } + knownObjects.add(value); + } + return value; + }; +}; + +export const jsonStringify = (obj: unknown, options?: JSONStringifyOptions): string => { + try { + if (options?.replaceCircularRefs === true) { + return JSON.stringify(obj, getReplaceCircularReferencesFn(options)); + } + return JSON.stringify(obj); + } catch (error) { + // TOOD: handle stringify errors? + throw error; + } +}; + export const sleep = async (ms: number): Promise => new Promise((resolve) => { setTimeout(resolve, ms);