Skip to content

Commit

Permalink
refactor(core): Deduplicate isObjectLiteral, add docs and tests (#12332)
Browse files Browse the repository at this point in the history
  • Loading branch information
netroy authored Dec 20, 2024
1 parent 06b86af commit 724e085
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -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[] };
Expand Down
3 changes: 1 addition & 2 deletions packages/cli/src/logging/logger.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ 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';
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';
Expand Down
Original file line number Diff line number Diff line change
@@ -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' });
Expand Down
3 changes: 1 addition & 2 deletions packages/cli/src/services/credentials-tester.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;

Expand Down
4 changes: 0 additions & 4 deletions packages/cli/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/workflow-execute-additional-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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';
Expand Down
6 changes: 2 additions & 4 deletions packages/core/src/SerializedBuffer.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isObjectLiteral } from './utils';

/** A nodejs Buffer gone through JSON.stringify */
export type SerializedBuffer = {
type: 'Buffer';
Expand All @@ -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) &&
Expand Down
35 changes: 35 additions & 0 deletions packages/core/src/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 18 additions & 0 deletions packages/core/src/utils.ts
Original file line number Diff line number Diff line change
@@ -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'
);
}

0 comments on commit 724e085

Please sign in to comment.