mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
refactor(core): Allow modules to define paths to load nodes from (#18193)
This commit is contained in:
@@ -218,3 +218,22 @@ describe('initModules', () => {
|
||||
expect(moduleRegistry.getActiveModules()).toEqual([moduleName]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadDir', () => {
|
||||
it('should load dirs defined by modules', async () => {
|
||||
const TEST_LOAD_DIR = '/path/to/module/load/dir';
|
||||
const ModuleClass = {
|
||||
entities: jest.fn().mockReturnValue([]),
|
||||
loadDir: jest.fn().mockReturnValue(TEST_LOAD_DIR),
|
||||
};
|
||||
const moduleMetadata = mock<ModuleMetadata>({
|
||||
getClasses: jest.fn().mockReturnValue([ModuleClass]),
|
||||
});
|
||||
Container.get = jest.fn().mockReturnValue(ModuleClass);
|
||||
const moduleRegistry = new ModuleRegistry(moduleMetadata, mock(), mock(), mock());
|
||||
|
||||
await moduleRegistry.loadModules([]); // empty to skip dynamic imports
|
||||
|
||||
expect(moduleRegistry.loadDirs).toEqual([TEST_LOAD_DIR]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,6 +15,8 @@ import { Logger } from '../logging/logger';
|
||||
export class ModuleRegistry {
|
||||
readonly entities: EntityClass[] = [];
|
||||
|
||||
readonly loadDirs: string[] = [];
|
||||
|
||||
readonly settings: Map<string, ModuleSettings> = new Map();
|
||||
|
||||
constructor(
|
||||
@@ -79,9 +81,11 @@ export class ModuleRegistry {
|
||||
for (const ModuleClass of this.moduleMetadata.getClasses()) {
|
||||
const entities = await Container.get(ModuleClass).entities?.();
|
||||
|
||||
if (!entities || entities.length === 0) continue;
|
||||
if (entities?.length) this.entities.push(...entities);
|
||||
|
||||
this.entities.push(...entities);
|
||||
const loadDir = Container.get(ModuleClass).loadDir?.();
|
||||
|
||||
if (loadDir) this.loadDirs.push(loadDir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,12 @@ export interface ModuleInterface {
|
||||
shutdown?(): Promise<void>;
|
||||
entities?(): Promise<EntityClass[]>;
|
||||
settings?(): Promise<ModuleSettings>;
|
||||
|
||||
/**
|
||||
* @returns Path to a dir to load nodes and credentials from.
|
||||
* @example '/Users/nathan/.n8n/nodes/node_modules'
|
||||
*/
|
||||
loadDir?(): string;
|
||||
}
|
||||
|
||||
export type ModuleClass = Constructable<ModuleInterface>;
|
||||
|
||||
@@ -30,7 +30,7 @@ describe('LoadNodesAndCredentials', () => {
|
||||
let instance: LoadNodesAndCredentials;
|
||||
|
||||
beforeEach(() => {
|
||||
instance = new LoadNodesAndCredentials(mock(), mock(), mock(), mock());
|
||||
instance = new LoadNodesAndCredentials(mock(), mock(), mock(), mock(), mock());
|
||||
instance.loaders.package1 = mock<DirectoryLoader>({
|
||||
directory: '/icons/package1',
|
||||
});
|
||||
@@ -58,7 +58,7 @@ describe('LoadNodesAndCredentials', () => {
|
||||
});
|
||||
|
||||
describe('convertNodeToAiTool', () => {
|
||||
const instance = new LoadNodesAndCredentials(mock(), mock(), mock(), mock());
|
||||
const instance = new LoadNodesAndCredentials(mock(), mock(), mock(), mock(), mock());
|
||||
|
||||
let fullNodeWrapper: { description: INodeTypeDescription };
|
||||
|
||||
@@ -290,7 +290,7 @@ describe('LoadNodesAndCredentials', () => {
|
||||
let instance: LoadNodesAndCredentials;
|
||||
|
||||
beforeEach(() => {
|
||||
instance = new LoadNodesAndCredentials(mock(), mock(), mock(), mock());
|
||||
instance = new LoadNodesAndCredentials(mock(), mock(), mock(), mock(), mock());
|
||||
instance.knownNodes['n8n-nodes-base.test'] = {
|
||||
className: 'Test',
|
||||
sourcePath: '/nodes-base/dist/nodes/Test/Test.node.js',
|
||||
@@ -330,7 +330,7 @@ describe('LoadNodesAndCredentials', () => {
|
||||
let instance: LoadNodesAndCredentials;
|
||||
|
||||
beforeEach(() => {
|
||||
instance = new LoadNodesAndCredentials(mock(), mock(), mock(), mock());
|
||||
instance = new LoadNodesAndCredentials(mock(), mock(), mock(), mock(), mock());
|
||||
instance.types.nodes = [
|
||||
{
|
||||
name: 'testNode',
|
||||
@@ -455,7 +455,7 @@ describe('LoadNodesAndCredentials', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
instance = new LoadNodesAndCredentials(mock(), mock(), mock(), mock());
|
||||
instance = new LoadNodesAndCredentials(mock(), mock(), mock(), mock(), mock());
|
||||
instance.loaders = { CUSTOM: mockLoader };
|
||||
|
||||
// Allow access to directory
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { inTest, isContainedWithin, Logger } from '@n8n/backend-common';
|
||||
import { inTest, isContainedWithin, Logger, ModuleRegistry } from '@n8n/backend-common';
|
||||
import { GlobalConfig } from '@n8n/config';
|
||||
import { Container, Service } from '@n8n/di';
|
||||
import type ParcelWatcher from '@parcel/watcher';
|
||||
@@ -58,6 +58,7 @@ export class LoadNodesAndCredentials {
|
||||
private readonly errorReporter: ErrorReporter,
|
||||
private readonly instanceSettings: InstanceSettings,
|
||||
private readonly globalConfig: GlobalConfig,
|
||||
private readonly moduleRegistry: ModuleRegistry,
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
@@ -98,6 +99,10 @@ export class LoadNodesAndCredentials {
|
||||
);
|
||||
}
|
||||
|
||||
for (const dir of this.moduleRegistry.loadDirs) {
|
||||
await this.loadNodesFromNodeModules(dir);
|
||||
}
|
||||
|
||||
await this.loadNodesFromCustomDirectories();
|
||||
await this.postProcessLoaders();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user