mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(core): Improve cyclic dependency check in the DI container (#12600)
This commit is contained in:
parent
eceee7f3f8
commit
c3c4a20002
|
@ -1,2 +1,7 @@
|
||||||
/** @type {import('jest').Config} */
|
/** @type {import('jest').Config} */
|
||||||
module.exports = require('../../../jest.config');
|
module.exports = {
|
||||||
|
...require('../../../jest.config'),
|
||||||
|
transform: {
|
||||||
|
'^.+\\.ts$': ['ts-jest', { isolatedModules: false }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
17
packages/@n8n/di/src/__tests__/circular-depedency.test.ts
Normal file
17
packages/@n8n/di/src/__tests__/circular-depedency.test.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { ServiceA } from './fixtures/ServiceA';
|
||||||
|
import { ServiceB } from './fixtures/ServiceB';
|
||||||
|
import { Container } from '../di';
|
||||||
|
|
||||||
|
describe('DI Container', () => {
|
||||||
|
describe('circular dependency', () => {
|
||||||
|
it('should detect multilevel circular dependencies', () => {
|
||||||
|
expect(() => Container.get(ServiceA)).toThrow(
|
||||||
|
'[DI] Circular dependency detected in ServiceB at index 0.\nServiceA -> ServiceB',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => Container.get(ServiceB)).toThrow(
|
||||||
|
'[DI] Circular dependency detected in ServiceB at index 0.\nServiceB',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
8
packages/@n8n/di/src/__tests__/fixtures/ServiceA.ts
Normal file
8
packages/@n8n/di/src/__tests__/fixtures/ServiceA.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// eslint-disable-next-line import/no-cycle
|
||||||
|
import { ServiceB } from './ServiceB';
|
||||||
|
import { Service } from '../../di';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class ServiceA {
|
||||||
|
constructor(readonly b: ServiceB) {}
|
||||||
|
}
|
8
packages/@n8n/di/src/__tests__/fixtures/ServiceB.ts
Normal file
8
packages/@n8n/di/src/__tests__/fixtures/ServiceB.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// eslint-disable-next-line import/no-cycle
|
||||||
|
import { ServiceA } from './ServiceA';
|
||||||
|
import { Service } from '../../di';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class ServiceB {
|
||||||
|
constructor(readonly a: ServiceA) {}
|
||||||
|
}
|
|
@ -78,13 +78,6 @@ class ContainerClass {
|
||||||
|
|
||||||
if (metadata?.instance) return metadata.instance as T;
|
if (metadata?.instance) return metadata.instance as T;
|
||||||
|
|
||||||
// Check for circular dependencies before proceeding with instantiation
|
|
||||||
if (resolutionStack.includes(type)) {
|
|
||||||
throw new DIError(
|
|
||||||
`Circular dependency detected. ${resolutionStack.map((t) => t.name).join(' -> ')}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add current type to resolution stack before resolving dependencies
|
// Add current type to resolution stack before resolving dependencies
|
||||||
resolutionStack.push(type);
|
resolutionStack.push(type);
|
||||||
|
|
||||||
|
@ -96,9 +89,15 @@ class ContainerClass {
|
||||||
} else {
|
} else {
|
||||||
const paramTypes = (Reflect.getMetadata('design:paramtypes', type) ??
|
const paramTypes = (Reflect.getMetadata('design:paramtypes', type) ??
|
||||||
[]) as Constructable[];
|
[]) as Constructable[];
|
||||||
const dependencies = paramTypes.map(<P>(paramType: Constructable<P>) =>
|
|
||||||
this.get(paramType),
|
const dependencies = paramTypes.map(<P>(paramType: Constructable<P>, index: number) => {
|
||||||
|
if (paramType === undefined) {
|
||||||
|
throw new DIError(
|
||||||
|
`Circular dependency detected in ${type.name} at index ${index}.\n${resolutionStack.map((t) => t.name).join(' -> ')}\n`,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
return this.get(paramType);
|
||||||
|
});
|
||||||
// Create new instance with resolved dependencies
|
// Create new instance with resolved dependencies
|
||||||
instance = new (type as Constructable)(...dependencies) as T;
|
instance = new (type as Constructable)(...dependencies) as T;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue