refactor(core): Reorganize webhook related components under src/webhooks (no-changelog) (#10296)
Some checks are pending
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions

This commit is contained in:
Tomi Turtiainen 2024-08-07 11:23:44 +03:00 committed by GitHub
parent 2a8f1753e8
commit c8d322a9ba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 118 additions and 104 deletions

View file

@ -13,15 +13,15 @@ import { N8nInstanceType } from '@/Interfaces';
import { ExternalHooks } from '@/ExternalHooks';
import { send, sendErrorResponse } from '@/ResponseHelper';
import { rawBodyReader, bodyParser, corsMiddleware } from '@/middlewares';
import { TestWebhooks } from '@/TestWebhooks';
import { WaitingForms } from '@/WaitingForms';
import { WaitingWebhooks } from '@/WaitingWebhooks';
import { webhookRequestHandler } from '@/WebhookHelpers';
import { TestWebhooks } from '@/webhooks/TestWebhooks';
import { WaitingWebhooks } from '@/webhooks/WaitingWebhooks';
import { webhookRequestHandler } from '@/webhooks/WebhookHelpers';
import { ActiveWebhooks } from '@/webhooks/ActiveWebhooks';
import { generateHostInstanceId } from './databases/utils/generators';
import { Logger } from '@/Logger';
import { ServiceUnavailableError } from './errors/response-errors/service-unavailable.error';
import { OnShutdown } from '@/decorators/OnShutdown';
import { ActiveWebhooks } from '@/ActiveWebhooks';
import { GlobalConfig } from '@n8n/config';
@Service()

View file

@ -27,7 +27,7 @@ import {
} from 'n8n-workflow';
import type { IWorkflowDb } from '@/Interfaces';
import * as WebhookHelpers from '@/WebhookHelpers';
import * as WebhookHelpers from '@/webhooks/WebhookHelpers';
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
@ -40,7 +40,7 @@ import {
} from '@/constants';
import { NodeTypes } from '@/NodeTypes';
import { ExternalHooks } from '@/ExternalHooks';
import { WebhookService } from './services/webhook.service';
import { WebhookService } from '@/webhooks/webhook.service';
import { Logger } from './Logger';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { OrchestrationService } from '@/services/orchestration.service';

View file

@ -1,4 +1,4 @@
import type { Application, Request, Response } from 'express';
import type { Application } from 'express';
import type {
ExecutionError,
ICredentialDataDecryptedObject,
@ -22,7 +22,6 @@ import type {
FeatureFlags,
INodeProperties,
IUserSettings,
IHttpRequestMethods,
StartNodeData,
} from 'n8n-workflow';
@ -239,34 +238,6 @@ export interface IExternalHooksFunctions {
};
}
export type WebhookCORSRequest = Request & { method: 'OPTIONS' };
export type WebhookRequest = Request<{ path: string }> & {
method: IHttpRequestMethods;
params: Record<string, string>;
};
export type WaitingWebhookRequest = WebhookRequest & {
params: WebhookRequest['path'] & { suffix?: string };
};
export interface WebhookAccessControlOptions {
allowedOrigins?: string;
}
export interface IWebhookManager {
/** Gets all request methods associated with a webhook path*/
getWebhookMethods?: (path: string) => Promise<IHttpRequestMethods[]>;
/** Find the CORS options matching a path and method */
findAccessControlOptions?: (
path: string,
httpMethod: IHttpRequestMethods,
) => Promise<WebhookAccessControlOptions | undefined>;
executeWebhook(req: WebhookRequest, res: Response): Promise<IResponseCallbackData>;
}
export interface IVersionNotificationSettings {
enabled: boolean;
endpoint: string;
@ -466,13 +437,6 @@ export interface IPushDataWorkerStatusPayload {
version: string;
}
export interface IResponseCallbackData {
data?: IDataObject | IDataObject[];
headers?: object;
noWebhookResponse?: boolean;
responseCode?: number;
}
export interface INodesTypeData {
[key: string]: {
className: string;

View file

@ -1,7 +1,7 @@
import { Service } from 'typedi';
import type { IExecutionResponse } from '@/Interfaces';
import { WaitingWebhooks } from '@/WaitingWebhooks';
import { WaitingWebhooks } from '@/webhooks/WaitingWebhooks';
@Service()
export class WaitingForms extends WaitingWebhooks {

View file

@ -4,7 +4,7 @@ import { ApplicationError } from 'n8n-workflow';
import config from '@/config';
import { ActiveExecutions } from '@/ActiveExecutions';
import { WebhookServer } from '@/WebhookServer';
import { WebhookServer } from '@/webhooks/WebhookServer';
import { Queue } from '@/Queue';
import { BaseCommand } from './BaseCommand';
@ -86,7 +86,7 @@ export class Webhook extends BaseCommand {
await this.initExternalHooks();
this.logger.debug('External hooks init complete');
await this.initExternalSecrets();
this.logger.debug('External seecrets init complete');
this.logger.debug('External secrets init complete');
}
async run() {

View file

@ -9,7 +9,7 @@ import { Workflow, sleep, ApplicationError } from 'n8n-workflow';
import * as Db from '@/Db';
import * as ResponseHelper from '@/ResponseHelper';
import * as WebhookHelpers from '@/WebhookHelpers';
import * as WebhookHelpers from '@/webhooks/WebhookHelpers';
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
import config from '@/config';
import type { Job, JobId, JobResponse, WebhookResponse } from '@/Queue';

View file

@ -7,7 +7,7 @@ import { License } from '@/License';
import { Logger } from '@/Logger';
import { ActiveWorkflowManager } from '@/ActiveWorkflowManager';
import { Push } from '@/push';
import { TestWebhooks } from '@/TestWebhooks';
import { TestWebhooks } from '@/webhooks/TestWebhooks';
import { OrchestrationService } from '@/services/orchestration.service';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { CommunityPackagesService } from '@/services/communityPackages.service';

View file

@ -5,20 +5,25 @@ import type { INode, IWebhookData, IHttpRequestMethods } from 'n8n-workflow';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import type {
IResponseCallbackData,
IWebhookResponseCallbackData,
IWebhookManager,
WebhookAccessControlOptions,
WebhookRequest,
} from '@/Interfaces';
} from './webhook.types';
import { Logger } from '@/Logger';
import { NodeTypes } from '@/NodeTypes';
import { WebhookService } from '@/services/webhook.service';
import { WebhookService } from '@/webhooks/webhook.service';
import { WebhookNotFoundError } from '@/errors/response-errors/webhook-not-found.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
import * as WebhookHelpers from '@/WebhookHelpers';
import * as WebhookHelpers from '@/webhooks/WebhookHelpers';
import { WorkflowStaticDataService } from '@/workflows/workflowStaticData.service';
/**
* Service for handling the execution of production webhooks, i.e. webhooks
* that belong to activated workflows and use the production URL
* (https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/#webhook-urls)
*/
@Service()
export class ActiveWebhooks implements IWebhookManager {
constructor(
@ -57,7 +62,7 @@ export class ActiveWebhooks implements IWebhookManager {
async executeWebhook(
request: WebhookRequest,
response: Response,
): Promise<IResponseCallbackData> {
): Promise<IWebhookResponseCallbackData> {
const httpMethod = request.method;
const path = request.params.path;

View file

@ -8,26 +8,30 @@ import type {
IRunData,
} from 'n8n-workflow';
import type {
IResponseCallbackData,
IWebhookResponseCallbackData,
IWebhookManager,
IWorkflowDb,
WebhookAccessControlOptions,
WebhookRequest,
} from '@/Interfaces';
} from './webhook.types';
import { Push } from '@/push';
import { NodeTypes } from '@/NodeTypes';
import * as WebhookHelpers from '@/WebhookHelpers';
import * as WebhookHelpers from '@/webhooks/WebhookHelpers';
import { TEST_WEBHOOK_TIMEOUT } from '@/constants';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { WorkflowMissingIdError } from '@/errors/workflow-missing-id.error';
import { WebhookNotFoundError } from '@/errors/response-errors/webhook-not-found.error';
import * as NodeExecuteFunctions from 'n8n-core';
import { removeTrailingSlash } from './utils';
import type { TestWebhookRegistration } from '@/services/test-webhook-registrations.service';
import { TestWebhookRegistrationsService } from '@/services/test-webhook-registrations.service';
import { removeTrailingSlash } from '@/utils';
import type { TestWebhookRegistration } from '@/webhooks/test-webhook-registrations.service';
import { TestWebhookRegistrationsService } from '@/webhooks/test-webhook-registrations.service';
import { OrchestrationService } from '@/services/orchestration.service';
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
import type { IWorkflowDb } from '@/Interfaces';
/**
* Service for handling the execution of webhooks of manual executions
* that use the [Test URL](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/#webhook-urls).
*/
@Service()
export class TestWebhooks implements IWebhookManager {
constructor(
@ -46,7 +50,7 @@ export class TestWebhooks implements IWebhookManager {
async executeWebhook(
request: WebhookRequest,
response: express.Response,
): Promise<IResponseCallbackData> {
): Promise<IWebhookResponseCallbackData> {
const httpMethod = request.method;
let path = removeTrailingSlash(request.params.path);
@ -117,7 +121,7 @@ export class TestWebhooks implements IWebhookManager {
undefined, // executionId
request,
response,
(error: Error | null, data: IResponseCallbackData) => {
(error: Error | null, data: IWebhookResponseCallbackData) => {
if (error !== null) reject(error);
else resolve(data);
},

View file

@ -2,21 +2,25 @@ import { NodeHelpers, Workflow } from 'n8n-workflow';
import { Service } from 'typedi';
import type express from 'express';
import * as WebhookHelpers from '@/WebhookHelpers';
import * as WebhookHelpers from '@/webhooks/WebhookHelpers';
import { NodeTypes } from '@/NodeTypes';
import type {
IExecutionResponse,
IResponseCallbackData,
IWebhookResponseCallbackData,
IWebhookManager,
IWorkflowDb,
WaitingWebhookRequest,
} from '@/Interfaces';
} from './webhook.types';
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
import { ExecutionRepository } from '@db/repositories/execution.repository';
import { Logger } from '@/Logger';
import { ConflictError } from './errors/response-errors/conflict.error';
import { NotFoundError } from './errors/response-errors/not-found.error';
import { ConflictError } from '@/errors/response-errors/conflict.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import type { IExecutionResponse, IWorkflowDb } from '@/Interfaces';
/**
* Service for handling the execution of webhooks of Wait nodes that use the
* [Resume On Webhook Call](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.wait/#on-webhook-call)
* feature.
*/
@Service()
export class WaitingWebhooks implements IWebhookManager {
protected includeForms = false;
@ -40,7 +44,7 @@ export class WaitingWebhooks implements IWebhookManager {
async executeWebhook(
req: WaitingWebhookRequest,
res: express.Response,
): Promise<IResponseCallbackData> {
): Promise<IWebhookResponseCallbackData> {
const { path: executionId, suffix } = req.params;
this.logReceivedWebhook(req.method, executionId);

View file

@ -43,27 +43,25 @@ import {
} from 'n8n-workflow';
import type {
IExecutionDb,
IResponseCallbackData,
IWebhookResponseCallbackData,
IWebhookManager,
IWorkflowDb,
IWorkflowExecutionDataProcess,
WebhookCORSRequest,
WebhookRequest,
} from '@/Interfaces';
} from './webhook.types';
import * as ResponseHelper from '@/ResponseHelper';
import * as WorkflowHelpers from '@/WorkflowHelpers';
import { WorkflowRunner } from '@/WorkflowRunner';
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
import { ActiveExecutions } from '@/ActiveExecutions';
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
import { OwnershipService } from './services/ownership.service';
import { parseBody } from './middlewares';
import { Logger } from './Logger';
import { NotFoundError } from './errors/response-errors/not-found.error';
import { InternalServerError } from './errors/response-errors/internal-server.error';
import { UnprocessableRequestError } from './errors/response-errors/unprocessable.error';
import type { Project } from './databases/entities/Project';
import { OwnershipService } from '@/services/ownership.service';
import { parseBody } from '@/middlewares';
import { Logger } from '@/Logger';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error';
import type { Project } from '@/databases/entities/Project';
import type { IExecutionDb, IWorkflowDb, IWorkflowExecutionDataProcess } from '@/Interfaces';
export const WEBHOOK_METHODS: IHttpRequestMethods[] = [
'DELETE',
@ -137,7 +135,7 @@ export const webhookRequestHandler =
return ResponseHelper.sendSuccessResponse(res, {}, true, 204);
}
let response;
let response: IWebhookResponseCallbackData;
try {
response = await webhookManager.executeWebhook(req, res);
} catch (error) {
@ -228,7 +226,7 @@ export async function executeWebhook(
executionId: string | undefined,
req: WebhookRequest,
res: express.Response,
responseCallback: (error: Error | null, data: IResponseCallbackData) => void,
responseCallback: (error: Error | null, data: IWebhookResponseCallbackData) => void,
destinationNode?: string,
): Promise<string | undefined> {
// Get the nodeType to know which responseMode is set

View file

@ -1,20 +1,21 @@
import { mock } from 'jest-mock-extended';
import { TestWebhooks } from '@/TestWebhooks';
import { TestWebhooks } from '@/webhooks/TestWebhooks';
import { WebhookNotFoundError } from '@/errors/response-errors/webhook-not-found.error';
import { v4 as uuid } from 'uuid';
import { generateNanoId } from '@/databases/utils/generators';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import * as WebhookHelpers from '@/WebhookHelpers';
import * as WebhookHelpers from '@/webhooks/WebhookHelpers';
import type * as express from 'express';
import type { IWorkflowDb, WebhookRequest } from '@/Interfaces';
import type { IWorkflowDb } from '@/Interfaces';
import type { IWebhookData, IWorkflowExecuteAdditionalData, Workflow } from 'n8n-workflow';
import type {
TestWebhookRegistrationsService,
TestWebhookRegistration,
} from '@/services/test-webhook-registrations.service';
} from '@/webhooks/test-webhook-registrations.service';
import * as AdditionalData from '@/WorkflowExecuteAdditionalData';
import type { WebhookRequest } from '@/webhooks/webhook.types';
jest.mock('@/WorkflowExecuteAdditionalData');

View file

@ -3,8 +3,8 @@ import { mock } from 'jest-mock-extended';
import { randomString } from 'n8n-workflow';
import type { IHttpRequestMethods } from 'n8n-workflow';
import type { IWebhookManager, WebhookCORSRequest, WebhookRequest } from '@/Interfaces';
import { webhookRequestHandler } from '@/WebhookHelpers';
import type { IWebhookManager, WebhookCORSRequest, WebhookRequest } from '@/webhooks/webhook.types';
import { webhookRequestHandler } from '@/webhooks/WebhookHelpers';
describe('WebhookHelpers', () => {
describe('webhookRequestHandler', () => {

View file

@ -1,7 +1,7 @@
import type { CacheService } from '@/services/cache/cache.service';
import type { OrchestrationService } from '@/services/orchestration.service';
import type { TestWebhookRegistration } from '@/services/test-webhook-registrations.service';
import { TestWebhookRegistrationsService } from '@/services/test-webhook-registrations.service';
import type { TestWebhookRegistration } from '@/webhooks/test-webhook-registrations.service';
import { TestWebhookRegistrationsService } from '@/webhooks/test-webhook-registrations.service';
import { mock } from 'jest-mock-extended';
describe('TestWebhookRegistrationsService', () => {

View file

@ -1,10 +1,11 @@
import { mock } from 'jest-mock-extended';
import { WaitingWebhooks } from '@/WaitingWebhooks';
import { WaitingWebhooks } from '@/webhooks/WaitingWebhooks';
import { ConflictError } from '@/errors/response-errors/conflict.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import type { IExecutionResponse, WaitingWebhookRequest } from '@/Interfaces';
import type { IExecutionResponse } from '@/Interfaces';
import type express from 'express';
import type { ExecutionRepository } from '@/databases/repositories/execution.repository';
import type { WaitingWebhookRequest } from '@/webhooks/webhook.types';
describe('WaitingWebhooks', () => {
const executionRepository = mock<ExecutionRepository>();

View file

@ -2,7 +2,7 @@ import { v4 as uuid } from 'uuid';
import config from '@/config';
import { WebhookRepository } from '@db/repositories/webhook.repository';
import { CacheService } from '@/services/cache/cache.service';
import { WebhookService } from '@/services/webhook.service';
import { WebhookService } from '@/webhooks/webhook.service';
import { WebhookEntity } from '@db/entities/WebhookEntity';
import { mockInstance } from '@test/mocking';

View file

@ -3,7 +3,7 @@ import { CacheService } from '@/services/cache/cache.service';
import type { IWebhookData } from 'n8n-workflow';
import type { IWorkflowDb } from '@/Interfaces';
import { TEST_WEBHOOK_TIMEOUT, TEST_WEBHOOK_TIMEOUT_BUFFER } from '@/constants';
import { OrchestrationService } from './orchestration.service';
import { OrchestrationService } from '@/services/orchestration.service';
export type TestWebhookRegistration = {
pushRef?: string;

View file

@ -0,0 +1,37 @@
import type { Request, Response } from 'express';
import type { IDataObject, IHttpRequestMethods } from 'n8n-workflow';
export type WebhookCORSRequest = Request & { method: 'OPTIONS' };
export type WebhookRequest = Request<{ path: string }> & {
method: IHttpRequestMethods;
params: Record<string, string>;
};
export type WaitingWebhookRequest = WebhookRequest & {
params: WebhookRequest['path'] & { suffix?: string };
};
export interface WebhookAccessControlOptions {
allowedOrigins?: string;
}
export interface IWebhookManager {
/** Gets all request methods associated with a webhook path*/
getWebhookMethods?: (path: string) => Promise<IHttpRequestMethods[]>;
/** Find the CORS options matching a path and method */
findAccessControlOptions?: (
path: string,
httpMethod: IHttpRequestMethods,
) => Promise<WebhookAccessControlOptions | undefined>;
executeWebhook(req: WebhookRequest, res: Response): Promise<IWebhookResponseCallbackData>;
}
export interface IWebhookResponseCallbackData {
data?: IDataObject | IDataObject[];
headers?: object;
noWebhookResponse?: boolean;
responseCode?: number;
}

View file

@ -30,7 +30,7 @@ import type {
import { NodeTypes } from '@/NodeTypes';
import { WorkflowRunner } from '@/WorkflowRunner';
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
import { TestWebhooks } from '@/TestWebhooks';
import { TestWebhooks } from '@/webhooks/TestWebhooks';
import { Logger } from '@/Logger';
import type { Project } from '@/databases/entities/Project';
import { GlobalConfig } from '@n8n/config';

View file

@ -8,8 +8,8 @@ import { ActiveWorkflowManager } from '@/ActiveWorkflowManager';
import { ExternalHooks } from '@/ExternalHooks';
import { Push } from '@/push';
import { SecretsHelper } from '@/SecretsHelpers';
import { WebhookService } from '@/services/webhook.service';
import * as WebhookHelpers from '@/WebhookHelpers';
import { WebhookService } from '@/webhooks/webhook.service';
import * as WebhookHelpers from '@/webhooks/WebhookHelpers';
import * as AdditionalData from '@/WorkflowExecuteAdditionalData';
import type { WebhookEntity } from '@db/entities/WebhookEntity';
import { NodeTypes } from '@/NodeTypes';

View file

@ -3,13 +3,13 @@ import { agent as testAgent } from 'supertest';
import { mock } from 'jest-mock-extended';
import { AbstractServer } from '@/AbstractServer';
import { ActiveWebhooks } from '@/ActiveWebhooks';
import { ActiveWebhooks } from '@/webhooks/ActiveWebhooks';
import { ExternalHooks } from '@/ExternalHooks';
import { InternalHooks } from '@/InternalHooks';
import { TestWebhooks } from '@/TestWebhooks';
import { WaitingWebhooks } from '@/WaitingWebhooks';
import { TestWebhooks } from '@/webhooks/TestWebhooks';
import { WaitingWebhooks } from '@/webhooks/WaitingWebhooks';
import { WaitingForms } from '@/WaitingForms';
import type { IResponseCallbackData } from '@/Interfaces';
import type { IWebhookResponseCallbackData } from '@/webhooks/webhook.types';
import { mockInstance } from '@test/mocking';
import { GlobalConfig } from '@n8n/config';
@ -80,7 +80,7 @@ describe('WebhookServer', () => {
}
const mockResponse = (data = {}, headers = {}, status = 200) => {
const response = mock<IResponseCallbackData>();
const response = mock<IWebhookResponseCallbackData>();
response.responseCode = status;
response.data = data;
response.headers = headers;