mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
refactor(core): Add shutdown method to modules (#17322)
This commit is contained in:
@@ -113,6 +113,22 @@ export class ModuleRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
async shutdownModule(moduleName: ModuleName) {
|
||||
const moduleEntry = this.moduleMetadata.get(moduleName);
|
||||
|
||||
if (!moduleEntry) {
|
||||
this.logger.debug('Skipping shutdown for unregistered module', { moduleName });
|
||||
return;
|
||||
}
|
||||
|
||||
await Container.get(moduleEntry.class).shutdown?.();
|
||||
|
||||
const index = this.activeModules.indexOf(moduleName);
|
||||
if (index > -1) this.activeModules.splice(index, 1);
|
||||
|
||||
this.logger.debug(`Shut down module "${moduleName}"`);
|
||||
}
|
||||
|
||||
isActive(moduleName: ModuleName) {
|
||||
return this.activeModules.includes(moduleName);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ export class ModuleMetadata {
|
||||
this.modules.set(moduleName, moduleEntry);
|
||||
}
|
||||
|
||||
get(moduleName: string) {
|
||||
return this.modules.get(moduleName);
|
||||
}
|
||||
|
||||
getEntries() {
|
||||
return [...this.modules.entries()];
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ export type ModuleSettings = Record<string, unknown>;
|
||||
|
||||
export interface ModuleInterface {
|
||||
init?(): Promise<void>;
|
||||
shutdown?(): Promise<void>;
|
||||
entities?(): Promise<EntityClass[]>;
|
||||
settings?(): Promise<ModuleSettings>;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Container, Service } from '@n8n/di';
|
||||
|
||||
import { OnShutdown } from '../on-shutdown';
|
||||
import { ShutdownRegistryMetadata } from '../shutdown-registry-metadata';
|
||||
import { ShutdownMetadata } from '../shutdown-metadata';
|
||||
|
||||
describe('OnShutdown', () => {
|
||||
let shutdownRegistryMetadata: ShutdownRegistryMetadata;
|
||||
let shutdownMetadata: ShutdownMetadata;
|
||||
|
||||
beforeEach(() => {
|
||||
shutdownRegistryMetadata = new ShutdownRegistryMetadata();
|
||||
Container.set(ShutdownRegistryMetadata, shutdownRegistryMetadata);
|
||||
jest.spyOn(shutdownRegistryMetadata, 'register');
|
||||
shutdownMetadata = new ShutdownMetadata();
|
||||
Container.set(ShutdownMetadata, shutdownMetadata);
|
||||
jest.spyOn(shutdownMetadata, 'register');
|
||||
});
|
||||
|
||||
it('should register a methods that is decorated with OnShutdown', () => {
|
||||
@@ -19,8 +19,8 @@ describe('OnShutdown', () => {
|
||||
async onShutdown() {}
|
||||
}
|
||||
|
||||
expect(shutdownRegistryMetadata.register).toHaveBeenCalledTimes(1);
|
||||
expect(shutdownRegistryMetadata.register).toHaveBeenCalledWith(100, {
|
||||
expect(shutdownMetadata.register).toHaveBeenCalledTimes(1);
|
||||
expect(shutdownMetadata.register).toHaveBeenCalledWith(100, {
|
||||
methodName: 'onShutdown',
|
||||
serviceClass: TestClass,
|
||||
});
|
||||
@@ -36,12 +36,12 @@ describe('OnShutdown', () => {
|
||||
async two() {}
|
||||
}
|
||||
|
||||
expect(shutdownRegistryMetadata.register).toHaveBeenCalledTimes(2);
|
||||
expect(shutdownRegistryMetadata.register).toHaveBeenCalledWith(100, {
|
||||
expect(shutdownMetadata.register).toHaveBeenCalledTimes(2);
|
||||
expect(shutdownMetadata.register).toHaveBeenCalledWith(100, {
|
||||
methodName: 'one',
|
||||
serviceClass: TestClass,
|
||||
});
|
||||
expect(shutdownRegistryMetadata.register).toHaveBeenCalledWith(100, {
|
||||
expect(shutdownMetadata.register).toHaveBeenCalledWith(100, {
|
||||
methodName: 'two',
|
||||
serviceClass: TestClass,
|
||||
});
|
||||
@@ -56,9 +56,9 @@ describe('OnShutdown', () => {
|
||||
}
|
||||
}
|
||||
|
||||
expect(shutdownRegistryMetadata.register).toHaveBeenCalledTimes(1);
|
||||
expect(shutdownMetadata.register).toHaveBeenCalledTimes(1);
|
||||
// @ts-expect-error We are checking internal parts of the shutdown service
|
||||
expect(shutdownRegistryMetadata.handlersByPriority[10].length).toEqual(1);
|
||||
expect(shutdownMetadata.handlersByPriority[10].length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should throw an error if the decorated member is not a function', () => {
|
||||
|
||||
@@ -3,6 +3,6 @@ export {
|
||||
DEFAULT_SHUTDOWN_PRIORITY,
|
||||
LOWEST_SHUTDOWN_PRIORITY,
|
||||
} from './constants';
|
||||
export { ShutdownRegistryMetadata } from './shutdown-registry-metadata';
|
||||
export { ShutdownMetadata } from './shutdown-metadata';
|
||||
export { OnShutdown } from './on-shutdown';
|
||||
export type { ShutdownHandler, ShutdownServiceClass } from './types';
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Container } from '@n8n/di';
|
||||
import { UnexpectedError } from 'n8n-workflow';
|
||||
|
||||
import { DEFAULT_SHUTDOWN_PRIORITY } from './constants';
|
||||
import { ShutdownRegistryMetadata } from './shutdown-registry-metadata';
|
||||
import { ShutdownMetadata } from './shutdown-metadata';
|
||||
import type { ShutdownServiceClass } from './types';
|
||||
|
||||
/**
|
||||
@@ -31,7 +31,7 @@ export const OnShutdown =
|
||||
const methodName = String(propertyKey);
|
||||
// TODO: assert that serviceClass is decorated with @Service
|
||||
if (typeof descriptor?.value === 'function') {
|
||||
Container.get(ShutdownRegistryMetadata).register(priority, { serviceClass, methodName });
|
||||
Container.get(ShutdownMetadata).register(priority, { serviceClass, methodName });
|
||||
} else {
|
||||
const name = `${serviceClass.name}.${methodName}()`;
|
||||
throw new UnexpectedError(
|
||||
|
||||
@@ -5,7 +5,7 @@ import { HIGHEST_SHUTDOWN_PRIORITY, LOWEST_SHUTDOWN_PRIORITY } from './constants
|
||||
import type { ShutdownHandler } from './types';
|
||||
|
||||
@Service()
|
||||
export class ShutdownRegistryMetadata {
|
||||
export class ShutdownMetadata {
|
||||
private handlersByPriority: ShutdownHandler[][] = [];
|
||||
|
||||
register(priority: number, handler: ShutdownHandler) {
|
||||
Reference in New Issue
Block a user