mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-19 19:11:13 +00:00
feat(core): Validate shutdown handlers on startup (#8260)
This commit is contained in:
@@ -9,7 +9,7 @@ import { Logger } from '@/Logger';
|
|||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
import * as CrashJournal from '@/CrashJournal';
|
import * as CrashJournal from '@/CrashJournal';
|
||||||
import { LICENSE_FEATURES, inTest } from '@/constants';
|
import { LICENSE_FEATURES, inDevelopment, inTest } from '@/constants';
|
||||||
import { initErrorHandling } from '@/ErrorReporting';
|
import { initErrorHandling } from '@/ErrorReporting';
|
||||||
import { ExternalHooks } from '@/ExternalHooks';
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
import { NodeTypes } from '@/NodeTypes';
|
import { NodeTypes } from '@/NodeTypes';
|
||||||
@@ -63,6 +63,12 @@ export abstract class BaseCommand extends Command {
|
|||||||
this.exitWithCrash('There was an error initializing DB', error),
|
this.exitWithCrash('There was an error initializing DB', error),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// This needs to happen after DB.init() or otherwise DB Connection is not
|
||||||
|
// available via the dependency Container that services depend on.
|
||||||
|
if (inDevelopment || inTest) {
|
||||||
|
this.shutdownService.validate();
|
||||||
|
}
|
||||||
|
|
||||||
await this.server?.init();
|
await this.server?.init();
|
||||||
|
|
||||||
await Db.migrate().catch(async (error: Error) =>
|
await Db.migrate().catch(async (error: Error) =>
|
||||||
|
|||||||
@@ -39,6 +39,26 @@ export class ShutdownService {
|
|||||||
this.handlersByPriority[priority].push(handler);
|
this.handlersByPriority[priority].push(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Validates that all the registered shutdown handlers are properly configured */
|
||||||
|
validate() {
|
||||||
|
const handlers = this.handlersByPriority.flat();
|
||||||
|
|
||||||
|
for (const { serviceClass, methodName } of handlers) {
|
||||||
|
if (!Container.has(serviceClass)) {
|
||||||
|
throw new ApplicationError(
|
||||||
|
`Component "${serviceClass.name}" is not registered with the DI container. Any component using @OnShutdown() must be decorated with @Service()`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = Container.get(serviceClass);
|
||||||
|
if (!service[methodName]) {
|
||||||
|
throw new ApplicationError(
|
||||||
|
`Component "${serviceClass.name}" does not have a "${methodName}" method`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Signals all registered listeners that the application is shutting down */
|
/** Signals all registered listeners that the application is shutting down */
|
||||||
shutdown() {
|
shutdown() {
|
||||||
if (this.shutdownPromise) {
|
if (this.shutdownPromise) {
|
||||||
|
|||||||
@@ -124,4 +124,36 @@ describe('ShutdownService', () => {
|
|||||||
expect(shutdownService.isShuttingDown()).toBe(false);
|
expect(shutdownService.isShuttingDown()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('validate', () => {
|
||||||
|
it('should throw error if component is not registered with the DI container', () => {
|
||||||
|
class UnregisteredComponent {
|
||||||
|
onShutdown() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdownService.register(10, {
|
||||||
|
serviceClass: UnregisteredComponent as unknown as ServiceClass,
|
||||||
|
methodName: 'onShutdown',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => shutdownService.validate()).toThrow(
|
||||||
|
'Component "UnregisteredComponent" is not registered with the DI container. Any component using @OnShutdown() must be decorated with @Service()',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if component is missing the shutdown method', () => {
|
||||||
|
class TestComponent {}
|
||||||
|
|
||||||
|
shutdownService.register(10, {
|
||||||
|
serviceClass: TestComponent as unknown as ServiceClass,
|
||||||
|
methodName: 'onShutdown',
|
||||||
|
});
|
||||||
|
|
||||||
|
Container.set(TestComponent, new TestComponent());
|
||||||
|
|
||||||
|
expect(() => shutdownService.validate()).toThrow(
|
||||||
|
'Component "TestComponent" does not have a "onShutdown" method',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user