mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -08:00
🔀 Merge master
This commit is contained in:
commit
61bb8de352
|
@ -12,7 +12,7 @@ import * as PCancelable from 'p-cancelable';
|
|||
import { Command, flags } from '@oclif/command';
|
||||
import { UserSettings, WorkflowExecute } from 'n8n-core';
|
||||
|
||||
import { INodeTypes, IRun, Workflow, LoggerProxy } from 'n8n-workflow';
|
||||
import { IExecuteResponsePromiseData, INodeTypes, IRun, Workflow, LoggerProxy } from 'n8n-workflow';
|
||||
|
||||
import { FindOneOptions } from 'typeorm';
|
||||
|
||||
|
@ -25,11 +25,13 @@ import {
|
|||
GenericHelpers,
|
||||
IBullJobData,
|
||||
IBullJobResponse,
|
||||
IBullWebhookResponse,
|
||||
IExecutionFlattedDb,
|
||||
InternalHooksManager,
|
||||
LoadNodesAndCredentials,
|
||||
NodeTypes,
|
||||
ResponseHelper,
|
||||
WebhookHelpers,
|
||||
WorkflowExecuteAdditionalData,
|
||||
} from '../src';
|
||||
|
||||
|
@ -172,6 +174,16 @@ export class Worker extends Command {
|
|||
currentExecutionDb.workflowData,
|
||||
{ retryOf: currentExecutionDb.retryOf as string },
|
||||
);
|
||||
|
||||
additionalData.hooks.hookFunctions.sendResponse = [
|
||||
async (response: IExecuteResponsePromiseData): Promise<void> => {
|
||||
await job.progress({
|
||||
executionId: job.data.executionId as string,
|
||||
response: WebhookHelpers.encodeWebhookResponse(response),
|
||||
} as IBullWebhookResponse);
|
||||
},
|
||||
];
|
||||
|
||||
additionalData.executionId = jobData.executionId;
|
||||
|
||||
let workflowExecute: WorkflowExecute;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n",
|
||||
"version": "0.147.1",
|
||||
"version": "0.148.0",
|
||||
"description": "n8n Workflow Automation Tool",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -110,10 +110,10 @@
|
|||
"localtunnel": "^2.0.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"mysql2": "~2.3.0",
|
||||
"n8n-core": "~0.91.0",
|
||||
"n8n-editor-ui": "~0.114.0",
|
||||
"n8n-nodes-base": "~0.144.1",
|
||||
"n8n-workflow": "~0.74.0",
|
||||
"n8n-core": "~0.92.0",
|
||||
"n8n-editor-ui": "~0.115.0",
|
||||
"n8n-nodes-base": "~0.145.0",
|
||||
"n8n-workflow": "~0.75.0",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
"open": "^7.0.0",
|
||||
"pg": "^8.3.0",
|
||||
|
|
|
@ -5,9 +5,12 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import { IRun } from 'n8n-workflow';
|
||||
|
||||
import { createDeferredPromise } from 'n8n-core';
|
||||
import {
|
||||
createDeferredPromise,
|
||||
IDeferredPromise,
|
||||
IExecuteResponsePromiseData,
|
||||
IRun,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { ChildProcess } from 'child_process';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
|
@ -116,6 +119,28 @@ export class ActiveExecutions {
|
|||
this.activeExecutions[executionId].workflowExecution = workflowExecution;
|
||||
}
|
||||
|
||||
attachResponsePromise(
|
||||
executionId: string,
|
||||
responsePromise: IDeferredPromise<IExecuteResponsePromiseData>,
|
||||
): void {
|
||||
if (this.activeExecutions[executionId] === undefined) {
|
||||
throw new Error(
|
||||
`No active execution with id "${executionId}" got found to attach to workflowExecution to!`,
|
||||
);
|
||||
}
|
||||
|
||||
this.activeExecutions[executionId].responsePromise = responsePromise;
|
||||
}
|
||||
|
||||
resolveResponsePromise(executionId: string, response: IExecuteResponsePromiseData): void {
|
||||
if (this.activeExecutions[executionId] === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
this.activeExecutions[executionId].responsePromise?.resolve(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an active execution
|
||||
*
|
||||
|
@ -193,6 +218,7 @@ export class ActiveExecutions {
|
|||
|
||||
this.activeExecutions[executionId].postExecutePromises.push(waitPromise);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
||||
return waitPromise.promise();
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,9 @@
|
|||
import { ActiveWorkflows, NodeExecuteFunctions } from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDeferredPromise,
|
||||
IExecuteData,
|
||||
IExecuteResponsePromiseData,
|
||||
IGetExecutePollFunctions,
|
||||
IGetExecuteTriggerFunctions,
|
||||
INode,
|
||||
|
@ -40,8 +42,6 @@ import {
|
|||
NodeTypes,
|
||||
ResponseHelper,
|
||||
WebhookHelpers,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
WorkflowCredentials,
|
||||
WorkflowExecuteAdditionalData,
|
||||
WorkflowHelpers,
|
||||
WorkflowRunner,
|
||||
|
@ -550,6 +550,7 @@ export class ActiveWorkflowRunner {
|
|||
data: INodeExecutionData[][],
|
||||
additionalData: IWorkflowExecuteAdditionalDataWorkflow,
|
||||
mode: WorkflowExecuteMode,
|
||||
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
|
||||
) {
|
||||
const nodeExecutionStack: IExecuteData[] = [
|
||||
{
|
||||
|
@ -580,7 +581,7 @@ export class ActiveWorkflowRunner {
|
|||
};
|
||||
|
||||
const workflowRunner = new WorkflowRunner();
|
||||
return workflowRunner.run(runData, true);
|
||||
return workflowRunner.run(runData, true, undefined, undefined, responsePromise);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -641,13 +642,16 @@ export class ActiveWorkflowRunner {
|
|||
mode,
|
||||
activation,
|
||||
);
|
||||
returnFunctions.emit = (data: INodeExecutionData[][]): void => {
|
||||
returnFunctions.emit = (
|
||||
data: INodeExecutionData[][],
|
||||
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
|
||||
): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
Logger.debug(`Received trigger for workflow "${workflow.name}"`);
|
||||
WorkflowHelpers.saveStaticData(workflow);
|
||||
// eslint-disable-next-line id-denylist
|
||||
this.runWorkflow(workflowData, node, data, additionalData, mode).catch((err) =>
|
||||
console.error(err),
|
||||
this.runWorkflow(workflowData, node, data, additionalData, mode, responsePromise).catch(
|
||||
(error) => console.error(error),
|
||||
);
|
||||
};
|
||||
return returnFunctions;
|
||||
|
|
|
@ -7,19 +7,19 @@ import {
|
|||
ICredentialsEncrypted,
|
||||
ICredentialType,
|
||||
IDataObject,
|
||||
IDeferredPromise,
|
||||
IExecuteResponsePromiseData,
|
||||
IRun,
|
||||
IRunData,
|
||||
IRunExecutionData,
|
||||
ITaskData,
|
||||
ITelemetrySettings,
|
||||
IWorkflowBase as IWorkflowBaseWorkflow,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
IWorkflowCredentials,
|
||||
Workflow,
|
||||
WorkflowExecuteMode,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { IDeferredPromise, WorkflowExecute } from 'n8n-core';
|
||||
import { WorkflowExecute } from 'n8n-core';
|
||||
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import * as PCancelable from 'p-cancelable';
|
||||
|
@ -47,6 +47,11 @@ export interface IBullJobResponse {
|
|||
success: boolean;
|
||||
}
|
||||
|
||||
export interface IBullWebhookResponse {
|
||||
executionId: string;
|
||||
response: IExecuteResponsePromiseData;
|
||||
}
|
||||
|
||||
export interface ICustomRequest extends Request {
|
||||
parsedUrl: Url | undefined;
|
||||
}
|
||||
|
@ -237,6 +242,7 @@ export interface IExecutingWorkflowData {
|
|||
process?: ChildProcess;
|
||||
startedAt: Date;
|
||||
postExecutePromises: Array<IDeferredPromise<IRun | undefined>>;
|
||||
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>;
|
||||
workflowExecution?: PCancelable<IRun>;
|
||||
}
|
||||
|
||||
|
@ -491,6 +497,7 @@ export interface IPushDataConsoleMessage {
|
|||
|
||||
export interface IResponseCallbackData {
|
||||
data?: IDataObject | IDataObject[];
|
||||
headers?: object;
|
||||
noWebhookResponse?: boolean;
|
||||
responseCode?: number;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,9 @@ class NodeTypesClass implements INodeTypes {
|
|||
}
|
||||
|
||||
getByNameAndVersion(nodeType: string, version?: number): INodeType {
|
||||
if (this.nodeTypes[nodeType] === undefined) {
|
||||
throw new Error(`The node-type "${nodeType}" is not known!`);
|
||||
}
|
||||
return NodeHelpers.getVersionedTypeNode(this.nodeTypes[nodeType].type, version);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import * as Bull from 'bull';
|
||||
import * as config from '../config';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import { IBullJobData } from './Interfaces';
|
||||
import { IBullJobData, IBullWebhookResponse } from './Interfaces';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import * as ActiveExecutions from './ActiveExecutions';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import * as WebhookHelpers from './WebhookHelpers';
|
||||
|
||||
export class Queue {
|
||||
private activeExecutions: ActiveExecutions.ActiveExecutions;
|
||||
|
||||
private jobQueue: Bull.Queue;
|
||||
|
||||
constructor() {
|
||||
this.activeExecutions = ActiveExecutions.getInstance();
|
||||
|
||||
const prefix = config.get('queue.bull.prefix') as string;
|
||||
const redisOptions = config.get('queue.bull.redis') as object;
|
||||
// Disabling ready check is necessary as it allows worker to
|
||||
|
@ -16,6 +25,14 @@ export class Queue {
|
|||
// More here: https://github.com/OptimalBits/bull/issues/890
|
||||
// @ts-ignore
|
||||
this.jobQueue = new Bull('jobs', { prefix, redis: redisOptions, enableReadyCheck: false });
|
||||
|
||||
this.jobQueue.on('global:progress', (jobId, progress: IBullWebhookResponse) => {
|
||||
this.activeExecutions.resolveResponsePromise(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
progress.executionId,
|
||||
WebhookHelpers.decodeWebhookResponse(progress.response),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async add(jobData: IBullJobData, jobOptions: object): Promise<Bull.Job> {
|
||||
|
|
|
@ -72,11 +72,16 @@ export function sendSuccessResponse(
|
|||
data: any,
|
||||
raw?: boolean,
|
||||
responseCode?: number,
|
||||
responseHeader?: object,
|
||||
) {
|
||||
if (responseCode !== undefined) {
|
||||
res.status(responseCode);
|
||||
}
|
||||
|
||||
if (responseHeader) {
|
||||
res.header(responseHeader);
|
||||
}
|
||||
|
||||
if (raw === true) {
|
||||
if (typeof data === 'string') {
|
||||
res.send(data);
|
||||
|
|
|
@ -680,6 +680,7 @@ class App {
|
|||
|
||||
// @ts-ignore
|
||||
savedWorkflow.id = savedWorkflow.id.toString();
|
||||
await this.externalHooks.run('workflow.afterCreate', [savedWorkflow]);
|
||||
void InternalHooksManager.getInstance().onWorkflowCreated(newWorkflow as IWorkflowBase);
|
||||
return savedWorkflow;
|
||||
},
|
||||
|
@ -2669,7 +2670,13 @@ class App {
|
|||
return;
|
||||
}
|
||||
|
||||
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
|
||||
ResponseHelper.sendSuccessResponse(
|
||||
res,
|
||||
response.data,
|
||||
true,
|
||||
response.responseCode,
|
||||
response.headers,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -2720,7 +2727,13 @@ class App {
|
|||
return;
|
||||
}
|
||||
|
||||
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
|
||||
ResponseHelper.sendSuccessResponse(
|
||||
res,
|
||||
response.data,
|
||||
true,
|
||||
response.responseCode,
|
||||
response.headers,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -2746,7 +2759,13 @@ class App {
|
|||
return;
|
||||
}
|
||||
|
||||
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
|
||||
ResponseHelper.sendSuccessResponse(
|
||||
res,
|
||||
response.data,
|
||||
true,
|
||||
response.responseCode,
|
||||
response.headers,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
||||
/* eslint-disable @typescript-eslint/no-shadow */
|
||||
|
@ -18,9 +19,13 @@ import { get } from 'lodash';
|
|||
import { BINARY_ENCODING, NodeExecuteFunctions } from 'n8n-core';
|
||||
|
||||
import {
|
||||
createDeferredPromise,
|
||||
IBinaryKeyData,
|
||||
IDataObject,
|
||||
IDeferredPromise,
|
||||
IExecuteData,
|
||||
IExecuteResponsePromiseData,
|
||||
IN8nHttpFullResponse,
|
||||
INode,
|
||||
IRunExecutionData,
|
||||
IWebhookData,
|
||||
|
@ -34,20 +39,20 @@ import {
|
|||
} from 'n8n-workflow';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import {
|
||||
ActiveExecutions,
|
||||
GenericHelpers,
|
||||
IExecutionDb,
|
||||
IResponseCallbackData,
|
||||
IWorkflowDb,
|
||||
IWorkflowExecutionDataProcess,
|
||||
ResponseHelper,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
WorkflowCredentials,
|
||||
WorkflowExecuteAdditionalData,
|
||||
WorkflowHelpers,
|
||||
WorkflowRunner,
|
||||
} from '.';
|
||||
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import * as ActiveExecutions from './ActiveExecutions';
|
||||
|
||||
const activeExecutions = ActiveExecutions.getInstance();
|
||||
|
||||
/**
|
||||
|
@ -91,6 +96,35 @@ export function getWorkflowWebhooks(
|
|||
return returnData;
|
||||
}
|
||||
|
||||
export function decodeWebhookResponse(
|
||||
response: IExecuteResponsePromiseData,
|
||||
): IExecuteResponsePromiseData {
|
||||
if (
|
||||
typeof response === 'object' &&
|
||||
typeof response.body === 'object' &&
|
||||
(response.body as IDataObject)['__@N8nEncodedBuffer@__']
|
||||
) {
|
||||
response.body = Buffer.from(
|
||||
(response.body as IDataObject)['__@N8nEncodedBuffer@__'] as string,
|
||||
BINARY_ENCODING,
|
||||
);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export function encodeWebhookResponse(
|
||||
response: IExecuteResponsePromiseData,
|
||||
): IExecuteResponsePromiseData {
|
||||
if (typeof response === 'object' && Buffer.isBuffer(response.body)) {
|
||||
response.body = {
|
||||
'__@N8nEncodedBuffer@__': response.body.toString(BINARY_ENCODING),
|
||||
};
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the webhooks which should be created for the give workflow
|
||||
*
|
||||
|
@ -169,7 +203,7 @@ export async function executeWebhook(
|
|||
200,
|
||||
) as number;
|
||||
|
||||
if (!['onReceived', 'lastNode'].includes(responseMode as string)) {
|
||||
if (!['onReceived', 'lastNode', 'responseNode'].includes(responseMode as string)) {
|
||||
// If the mode is not known we error. Is probably best like that instead of using
|
||||
// the default that people know as early as possible (probably already testing phase)
|
||||
// that something does not resolve properly.
|
||||
|
@ -356,9 +390,52 @@ export async function executeWebhook(
|
|||
workflowData,
|
||||
};
|
||||
|
||||
let responsePromise: IDeferredPromise<IN8nHttpFullResponse> | undefined;
|
||||
if (responseMode === 'responseNode') {
|
||||
responsePromise = await createDeferredPromise<IN8nHttpFullResponse>();
|
||||
responsePromise
|
||||
.promise()
|
||||
.then((response: IN8nHttpFullResponse) => {
|
||||
if (didSendResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Buffer.isBuffer(response.body)) {
|
||||
res.header(response.headers);
|
||||
res.end(response.body);
|
||||
|
||||
responseCallback(null, {
|
||||
noWebhookResponse: true,
|
||||
});
|
||||
} else {
|
||||
// TODO: This probably needs some more changes depending on the options on the
|
||||
// Webhook Response node
|
||||
responseCallback(null, {
|
||||
data: response.body as IDataObject,
|
||||
headers: response.headers,
|
||||
responseCode: response.statusCode,
|
||||
});
|
||||
}
|
||||
|
||||
didSendResponse = true;
|
||||
})
|
||||
.catch(async (error) => {
|
||||
Logger.error(
|
||||
`Error with Webhook-Response for execution "${executionId}": "${error.message}"`,
|
||||
{ executionId, workflowId: workflow.id },
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Start now to run the workflow
|
||||
const workflowRunner = new WorkflowRunner();
|
||||
executionId = await workflowRunner.run(runData, true, !didSendResponse, executionId);
|
||||
executionId = await workflowRunner.run(
|
||||
runData,
|
||||
true,
|
||||
!didSendResponse,
|
||||
executionId,
|
||||
responsePromise,
|
||||
);
|
||||
|
||||
Logger.verbose(
|
||||
`Started execution of workflow "${workflow.name}" from webhook with execution ID ${executionId}`,
|
||||
|
@ -398,6 +475,20 @@ export async function executeWebhook(
|
|||
return data;
|
||||
}
|
||||
|
||||
if (responseMode === 'responseNode') {
|
||||
if (!didSendResponse) {
|
||||
// Return an error if no Webhook-Response node did send any data
|
||||
responseCallback(null, {
|
||||
data: {
|
||||
message: 'Workflow executed sucessfully.',
|
||||
},
|
||||
responseCode,
|
||||
});
|
||||
didSendResponse = true;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (returnData === undefined) {
|
||||
if (!didSendResponse) {
|
||||
responseCallback(null, {
|
||||
|
|
|
@ -64,7 +64,13 @@ export function registerProductionWebhooks() {
|
|||
return;
|
||||
}
|
||||
|
||||
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
|
||||
ResponseHelper.sendSuccessResponse(
|
||||
res,
|
||||
response.data,
|
||||
true,
|
||||
response.responseCode,
|
||||
response.headers,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -115,7 +121,13 @@ export function registerProductionWebhooks() {
|
|||
return;
|
||||
}
|
||||
|
||||
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
|
||||
ResponseHelper.sendSuccessResponse(
|
||||
res,
|
||||
response.data,
|
||||
true,
|
||||
response.responseCode,
|
||||
response.headers,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -141,7 +153,13 @@ export function registerProductionWebhooks() {
|
|||
return;
|
||||
}
|
||||
|
||||
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
|
||||
ResponseHelper.sendSuccessResponse(
|
||||
res,
|
||||
response.data,
|
||||
true,
|
||||
response.responseCode,
|
||||
response.headers,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -173,7 +191,13 @@ export function registerProductionWebhooks() {
|
|||
return;
|
||||
}
|
||||
|
||||
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
|
||||
ResponseHelper.sendSuccessResponse(
|
||||
res,
|
||||
response.data,
|
||||
true,
|
||||
response.responseCode,
|
||||
response.headers,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -199,7 +223,13 @@ export function registerProductionWebhooks() {
|
|||
return;
|
||||
}
|
||||
|
||||
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
|
||||
ResponseHelper.sendSuccessResponse(
|
||||
res,
|
||||
response.data,
|
||||
true,
|
||||
response.responseCode,
|
||||
response.headers,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -225,7 +255,13 @@ export function registerProductionWebhooks() {
|
|||
return;
|
||||
}
|
||||
|
||||
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
|
||||
ResponseHelper.sendSuccessResponse(
|
||||
res,
|
||||
response.data,
|
||||
true,
|
||||
response.responseCode,
|
||||
response.headers,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ import { IProcessMessage, WorkflowExecute } from 'n8n-core';
|
|||
|
||||
import {
|
||||
ExecutionError,
|
||||
IDeferredPromise,
|
||||
IExecuteResponsePromiseData,
|
||||
IRun,
|
||||
LoggerProxy as Logger,
|
||||
Workflow,
|
||||
|
@ -41,9 +43,7 @@ import {
|
|||
IBullJobResponse,
|
||||
ICredentialsOverwrite,
|
||||
ICredentialsTypeData,
|
||||
IExecutionDb,
|
||||
IExecutionFlattedDb,
|
||||
IExecutionResponse,
|
||||
IProcessMessageDataHook,
|
||||
ITransferNodeTypes,
|
||||
IWorkflowExecutionDataProcess,
|
||||
|
@ -51,6 +51,7 @@ import {
|
|||
NodeTypes,
|
||||
Push,
|
||||
ResponseHelper,
|
||||
WebhookHelpers,
|
||||
WorkflowExecuteAdditionalData,
|
||||
WorkflowHelpers,
|
||||
} from '.';
|
||||
|
@ -146,6 +147,7 @@ export class WorkflowRunner {
|
|||
loadStaticData?: boolean,
|
||||
realtime?: boolean,
|
||||
executionId?: string,
|
||||
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
|
||||
): Promise<string> {
|
||||
const executionsProcess = config.get('executions.process') as string;
|
||||
const executionsMode = config.get('executions.mode') as string;
|
||||
|
@ -153,11 +155,17 @@ export class WorkflowRunner {
|
|||
if (executionsMode === 'queue' && data.executionMode !== 'manual') {
|
||||
// Do not run "manual" executions in bull because sending events to the
|
||||
// frontend would not be possible
|
||||
executionId = await this.runBull(data, loadStaticData, realtime, executionId);
|
||||
executionId = await this.runBull(
|
||||
data,
|
||||
loadStaticData,
|
||||
realtime,
|
||||
executionId,
|
||||
responsePromise,
|
||||
);
|
||||
} else if (executionsProcess === 'main') {
|
||||
executionId = await this.runMainProcess(data, loadStaticData, executionId);
|
||||
executionId = await this.runMainProcess(data, loadStaticData, executionId, responsePromise);
|
||||
} else {
|
||||
executionId = await this.runSubprocess(data, loadStaticData, executionId);
|
||||
executionId = await this.runSubprocess(data, loadStaticData, executionId, responsePromise);
|
||||
}
|
||||
|
||||
const postExecutePromise = this.activeExecutions.getPostExecutePromise(executionId);
|
||||
|
@ -200,6 +208,7 @@ export class WorkflowRunner {
|
|||
data: IWorkflowExecutionDataProcess,
|
||||
loadStaticData?: boolean,
|
||||
restartExecutionId?: string,
|
||||
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
|
||||
): Promise<string> {
|
||||
if (loadStaticData === true && data.workflowData.id) {
|
||||
data.workflowData.staticData = await WorkflowHelpers.getStaticDataById(
|
||||
|
@ -256,6 +265,15 @@ export class WorkflowRunner {
|
|||
executionId,
|
||||
true,
|
||||
);
|
||||
|
||||
additionalData.hooks.hookFunctions.sendResponse = [
|
||||
async (response: IExecuteResponsePromiseData): Promise<void> => {
|
||||
if (responsePromise) {
|
||||
responsePromise.resolve(response);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
additionalData.sendMessageToUI = WorkflowExecuteAdditionalData.sendMessageToUI.bind({
|
||||
sessionId: data.sessionId,
|
||||
});
|
||||
|
@ -341,11 +359,15 @@ export class WorkflowRunner {
|
|||
loadStaticData?: boolean,
|
||||
realtime?: boolean,
|
||||
restartExecutionId?: string,
|
||||
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
|
||||
): Promise<string> {
|
||||
// TODO: If "loadStaticData" is set to true it has to load data new on worker
|
||||
|
||||
// Register the active execution
|
||||
const executionId = await this.activeExecutions.add(data, undefined, restartExecutionId);
|
||||
if (responsePromise) {
|
||||
this.activeExecutions.attachResponsePromise(executionId, responsePromise);
|
||||
}
|
||||
|
||||
const jobData: IBullJobData = {
|
||||
executionId,
|
||||
|
@ -545,6 +567,7 @@ export class WorkflowRunner {
|
|||
data: IWorkflowExecutionDataProcess,
|
||||
loadStaticData?: boolean,
|
||||
restartExecutionId?: string,
|
||||
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
|
||||
): Promise<string> {
|
||||
let startedAt = new Date();
|
||||
const subprocess = fork(pathJoin(__dirname, 'WorkflowRunnerProcess.js'));
|
||||
|
@ -653,6 +676,10 @@ export class WorkflowRunner {
|
|||
} else if (message.type === 'end') {
|
||||
clearTimeout(executionTimeout);
|
||||
this.activeExecutions.remove(executionId, message.data.runData);
|
||||
} else if (message.type === 'sendResponse') {
|
||||
if (responsePromise) {
|
||||
responsePromise.resolve(WebhookHelpers.decodeWebhookResponse(message.data.response));
|
||||
}
|
||||
} else if (message.type === 'sendMessageToUI') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
WorkflowExecuteAdditionalData.sendMessageToUI.bind({ sessionId: data.sessionId })(
|
||||
|
|
|
@ -10,6 +10,7 @@ import { IProcessMessage, UserSettings, WorkflowExecute } from 'n8n-core';
|
|||
import {
|
||||
ExecutionError,
|
||||
IDataObject,
|
||||
IExecuteResponsePromiseData,
|
||||
IExecuteWorkflowInfo,
|
||||
ILogger,
|
||||
INodeExecutionData,
|
||||
|
@ -33,6 +34,7 @@ import {
|
|||
IWorkflowExecuteProcess,
|
||||
IWorkflowExecutionDataProcessWithExecution,
|
||||
NodeTypes,
|
||||
WebhookHelpers,
|
||||
WorkflowExecuteAdditionalData,
|
||||
WorkflowHelpers,
|
||||
} from '.';
|
||||
|
@ -200,6 +202,15 @@ export class WorkflowRunnerProcess {
|
|||
workflowTimeout <= 0 ? undefined : Date.now() + workflowTimeout * 1000,
|
||||
);
|
||||
additionalData.hooks = this.getProcessForwardHooks();
|
||||
|
||||
additionalData.hooks.hookFunctions.sendResponse = [
|
||||
async (response: IExecuteResponsePromiseData): Promise<void> => {
|
||||
await sendToParentProcess('sendResponse', {
|
||||
response: WebhookHelpers.encodeWebhookResponse(response),
|
||||
});
|
||||
},
|
||||
];
|
||||
|
||||
additionalData.executionId = inputData.executionId;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-core",
|
||||
"version": "0.91.0",
|
||||
"version": "0.92.0",
|
||||
"description": "Core functionality of n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -50,7 +50,7 @@
|
|||
"form-data": "^4.0.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"mime-types": "^2.1.27",
|
||||
"n8n-workflow": "~0.74.0",
|
||||
"n8n-workflow": "~0.75.0",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
"p-cancelable": "^2.0.0",
|
||||
"qs": "^6.10.1",
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
ICredentialsExpressionResolveValues,
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
IExecuteResponsePromiseData,
|
||||
IExecuteSingleFunctions,
|
||||
IExecuteWorkflowInfo,
|
||||
IHttpRequestOptions,
|
||||
|
@ -1635,6 +1636,9 @@ export function getExecuteFunctions(
|
|||
Logger.warn(`There was a problem sending messsage to UI: ${error.message}`);
|
||||
}
|
||||
},
|
||||
async sendResponse(response: IExecuteResponsePromiseData): Promise<void> {
|
||||
await additionalData.hooks?.executeHookFunctions('sendResponse', [response]);
|
||||
},
|
||||
helpers: {
|
||||
httpRequest,
|
||||
prepareBinaryData,
|
||||
|
|
|
@ -12,7 +12,6 @@ export * from './ActiveWorkflows';
|
|||
export * from './ActiveWebhooks';
|
||||
export * from './Constants';
|
||||
export * from './Credentials';
|
||||
export * from './DeferredPromise';
|
||||
export * from './Interfaces';
|
||||
export * from './LoadNodeParameterOptions';
|
||||
export * from './NodeExecuteFunctions';
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
ICredentialDataDecryptedObject,
|
||||
ICredentialsHelper,
|
||||
IDataObject,
|
||||
IDeferredPromise,
|
||||
IExecuteWorkflowInfo,
|
||||
INodeCredentialsDetails,
|
||||
INodeExecutionData,
|
||||
|
@ -20,7 +21,7 @@ import {
|
|||
WorkflowHooks,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { Credentials, IDeferredPromise, IExecuteFunctions } from '../src';
|
||||
import { Credentials, IExecuteFunctions } from '../src';
|
||||
|
||||
export class CredentialsHelper extends ICredentialsHelper {
|
||||
getDecrypted(
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
import { IConnections, ILogger, INode, IRun, LoggerProxy, Workflow } from 'n8n-workflow';
|
||||
import {
|
||||
createDeferredPromise,
|
||||
IConnections,
|
||||
ILogger,
|
||||
INode,
|
||||
IRun,
|
||||
LoggerProxy,
|
||||
Workflow,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { createDeferredPromise, WorkflowExecute } from '../src';
|
||||
import { WorkflowExecute } from '../src';
|
||||
|
||||
import * as Helpers from './Helpers';
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-editor-ui",
|
||||
"version": "0.114.0",
|
||||
"version": "0.115.0",
|
||||
"description": "Workflow Editor UI for n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -72,7 +72,7 @@
|
|||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.set": "^4.3.2",
|
||||
"n8n-workflow": "~0.74.0",
|
||||
"n8n-workflow": "~0.75.0",
|
||||
"sass": "^1.26.5",
|
||||
"normalize-wheel": "^1.0.1",
|
||||
"prismjs": "^1.17.1",
|
||||
|
|
|
@ -98,7 +98,7 @@ const module: Module<ICredentialsState, IRootState> = {
|
|||
},
|
||||
getCredentialsByType: (state: ICredentialsState, getters: any) => { // tslint:disable-line:no-any
|
||||
return (credentialType: string): ICredentialsResponse[] => {
|
||||
return getters.allCredentialsByType[credentialType];
|
||||
return getters.allCredentialsByType[credentialType] || [];
|
||||
};
|
||||
},
|
||||
getNodesWithAccess (state: ICredentialsState, getters: any, rootState: IRootState, rootGetters: any) { // tslint:disable-line:no-any
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-node-dev",
|
||||
"version": "0.31.0",
|
||||
"version": "0.32.0",
|
||||
"description": "CLI to simplify n8n credentials/node development",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -60,8 +60,8 @@
|
|||
"change-case": "^4.1.1",
|
||||
"copyfiles": "^2.1.1",
|
||||
"inquirer": "^7.0.1",
|
||||
"n8n-core": "~0.91.0",
|
||||
"n8n-workflow": "~0.74.0",
|
||||
"n8n-core": "~0.92.0",
|
||||
"n8n-workflow": "~0.75.0",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
"replace-in-file": "^6.0.0",
|
||||
"request": "^2.88.2",
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class DropcontactApi implements ICredentialType {
|
||||
name = 'dropcontactApi';
|
||||
displayName = 'Dropcontact API';
|
||||
documentationUrl = 'dropcontact';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'API Key',
|
||||
name: 'apiKey',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
20
packages/nodes-base/nodes/Aws/Textract/AwsTextract.node.json
Normal file
20
packages/nodes-base/nodes/Aws/Textract/AwsTextract.node.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"node": "n8n-nodes-base.awsTextract",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Utility"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/aws"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.awsTextract/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
370
packages/nodes-base/nodes/Dropcontact/Dropcontact.node.ts
Normal file
370
packages/nodes-base/nodes/Dropcontact/Dropcontact.node.ts
Normal file
|
@ -0,0 +1,370 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
NodeApiError,
|
||||
NodeCredentialTestResult,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
dropcontactApiRequest,
|
||||
validateCrendetials,
|
||||
} from './GenericFunction';
|
||||
|
||||
export class Dropcontact implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Dropcontact',
|
||||
name: 'dropcontact',
|
||||
icon: 'file:dropcontact.svg',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
description: 'Find B2B emails and enrich contacts',
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
defaults: {
|
||||
name: 'Dropcontact',
|
||||
color: '#0ABA9F',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'dropcontactApi',
|
||||
required: true,
|
||||
testedBy: 'dropcontactApiCredentialTest',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
noDataExpression: true,
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Contact',
|
||||
value: 'contact',
|
||||
},
|
||||
],
|
||||
default: 'contact',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Operation',
|
||||
noDataExpression: true,
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Enrich',
|
||||
value: 'enrich',
|
||||
description: 'Find B2B emails and enrich your contact from his name and his website',
|
||||
},
|
||||
{
|
||||
name: 'Fetch Request',
|
||||
value: 'fetchRequest',
|
||||
},
|
||||
],
|
||||
default: 'enrich',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Request ID',
|
||||
name: 'requestId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'fetchRequest',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'enrich',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Simplify Output (Faster)',
|
||||
name: 'simplify',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'enrich',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'When off, waits for the contact data before completing. Waiting time can be adjusted with Extend Wait Time option. When on, returns a request_id that can be used later in the Fetch Request operation.',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'enrich',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Company SIREN Number',
|
||||
name: 'num_siren',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Company SIRET Code',
|
||||
name: 'siret',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Company Name',
|
||||
name: 'company',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Country',
|
||||
name: 'country',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'First Name',
|
||||
name: 'first_name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Full Name',
|
||||
name: 'full_name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Name',
|
||||
name: 'last_name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'LinkedIn Profile',
|
||||
name: 'linkedin',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Phone Number',
|
||||
name: 'phone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Website',
|
||||
name: 'website',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'enrich',
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Data Fetch Wait Time',
|
||||
name: 'waitTime',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/simplify': [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
default: 45,
|
||||
description: 'When not simplifying the response, data will be fetched in two steps. This parameter controls how long to wait (in seconds) before trying the second step',
|
||||
},
|
||||
{
|
||||
displayName: 'French Company Enrich',
|
||||
name: 'siren',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `Whether you want the <a href="https://en.wikipedia.org/wiki/SIREN_code" target="_blank">SIREN number</a>, NAF code, TVA number, company address and informations about the company leader.</br>
|
||||
Only applies to french companies`,
|
||||
},
|
||||
{
|
||||
displayName: 'Language',
|
||||
name: 'language',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'English',
|
||||
value: 'en',
|
||||
},
|
||||
{
|
||||
name: 'French',
|
||||
value: 'fr',
|
||||
},
|
||||
],
|
||||
default: 'en',
|
||||
description: 'Whether the response is in English or French',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: {
|
||||
async dropcontactApiCredentialTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<NodeCredentialTestResult> {
|
||||
try {
|
||||
await validateCrendetials.call(this, credential.data as ICredentialDataDecryptedObject);
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: 'The API Key included in the request is invalid',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Connection successful!',
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const entryData = this.getInputData();
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
// tslint:disable-next-line: no-any
|
||||
let responseData: any;
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
if (resource === 'contact') {
|
||||
if (operation === 'enrich') {
|
||||
const options = this.getNodeParameter('options', 0) as IDataObject;
|
||||
const data = [];
|
||||
const simplify = this.getNodeParameter('simplify', 0) as boolean;
|
||||
|
||||
const siren = options.siren === true ? true : false;
|
||||
const language = options.language ? options.language : 'en';
|
||||
|
||||
for (let i = 0; i < entryData.length; i++) {
|
||||
const email = this.getNodeParameter('email', i) as string;
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
const body: IDataObject = {};
|
||||
if (email !== '') {
|
||||
body.email = email;
|
||||
}
|
||||
Object.assign(body, additionalFields);
|
||||
data.push(body);
|
||||
}
|
||||
|
||||
responseData = await dropcontactApiRequest.call(this, 'POST', '/batch', { data, siren, language }, {}) as { request_id: string, error: string, success: boolean };
|
||||
|
||||
if (!responseData.success) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: responseData.reason || 'invalid request' });
|
||||
} else {
|
||||
throw new NodeApiError(this.getNode(), { error: responseData.reason || 'invalid request' });
|
||||
}
|
||||
}
|
||||
|
||||
if (simplify === false) {
|
||||
const waitTime = this.getNodeParameter('options.waitTime', 0, 45) as number;
|
||||
// tslint:disable-next-line: no-any
|
||||
const delay = (ms: any) => new Promise(res => setTimeout(res, ms * 1000));
|
||||
await delay(waitTime);
|
||||
responseData = await dropcontactApiRequest.call(this, 'GET', `/batch/${responseData.request_id}`, {}, {});
|
||||
if (!responseData.success) {
|
||||
if (this.continueOnFail()) {
|
||||
responseData.push({ error: responseData.reason });
|
||||
} else {
|
||||
throw new NodeApiError(this.getNode(), {
|
||||
error: responseData.reason,
|
||||
description: 'Hint: Increase the Wait Time to avoid this error',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
returnData.push(...responseData.data);
|
||||
}
|
||||
} else {
|
||||
returnData.push(responseData);
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'fetchRequest') {
|
||||
for (let i = 0; i < entryData.length; i++) {
|
||||
const requestId = this.getNodeParameter('requestId', i) as string;
|
||||
responseData = await dropcontactApiRequest.call(this, 'GET', `/batch/${requestId}`, {}, {}) as { request_id: string, error: string, success: boolean };
|
||||
if (!responseData.success) {
|
||||
if (this.continueOnFail()) {
|
||||
responseData.push({ error: responseData.reason || 'invalid request' });
|
||||
} else {
|
||||
throw new NodeApiError(this.getNode(), { error: responseData.reason || 'invalid request' });
|
||||
}
|
||||
}
|
||||
returnData.push(...responseData.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
82
packages/nodes-base/nodes/Dropcontact/GenericFunction.ts
Normal file
82
packages/nodes-base/nodes/Dropcontact/GenericFunction.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
/**
|
||||
* Make an authenticated API request to Bubble.
|
||||
*/
|
||||
export async function dropcontactApiRequest(
|
||||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: string,
|
||||
endpoint: string,
|
||||
body: IDataObject,
|
||||
qs: IDataObject,
|
||||
) {
|
||||
|
||||
const { apiKey } = await this.getCredentials('dropcontactApi') as {
|
||||
apiKey: string,
|
||||
};
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'user-agent': 'n8n',
|
||||
'X-Access-Token': apiKey,
|
||||
},
|
||||
method,
|
||||
uri: `https://api.dropcontact.io${endpoint}`,
|
||||
qs,
|
||||
body,
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (!Object.keys(body).length) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
if (!Object.keys(qs).length) {
|
||||
delete options.qs;
|
||||
}
|
||||
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function validateCrendetials(this: ICredentialTestFunctions, decryptedCredentials: ICredentialDataDecryptedObject): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = decryptedCredentials;
|
||||
|
||||
const { apiKey } = credentials as {
|
||||
apiKey: string,
|
||||
};
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'user-agent': 'n8n',
|
||||
'X-Access-Token': apiKey,
|
||||
},
|
||||
method: 'POST',
|
||||
body: {
|
||||
data: [{ email: '' }],
|
||||
},
|
||||
uri: `https://api.dropcontact.io/batch`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
return this.helpers.request!(options);
|
||||
}
|
||||
|
3
packages/nodes-base/nodes/Dropcontact/dropcontact.svg
Normal file
3
packages/nodes-base/nodes/Dropcontact/dropcontact.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.5692 16.219C28.1469 15.5371 26.5535 15.1552 24.8709 15.1552C18.8615 15.1552 13.9899 20.0268 13.9899 26.0362C13.9899 32.0456 18.8615 36.9172 24.8709 36.9172C28.9621 36.9172 32.526 34.6592 34.3843 31.3215L31.7614 29.8643C30.4154 32.2818 27.8341 33.9172 24.8709 33.9172C20.5183 33.9172 16.9899 30.3888 16.9899 26.0362C16.9899 21.6836 20.5183 18.1552 24.8709 18.1552C26.0896 18.1552 27.2437 18.4318 28.2738 18.9257L29.5692 16.219ZM34.7779 1.98444V27.7461H31.669V0.899363C29.5459 0.313171 27.3095 0 25 0C11.1929 0 0 11.1929 0 25C0 38.8071 11.1929 50 25 50C38.8071 50 50 38.8071 50 25C50 14.6626 43.7259 5.7907 34.7779 1.98444Z" fill="#0ABA9F"/>
|
||||
</svg>
|
After Width: | Height: | Size: 773 B |
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"node": "n8n-nodes-base.googleDriveTrigger",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Data & Storage"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/google"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.googleDriveTrigger/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
24
packages/nodes-base/nodes/LocalFileTrigger.node.json
Normal file
24
packages/nodes-base/nodes/LocalFileTrigger.node.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"node": "n8n-nodes-base.localFileTrigger",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Core Nodes"
|
||||
],
|
||||
"resources": {
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.localFileTrigger/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"alias": [
|
||||
"Watch",
|
||||
"Monitor"
|
||||
],
|
||||
"subcategories": {
|
||||
"Core Nodes":[
|
||||
"Files"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"node": "n8n-nodes-base.microsoftDynamicsCrm",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Marketing & Content",
|
||||
"Sales"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/microsoft"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.microsoftDynamicsCrm/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
278
packages/nodes-base/nodes/RespondToWebhook.node.ts
Normal file
278
packages/nodes-base/nodes/RespondToWebhook.node.ts
Normal file
|
@ -0,0 +1,278 @@
|
|||
import {
|
||||
BINARY_ENCODING,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
IN8nHttpFullResponse,
|
||||
IN8nHttpResponse,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class RespondToWebhook implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Respond to Webhook',
|
||||
icon: 'file:webhook.svg',
|
||||
name: 'respondToWebhook',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
description: 'Returns data for Webhook',
|
||||
defaults: {
|
||||
name: 'Respond to Webhook',
|
||||
color: '#885577',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Respond With',
|
||||
name: 'respondWith',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'First Incoming Item',
|
||||
value: 'firstIncomingItem',
|
||||
},
|
||||
{
|
||||
name: 'Text',
|
||||
value: 'text',
|
||||
},
|
||||
{
|
||||
name: 'JSON',
|
||||
value: 'json',
|
||||
},
|
||||
{
|
||||
name: 'Binary',
|
||||
value: 'binary',
|
||||
},
|
||||
{
|
||||
name: 'No Data',
|
||||
value: 'noData',
|
||||
},
|
||||
],
|
||||
default: 'firstIncomingItem',
|
||||
description: 'The data that should be returned',
|
||||
},
|
||||
{
|
||||
displayName: 'When using expressions, note that this node will only run for the first item in the input data.',
|
||||
name: 'webhookNotice',
|
||||
type: 'notice',
|
||||
displayOptions: {
|
||||
show: {
|
||||
respondWith: [
|
||||
'json',
|
||||
'text',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Response Body',
|
||||
name: 'responseBody',
|
||||
type: 'json',
|
||||
displayOptions: {
|
||||
show: {
|
||||
respondWith: [
|
||||
'json',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
placeholder: '{ "key": "value" }',
|
||||
description: 'The HTTP Response JSON data',
|
||||
},
|
||||
{
|
||||
displayName: 'Response Body',
|
||||
name: 'responseBody',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
respondWith: [
|
||||
'text',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'e.g. Workflow started',
|
||||
description: 'The HTTP Response text data',
|
||||
},
|
||||
{
|
||||
displayName: 'Response Data Source',
|
||||
name: 'responseDataSource',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
respondWith: [
|
||||
'binary',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Choose Automatically From Input',
|
||||
value: 'automatically',
|
||||
description: 'Use if input data will contain a single piece of binary data',
|
||||
},
|
||||
{
|
||||
name: 'Specify Myself',
|
||||
value: 'set',
|
||||
description: 'Enter the name of the input field the binary data will be in',
|
||||
},
|
||||
],
|
||||
default: 'automatically',
|
||||
},
|
||||
{
|
||||
displayName: 'Input Field Name',
|
||||
name: 'inputFieldName',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: 'data',
|
||||
displayOptions: {
|
||||
show: {
|
||||
respondWith: [
|
||||
'binary',
|
||||
],
|
||||
responseDataSource: [
|
||||
'set',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The name of the node input field with the binary data',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Response Code',
|
||||
name: 'responseCode',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 100,
|
||||
maxValue: 599,
|
||||
},
|
||||
default: 200,
|
||||
description: 'The HTTP Response code to return. Defaults to 200.',
|
||||
},
|
||||
{
|
||||
displayName: 'Response Headers',
|
||||
name: 'responseHeaders',
|
||||
placeholder: 'Add Response Header',
|
||||
description: 'Add headers to the webhook response',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'entries',
|
||||
displayName: 'Entries',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Name of the header',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Value of the header',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
|
||||
const respondWith = this.getNodeParameter('respondWith', 0) as string;
|
||||
const options = this.getNodeParameter('options', 0, {}) as IDataObject;
|
||||
|
||||
const headers = {} as IDataObject;
|
||||
if (options.responseHeaders) {
|
||||
for (const header of (options.responseHeaders as IDataObject).entries as IDataObject[]) {
|
||||
if (typeof header.name !== 'string') {
|
||||
header.name = header.name?.toString();
|
||||
}
|
||||
headers[header.name?.toLowerCase() as string] = header.value?.toString();
|
||||
}
|
||||
}
|
||||
|
||||
let responseBody: IN8nHttpResponse;
|
||||
if (respondWith === 'json') {
|
||||
const responseBodyParameter = this.getNodeParameter('responseBody', 0) as string;
|
||||
if (responseBodyParameter) {
|
||||
responseBody = JSON.parse(responseBodyParameter);
|
||||
}
|
||||
} else if (respondWith === 'firstIncomingItem') {
|
||||
responseBody = items[0].json;
|
||||
} else if (respondWith === 'text') {
|
||||
responseBody = this.getNodeParameter('responseBody', 0) as string;
|
||||
} else if (respondWith === 'binary') {
|
||||
const item = this.getInputData()[0];
|
||||
|
||||
if (item.binary === undefined) {
|
||||
throw new NodeOperationError(this.getNode(), 'No binary data exists on the first item!');
|
||||
}
|
||||
|
||||
let responseBinaryPropertyName: string;
|
||||
|
||||
const responseDataSource = this.getNodeParameter('responseDataSource', 0) as string;
|
||||
|
||||
if (responseDataSource === 'set') {
|
||||
responseBinaryPropertyName = this.getNodeParameter('inputFieldName', 0) as string;
|
||||
} else {
|
||||
const binaryKeys = Object.keys(item.binary);
|
||||
if (binaryKeys.length === 0) {
|
||||
throw new NodeOperationError(this.getNode(), 'No binary data exists on the first item!');
|
||||
}
|
||||
responseBinaryPropertyName = binaryKeys[0];
|
||||
}
|
||||
|
||||
const binaryData = item.binary[responseBinaryPropertyName];
|
||||
|
||||
if (binaryData === undefined) {
|
||||
throw new NodeOperationError(this.getNode(), `No binary data property "${responseBinaryPropertyName}" does not exists on item!`);
|
||||
}
|
||||
|
||||
if (headers['content-type']) {
|
||||
headers['content-type'] = binaryData.mimeType;
|
||||
}
|
||||
responseBody = Buffer.from(binaryData.data, BINARY_ENCODING);
|
||||
} else if (respondWith !== 'noData') {
|
||||
throw new NodeOperationError(this.getNode(), `The Response Data option "${respondWith}" is not supported!`);
|
||||
}
|
||||
|
||||
const response: IN8nHttpFullResponse = {
|
||||
body: responseBody,
|
||||
headers,
|
||||
statusCode: options.responseCode as number || 200,
|
||||
};
|
||||
|
||||
this.sendResponse(response);
|
||||
|
||||
return this.prepareOutputData(items);
|
||||
}
|
||||
|
||||
}
|
|
@ -283,7 +283,7 @@ export class Wait implements INodeType {
|
|||
description: 'The HTTP Response code to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Respond When',
|
||||
displayName: 'Respond',
|
||||
name: 'responseMode',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
|
@ -295,14 +295,19 @@ export class Wait implements INodeType {
|
|||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Webhook received',
|
||||
name: 'Immediately',
|
||||
value: 'onReceived',
|
||||
description: 'Returns directly with defined Response Code',
|
||||
description: 'As soon as this node executes',
|
||||
},
|
||||
{
|
||||
name: 'Last node finishes',
|
||||
name: 'When last node finishes',
|
||||
value: 'lastNode',
|
||||
description: 'Returns data of the last executed node',
|
||||
description: 'Returns data of the last-executed node',
|
||||
},
|
||||
{
|
||||
name: 'Using \'Respond to Webhook\' node',
|
||||
value: 'responseNode',
|
||||
description: 'Response defined in that node',
|
||||
},
|
||||
],
|
||||
default: 'onReceived',
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
INodeType,
|
||||
INodeTypeDescription,
|
||||
IWebhookResponseData,
|
||||
NodeApiError,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
@ -143,10 +142,54 @@ export class Webhook implements INodeType {
|
|||
required: true,
|
||||
description: 'The path to listen to.',
|
||||
},
|
||||
{
|
||||
displayName: 'Respond',
|
||||
name: 'responseMode',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Immediately',
|
||||
value: 'onReceived',
|
||||
description: 'As soon as this node executes',
|
||||
},
|
||||
{
|
||||
name: 'When last node finishes',
|
||||
value: 'lastNode',
|
||||
description: 'Returns data of the last-executed node',
|
||||
},
|
||||
{
|
||||
name: 'Using \'Respond to Webhook\' node',
|
||||
value: 'responseNode',
|
||||
description: 'Response defined in that node',
|
||||
},
|
||||
],
|
||||
default: 'onReceived',
|
||||
description: 'When and how to respond to the webhook.',
|
||||
},
|
||||
{
|
||||
displayName: 'Insert a \'Respond to Webhook\' node to control when and how you respond. <a href="https://docs.n8n.io/nodes/n8n-nodes-base.respondToWebhook" target="_blank">More details</a>',
|
||||
name: 'webhookNotice',
|
||||
type: 'notice',
|
||||
displayOptions: {
|
||||
show: {
|
||||
responseMode: [
|
||||
'responseNode',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Response Code',
|
||||
name: 'responseCode',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
hide: {
|
||||
responseMode: [
|
||||
'responseNode',
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 100,
|
||||
maxValue: 599,
|
||||
|
@ -154,25 +197,6 @@ export class Webhook implements INodeType {
|
|||
default: 200,
|
||||
description: 'The HTTP Response code to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Respond When',
|
||||
name: 'responseMode',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Webhook received',
|
||||
value: 'onReceived',
|
||||
description: 'Returns directly with defined Response Code',
|
||||
},
|
||||
{
|
||||
name: 'Last node finishes',
|
||||
value: 'lastNode',
|
||||
description: 'Returns data of the last executed node',
|
||||
},
|
||||
],
|
||||
default: 'onReceived',
|
||||
description: 'When and how to respond to the webhook.',
|
||||
},
|
||||
{
|
||||
displayName: 'Response Data',
|
||||
name: 'responseData',
|
||||
|
|
|
@ -130,7 +130,7 @@ export const userFields = [
|
|||
},
|
||||
{
|
||||
displayName: 'External ID',
|
||||
name: 'externalId',
|
||||
name: 'external_id',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'A unique identifier from another system',
|
||||
|
@ -387,7 +387,7 @@ export const userFields = [
|
|||
},
|
||||
{
|
||||
displayName: 'External ID',
|
||||
name: 'externalId',
|
||||
name: 'external_id',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'A unique identifier from another system',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-nodes-base",
|
||||
"version": "0.144.1",
|
||||
"version": "0.145.0",
|
||||
"description": "Base nodes of n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -78,6 +78,7 @@
|
|||
"dist/credentials/DriftOAuth2Api.credentials.js",
|
||||
"dist/credentials/DropboxApi.credentials.js",
|
||||
"dist/credentials/DropboxOAuth2Api.credentials.js",
|
||||
"dist/credentials/DropcontactApi.credentials.js",
|
||||
"dist/credentials/EgoiApi.credentials.js",
|
||||
"dist/credentials/ElasticsearchApi.credentials.js",
|
||||
"dist/credentials/ElasticSecurityApi.credentials.js",
|
||||
|
@ -379,6 +380,7 @@
|
|||
"dist/nodes/Disqus/Disqus.node.js",
|
||||
"dist/nodes/Drift/Drift.node.js",
|
||||
"dist/nodes/Dropbox/Dropbox.node.js",
|
||||
"dist/nodes/Dropcontact/Dropcontact.node.js",
|
||||
"dist/nodes/EditImage.node.js",
|
||||
"dist/nodes/Egoi/Egoi.node.js",
|
||||
"dist/nodes/Elastic/ElasticSecurity/ElasticSecurity.node.js",
|
||||
|
@ -552,6 +554,7 @@
|
|||
"dist/nodes/Reddit/Reddit.node.js",
|
||||
"dist/nodes/Redis/Redis.node.js",
|
||||
"dist/nodes/RenameKeys.node.js",
|
||||
"dist/nodes/RespondToWebhook.node.js",
|
||||
"dist/nodes/Rocketchat/Rocketchat.node.js",
|
||||
"dist/nodes/RssFeedRead.node.js",
|
||||
"dist/nodes/Rundeck/Rundeck.node.js",
|
||||
|
@ -668,7 +671,7 @@
|
|||
"@types/xml2js": "^0.4.3",
|
||||
"gulp": "^4.0.0",
|
||||
"jest": "^26.4.2",
|
||||
"n8n-workflow": "~0.74.0",
|
||||
"n8n-workflow": "~0.75.0",
|
||||
"nodelinter": "^0.1.9",
|
||||
"ts-jest": "^26.3.0",
|
||||
"tslint": "^6.1.2",
|
||||
|
@ -708,7 +711,7 @@
|
|||
"mqtt": "4.2.6",
|
||||
"mssql": "^6.2.0",
|
||||
"mysql2": "~2.3.0",
|
||||
"n8n-core": "~0.91.0",
|
||||
"n8n-core": "~0.92.0",
|
||||
"node-ssh": "^12.0.0",
|
||||
"nodemailer": "^6.5.0",
|
||||
"pdf-parse": "^1.1.1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-workflow",
|
||||
"version": "0.74.0",
|
||||
"version": "0.75.0",
|
||||
"description": "Workflow base code of n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import * as express from 'express';
|
||||
import * as FormData from 'form-data';
|
||||
import { URLSearchParams } from 'url';
|
||||
import { IDeferredPromise } from './DeferredPromise';
|
||||
import { Workflow } from './Workflow';
|
||||
import { WorkflowHooks } from './WorkflowHooks';
|
||||
import { WorkflowOperationError } from './WorkflowErrors';
|
||||
|
@ -208,6 +209,9 @@ export interface IDataObject {
|
|||
[key: string]: GenericValue | IDataObject | GenericValue[] | IDataObject[];
|
||||
}
|
||||
|
||||
// export type IExecuteResponsePromiseData = IDataObject;
|
||||
export type IExecuteResponsePromiseData = IDataObject | IN8nHttpFullResponse;
|
||||
|
||||
export interface INodeTypeNameVersion {
|
||||
name: string;
|
||||
version: number;
|
||||
|
@ -324,13 +328,13 @@ export interface IHttpRequestOptions {
|
|||
json?: boolean;
|
||||
}
|
||||
|
||||
export type IN8nHttpResponse = IDataObject | Buffer | GenericValue | GenericValue[];
|
||||
export type IN8nHttpResponse = IDataObject | Buffer | GenericValue | GenericValue[] | null;
|
||||
|
||||
export interface IN8nHttpFullResponse {
|
||||
body: IN8nHttpResponse;
|
||||
headers: IDataObject;
|
||||
statusCode: number;
|
||||
statusMessage: string;
|
||||
statusMessage?: string;
|
||||
}
|
||||
|
||||
export interface IExecuteFunctions {
|
||||
|
@ -371,7 +375,8 @@ export interface IExecuteFunctions {
|
|||
outputIndex?: number,
|
||||
): Promise<INodeExecutionData[][]>;
|
||||
putExecutionToWait(waitTill: Date): Promise<void>;
|
||||
sendMessageToUI(message: any): void;
|
||||
sendMessageToUI(message: any): void; // tslint:disable-line:no-any
|
||||
sendResponse(response: IExecuteResponsePromiseData): void; // tslint:disable-line:no-any
|
||||
helpers: {
|
||||
httpRequest(
|
||||
requestOptions: IHttpRequestOptions,
|
||||
|
@ -492,7 +497,10 @@ export interface IPollFunctions {
|
|||
}
|
||||
|
||||
export interface ITriggerFunctions {
|
||||
emit(data: INodeExecutionData[][]): void;
|
||||
emit(
|
||||
data: INodeExecutionData[][],
|
||||
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
|
||||
): void;
|
||||
getCredentials(type: string): Promise<ICredentialDataDecryptedObject | undefined>;
|
||||
getMode(): WorkflowExecuteMode;
|
||||
getActivationMode(): WorkflowActivateMode;
|
||||
|
@ -975,6 +983,7 @@ export interface IWorkflowExecuteHooks {
|
|||
nodeExecuteBefore?: Array<(nodeName: string) => Promise<void>>;
|
||||
workflowExecuteAfter?: Array<(data: IRun, newStaticData: IDataObject) => Promise<void>>;
|
||||
workflowExecuteBefore?: Array<(workflow: Workflow, data: IRunExecutionData) => Promise<void>>;
|
||||
sendResponse?: Array<(response: IExecuteResponsePromiseData) => Promise<void>>;
|
||||
}
|
||||
|
||||
export interface IWorkflowExecuteAdditionalData {
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
import {
|
||||
Expression,
|
||||
IConnections,
|
||||
IDeferredPromise,
|
||||
IExecuteResponsePromiseData,
|
||||
IGetExecuteTriggerFunctions,
|
||||
INode,
|
||||
INodeExecuteFunctions,
|
||||
|
@ -946,10 +948,23 @@ export class Workflow {
|
|||
|
||||
// Add the manual trigger response which resolves when the first time data got emitted
|
||||
triggerResponse!.manualTriggerResponse = new Promise((resolve) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
triggerFunctions.emit = ((resolve) => (data: INodeExecutionData[][]) => {
|
||||
resolve(data);
|
||||
})(resolve);
|
||||
triggerFunctions.emit = (
|
||||
(resolveEmit) =>
|
||||
(
|
||||
data: INodeExecutionData[][],
|
||||
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
|
||||
) => {
|
||||
additionalData.hooks!.hookFunctions.sendResponse = [
|
||||
async (response: IExecuteResponsePromiseData): Promise<void> => {
|
||||
if (responsePromise) {
|
||||
responsePromise.resolve(response);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
resolveEmit(data);
|
||||
}
|
||||
)(resolve);
|
||||
});
|
||||
|
||||
return triggerResponse;
|
||||
|
|
|
@ -3,6 +3,7 @@ import * as LoggerProxy from './LoggerProxy';
|
|||
import * as NodeHelpers from './NodeHelpers';
|
||||
import * as ObservableObject from './ObservableObject';
|
||||
|
||||
export * from './DeferredPromise';
|
||||
export * from './Interfaces';
|
||||
export * from './Expression';
|
||||
export * from './NodeErrors';
|
||||
|
|
Loading…
Reference in a new issue