refactor(core): Use an IoC container to manage singleton classes [Part-1] (no-changelog) (#5509)

* add typedi

* convert ActiveWorkflowRunner into an injectable service

* convert ExternalHooks into an injectable service

* convert InternalHooks into an injectable service

* convert LoadNodesAndCredentials into an injectable service

* convert NodeTypes and CredentialTypes into an injectable service

* convert ActiveExecutions into an injectable service

* convert WaitTracker into an injectable service

* convert Push into an injectable service

* convert ActiveWebhooks and  TestWebhooks into an injectable services

* handle circular references, and log errors when a circular dependency is found
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2023-02-21 19:21:56 +01:00
committed by GitHub
parent aca94bb995
commit 52f740b9e8
79 changed files with 594 additions and 634 deletions

View File

@@ -8,6 +8,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { Container, Service } from 'typedi';
import { ActiveWorkflows, NodeExecuteFunctions } from 'n8n-core';
import type {
@@ -55,7 +57,7 @@ import config from '@/config';
import type { User } from '@db/entities/User';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import type { WebhookEntity } from '@db/entities/WebhookEntity';
import * as ActiveExecutions from '@/ActiveExecutions';
import { ActiveExecutions } from '@/ActiveExecutions';
import { createErrorExecution } from '@/GenericHelpers';
import { WORKFLOW_REACTIVATE_INITIAL_TIMEOUT, WORKFLOW_REACTIVATE_MAX_TIMEOUT } from '@/constants';
import { NodeTypes } from '@/NodeTypes';
@@ -68,8 +70,9 @@ import { START_NODES } from './constants';
const WEBHOOK_PROD_UNREGISTERED_HINT =
"The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)";
@Service()
export class ActiveWorkflowRunner {
private activeWorkflows: ActiveWorkflows | null = null;
private activeWorkflows = new ActiveWorkflows();
private activationErrors: {
[key: string]: IActivationError;
@@ -79,9 +82,7 @@ export class ActiveWorkflowRunner {
[key: string]: IQueuedWorkflowActivations;
} = {};
constructor() {
this.activeWorkflows = new ActiveWorkflows();
}
constructor(private externalHooks: ExternalHooks) {}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
async init() {
@@ -133,7 +134,7 @@ export class ActiveWorkflowRunner {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
Logger.info(` ${error.message}`);
Logger.error(
`Issue on intital workflow activation try "${workflowData.name}" (startup)`,
`Issue on initial workflow activation try "${workflowData.name}" (startup)`,
{
workflowName: workflowData.name,
workflowId: workflowData.id,
@@ -148,21 +149,18 @@ export class ActiveWorkflowRunner {
}
Logger.verbose('Finished initializing active workflows (startup)');
}
const externalHooks = ExternalHooks();
await externalHooks.run('activeWorkflows.initialized', []);
await this.externalHooks.run('activeWorkflows.initialized', []);
}
/**
* Removes all the currently active workflows
*
*/
async removeAll(): Promise<void> {
let activeWorkflowIds: string[] = [];
Logger.verbose('Call to remove all active workflows received (removeAll)');
if (this.activeWorkflows !== null) {
activeWorkflowIds.push.apply(activeWorkflowIds, this.activeWorkflows.allActiveWorkflows());
}
activeWorkflowIds.push.apply(activeWorkflowIds, this.activeWorkflows.allActiveWorkflows());
const activeWorkflows = await this.getActiveWorkflows();
activeWorkflowIds = [
@@ -183,7 +181,6 @@ export class ActiveWorkflowRunner {
/**
* Checks if a webhook for the given method and path exists and executes the workflow.
*
*/
async executeWebhook(
httpMethod: WebhookHttpMethod,
@@ -192,11 +189,6 @@ export class ActiveWorkflowRunner {
res: express.Response,
): Promise<IResponseCallbackData> {
Logger.debug(`Received webhook "${httpMethod}" for path "${path}"`);
if (this.activeWorkflows === null) {
throw new ResponseHelper.NotFoundError(
'The "activeWorkflows" instance did not get initialized yet.',
);
}
// Reset request parameters
req.params = {};
@@ -279,7 +271,7 @@ export class ActiveWorkflowRunner {
);
}
const nodeTypes = NodeTypes();
const nodeTypes = Container.get(NodeTypes);
const workflow = new Workflow({
id: webhook.workflowId,
name: workflowData.name,
@@ -482,6 +474,7 @@ export class ActiveWorkflowRunner {
try {
await this.removeWorkflowWebhooks(workflow.id as string);
} catch (error) {
ErrorReporter.error(error);
Logger.error(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`Could not remove webhooks of workflow "${workflow.id}" because of error: "${error.message}"`,
@@ -521,7 +514,7 @@ export class ActiveWorkflowRunner {
throw new Error(`Could not find workflow with id "${workflowId}"`);
}
const nodeTypes = NodeTypes();
const nodeTypes = Container.get(NodeTypes);
const workflow = new Workflow({
id: workflowId,
name: workflowData.name,
@@ -645,7 +638,7 @@ export class ActiveWorkflowRunner {
if (donePromise) {
executePromise.then((executionId) => {
ActiveExecutions.getInstance()
Container.get(ActiveExecutions)
.getPostExecutePromise(executionId)
.then(donePromise.resolve)
.catch(donePromise.reject);
@@ -702,7 +695,7 @@ export class ActiveWorkflowRunner {
if (donePromise) {
executePromise.then((executionId) => {
ActiveExecutions.getInstance()
Container.get(ActiveExecutions)
.getPostExecutePromise(executionId)
.then(donePromise.resolve)
.catch(donePromise.reject);
@@ -723,7 +716,7 @@ export class ActiveWorkflowRunner {
// Remove the workflow as "active"
await this.activeWorkflows?.remove(workflowData.id);
await this.activeWorkflows.remove(workflowData.id);
this.activationErrors[workflowData.id] = {
time: new Date().getTime(),
error: {
@@ -777,10 +770,6 @@ export class ActiveWorkflowRunner {
activation: WorkflowActivateMode,
workflowData?: IWorkflowDb,
): Promise<void> {
if (this.activeWorkflows === null) {
throw new Error('The "activeWorkflows" instance did not get initialized yet.');
}
let workflowInstance: Workflow;
try {
if (workflowData === undefined) {
@@ -793,7 +782,7 @@ export class ActiveWorkflowRunner {
if (!workflowData) {
throw new Error(`Could not find workflow with id "${workflowId}".`);
}
const nodeTypes = NodeTypes();
const nodeTypes = Container.get(NodeTypes);
workflowInstance = new Workflow({
id: workflowId,
name: workflowData.name,
@@ -978,47 +967,31 @@ export class ActiveWorkflowRunner {
*/
// TODO: this should happen in a transaction
async remove(workflowId: string): Promise<void> {
if (this.activeWorkflows !== null) {
// Remove all the webhooks of the workflow
try {
await this.removeWorkflowWebhooks(workflowId);
} catch (error) {
ErrorReporter.error(error);
Logger.error(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`Could not remove webhooks of workflow "${workflowId}" because of error: "${error.message}"`,
);
}
if (this.activationErrors[workflowId] !== undefined) {
// If there were any activation errors delete them
delete this.activationErrors[workflowId];
}
if (this.queuedWorkflowActivations[workflowId] !== undefined) {
this.removeQueuedWorkflowActivation(workflowId);
}
// if it's active in memory then it's a trigger
// so remove from list of actives workflows
if (this.activeWorkflows.isActive(workflowId)) {
await this.activeWorkflows.remove(workflowId);
Logger.verbose(`Successfully deactivated workflow "${workflowId}"`, { workflowId });
}
return;
// Remove all the webhooks of the workflow
try {
await this.removeWorkflowWebhooks(workflowId);
} catch (error) {
ErrorReporter.error(error);
Logger.error(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`Could not remove webhooks of workflow "${workflowId}" because of error: "${error.message}"`,
);
}
throw new Error('The "activeWorkflows" instance did not get initialized yet.');
if (this.activationErrors[workflowId] !== undefined) {
// If there were any activation errors delete them
delete this.activationErrors[workflowId];
}
if (this.queuedWorkflowActivations[workflowId] !== undefined) {
this.removeQueuedWorkflowActivation(workflowId);
}
// if it's active in memory then it's a trigger
// so remove from list of actives workflows
if (this.activeWorkflows.isActive(workflowId)) {
await this.activeWorkflows.remove(workflowId);
Logger.verbose(`Successfully deactivated workflow "${workflowId}"`, { workflowId });
}
}
}
let workflowRunnerInstance: ActiveWorkflowRunner | undefined;
export function getInstance(): ActiveWorkflowRunner {
if (workflowRunnerInstance === undefined) {
workflowRunnerInstance = new ActiveWorkflowRunner();
}
return workflowRunnerInstance;
}