mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57:29 -08:00
refactor(core): Deduplicate isObjectLiteral, add docs and tests (#12332)
Some checks failed
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions
Benchmark Docker Image CI / build (push) Has been cancelled
Some checks failed
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions
Benchmark Docker Image CI / build (push) Has been cancelled
This commit is contained in:
parent
06b86af735
commit
724e08562f
|
@ -1,7 +1,7 @@
|
|||
import { isObjectLiteral } from 'n8n-core';
|
||||
import type { IDataObject, INodeExecutionData } from 'n8n-workflow';
|
||||
|
||||
import type { MigrationContext, IrreversibleMigration } from '@/databases/types';
|
||||
import { isObjectLiteral } from '@/utils';
|
||||
|
||||
type OldPinnedData = { [nodeName: string]: IDataObject[] };
|
||||
type NewPinnedData = { [nodeName: string]: INodeExecutionData[] };
|
||||
|
|
|
@ -2,7 +2,7 @@ import type { LogScope } from '@n8n/config';
|
|||
import { GlobalConfig } from '@n8n/config';
|
||||
import callsites from 'callsites';
|
||||
import type { TransformableInfo } from 'logform';
|
||||
import { InstanceSettings } from 'n8n-core';
|
||||
import { InstanceSettings, isObjectLiteral } from 'n8n-core';
|
||||
import { LoggerProxy, LOG_LEVELS } from 'n8n-workflow';
|
||||
import path, { basename } from 'node:path';
|
||||
import pc from 'picocolors';
|
||||
|
@ -10,7 +10,6 @@ import { Service } from 'typedi';
|
|||
import winston from 'winston';
|
||||
|
||||
import { inDevelopment, inProduction } from '@/constants';
|
||||
import { isObjectLiteral } from '@/utils';
|
||||
|
||||
import { noOp } from './constants';
|
||||
import type { LogLocationMetadata, LogLevel, LogMetadata } from './types';
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { plainToInstance, instanceToPlain } from 'class-transformer';
|
||||
import { validate } from 'class-validator';
|
||||
import { isObjectLiteral } from 'n8n-core';
|
||||
import { ApplicationError, jsonParse } from 'n8n-workflow';
|
||||
|
||||
import { isObjectLiteral } from '@/utils';
|
||||
|
||||
export class BaseFilter {
|
||||
protected static async toFilter(rawFilter: string, Filter: typeof BaseFilter) {
|
||||
const dto = jsonParse(rawFilter, { errorMessage: 'Failed to parse filter JSON' });
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
import get from 'lodash/get';
|
||||
import { ErrorReporter, NodeExecuteFunctions, RoutingNode } from 'n8n-core';
|
||||
import { ErrorReporter, NodeExecuteFunctions, RoutingNode, isObjectLiteral } from 'n8n-core';
|
||||
import type {
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunction,
|
||||
|
@ -34,7 +34,6 @@ import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-da
|
|||
|
||||
import { RESPONSE_ERROR_MESSAGES } from '../constants';
|
||||
import { CredentialsHelper } from '../credentials-helper';
|
||||
import { isObjectLiteral } from '../utils';
|
||||
|
||||
const { OAUTH2_CREDENTIAL_TEST_SUCCEEDED, OAUTH2_CREDENTIAL_TEST_FAILED } = RESPONSE_ERROR_MESSAGES;
|
||||
|
||||
|
|
|
@ -58,10 +58,6 @@ export function isStringArray(value: unknown): value is string[] {
|
|||
|
||||
export const isIntegerString = (value: string) => /^\d+$/.test(value);
|
||||
|
||||
export function isObjectLiteral(item: unknown): item is { [key: string]: unknown } {
|
||||
return typeof item === 'object' && item !== null && !Array.isArray(item);
|
||||
}
|
||||
|
||||
export function removeTrailingSlash(path: string) {
|
||||
return path.endsWith('/') ? path.slice(0, -1) : path;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
import type { PushType } from '@n8n/api-types';
|
||||
import { GlobalConfig } from '@n8n/config';
|
||||
import { stringify } from 'flatted';
|
||||
import { ErrorReporter, WorkflowExecute } from 'n8n-core';
|
||||
import { ErrorReporter, WorkflowExecute, isObjectLiteral } from 'n8n-core';
|
||||
import { ApplicationError, NodeOperationError, Workflow, WorkflowHooks } from 'n8n-workflow';
|
||||
import type {
|
||||
IDataObject,
|
||||
|
@ -45,7 +45,7 @@ import type { IWorkflowErrorData, UpdateExecutionPayload } from '@/interfaces';
|
|||
import { NodeTypes } from '@/node-types';
|
||||
import { Push } from '@/push';
|
||||
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
|
||||
import { findSubworkflowStart, isObjectLiteral, isWorkflowIdValid } from '@/utils';
|
||||
import { findSubworkflowStart, isWorkflowIdValid } from '@/utils';
|
||||
import * as WorkflowHelpers from '@/workflow-helpers';
|
||||
|
||||
import { WorkflowRepository } from './databases/repositories/workflow.repository';
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { isObjectLiteral } from './utils';
|
||||
|
||||
/** A nodejs Buffer gone through JSON.stringify */
|
||||
export type SerializedBuffer = {
|
||||
type: 'Buffer';
|
||||
|
@ -9,10 +11,6 @@ export function toBuffer(serializedBuffer: SerializedBuffer): Buffer {
|
|||
return Buffer.from(serializedBuffer.data);
|
||||
}
|
||||
|
||||
function isObjectLiteral(item: unknown): item is { [key: string]: unknown } {
|
||||
return typeof item === 'object' && item !== null && !Array.isArray(item);
|
||||
}
|
||||
|
||||
export function isSerializedBuffer(candidate: unknown): candidate is SerializedBuffer {
|
||||
return (
|
||||
isObjectLiteral(candidate) &&
|
||||
|
|
35
packages/core/src/__tests__/utils.test.ts
Normal file
35
packages/core/src/__tests__/utils.test.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { isObjectLiteral } from '@/utils';
|
||||
|
||||
describe('isObjectLiteral', () => {
|
||||
test.each([
|
||||
['empty object literal', {}, true],
|
||||
['object with properties', { foo: 'bar', num: 123 }, true],
|
||||
['nested object literal', { nested: { foo: 'bar' } }, true],
|
||||
['object with symbol key', { [Symbol.for('foo')]: 'bar' }, true],
|
||||
['null', null, false],
|
||||
['empty array', [], false],
|
||||
['array with values', [1, 2, 3], false],
|
||||
['number', 42, false],
|
||||
['string', 'string', false],
|
||||
['boolean', true, false],
|
||||
['undefined', undefined, false],
|
||||
['Date object', new Date(), false],
|
||||
['RegExp object', new RegExp(''), false],
|
||||
['Map object', new Map(), false],
|
||||
['Set object', new Set(), false],
|
||||
['arrow function', () => {}, false],
|
||||
['regular function', function () {}, false],
|
||||
['class instance', new (class TestClass {})(), false],
|
||||
['object with custom prototype', Object.create({ customMethod: () => {} }), true],
|
||||
['Object.create(null)', Object.create(null), false],
|
||||
['Buffer', Buffer.from('test'), false],
|
||||
['Serialized Buffer', Buffer.from('test').toJSON(), true],
|
||||
['Promise', new Promise(() => {}), false],
|
||||
])('should return %s for %s', (_, input, expected) => {
|
||||
expect(isObjectLiteral(input)).toBe(expected);
|
||||
});
|
||||
|
||||
it('should return false for Error objects', () => {
|
||||
expect(isObjectLiteral(new Error())).toBe(false);
|
||||
});
|
||||
});
|
|
@ -25,3 +25,4 @@ export * from './node-execution-context';
|
|||
export * from './PartialExecutionUtils';
|
||||
export { ErrorReporter } from './error-reporter';
|
||||
export * from './SerializedBuffer';
|
||||
export { isObjectLiteral } from './utils';
|
||||
|
|
18
packages/core/src/utils.ts
Normal file
18
packages/core/src/utils.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
type ObjectLiteral = { [key: string | symbol]: unknown };
|
||||
|
||||
/**
|
||||
* Checks if the provided value is a plain object literal (not null, not an array, not a class instance, and not a primitive).
|
||||
* This function serves as a type guard.
|
||||
*
|
||||
* @param candidate - The value to check
|
||||
* @returns {boolean} True if the value is an object literal, false otherwise
|
||||
*/
|
||||
export function isObjectLiteral(candidate: unknown): candidate is ObjectLiteral {
|
||||
return (
|
||||
typeof candidate === 'object' &&
|
||||
candidate !== null &&
|
||||
!Array.isArray(candidate) &&
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
(Object.getPrototypeOf(candidate) as Object)?.constructor?.name === 'Object'
|
||||
);
|
||||
}
|
Loading…
Reference in a new issue