mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57:29 -08:00
✨ Add functionality to send console.log messages to editor-UI (#1816)
* ✨ Send console.log messages to editor-UI * ⚡ Send message only to session which started workflow * ⚡ Made it also work in own process * ⚡ Add support for console.log UI forward also to FunctionItem Node * 👕 Fix lint issue * 👕 Fix linting issue * ⚡ Improve code Co-authored-by: Iván Ovejero <ivov.src@gmail.com> Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
This commit is contained in:
parent
8e793e27b3
commit
4946bfcd3e
|
@ -337,11 +337,11 @@ export interface IPackageVersions {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPushData {
|
export interface IPushData {
|
||||||
data: IPushDataExecutionFinished | IPushDataNodeExecuteAfter | IPushDataNodeExecuteBefore | IPushDataTestWebhook;
|
data: IPushDataExecutionFinished | IPushDataNodeExecuteAfter | IPushDataNodeExecuteBefore | IPushDataTestWebhook | IPushDataConsoleMessage;
|
||||||
type: IPushDataType;
|
type: IPushDataType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IPushDataType = 'executionFinished' | 'executionStarted' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'testWebhookDeleted' | 'testWebhookReceived';
|
export type IPushDataType = 'executionFinished' | 'executionStarted' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'sendConsoleMessage' | 'testWebhookDeleted' | 'testWebhookReceived';
|
||||||
|
|
||||||
export interface IPushDataExecutionFinished {
|
export interface IPushDataExecutionFinished {
|
||||||
data: IRun;
|
data: IRun;
|
||||||
|
@ -376,6 +376,10 @@ export interface IPushDataTestWebhook {
|
||||||
workflowId: string;
|
workflowId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IPushDataConsoleMessage {
|
||||||
|
source: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IResponseCallbackData {
|
export interface IResponseCallbackData {
|
||||||
data?: IDataObject | IDataObject[];
|
data?: IDataObject | IDataObject[];
|
||||||
|
|
|
@ -663,6 +663,24 @@ export async function executeWorkflow(workflowInfo: IExecuteWorkflowInfo, additi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function sendMessageToUI(source: string, message: string) {
|
||||||
|
if (this.sessionId === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push data to session which started workflow
|
||||||
|
try {
|
||||||
|
const pushInstance = Push.getInstance();
|
||||||
|
pushInstance.send('sendConsoleMessage', {
|
||||||
|
source: `Node: "${source}"`,
|
||||||
|
message,
|
||||||
|
}, this.sessionId);
|
||||||
|
} catch (error) {
|
||||||
|
Logger.warn(`There was a problem sending messsage to UI: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the base additional data without webhooks
|
* Returns the base additional data without webhooks
|
||||||
*
|
*
|
||||||
|
@ -785,4 +803,3 @@ export function getWorkflowHooksMain(data: IWorkflowExecutionDataProcess, execut
|
||||||
|
|
||||||
return new WorkflowHooks(hookFunctions, data.executionMode, executionId, data.workflowData, { sessionId: data.sessionId, retryOf: data.retryOf as string });
|
return new WorkflowHooks(hookFunctions, data.executionMode, executionId, data.workflowData, { sessionId: data.sessionId, retryOf: data.retryOf as string });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -182,6 +182,8 @@ export class WorkflowRunner {
|
||||||
|
|
||||||
additionalData.hooks = WorkflowExecuteAdditionalData.getWorkflowHooksMain(data, executionId, true);
|
additionalData.hooks = WorkflowExecuteAdditionalData.getWorkflowHooksMain(data, executionId, true);
|
||||||
|
|
||||||
|
additionalData.sendMessageToUI = WorkflowExecuteAdditionalData.sendMessageToUI.bind({sessionId: data.sessionId});
|
||||||
|
|
||||||
let workflowExecution: PCancelable<IRun>;
|
let workflowExecution: PCancelable<IRun>;
|
||||||
if (data.executionData !== undefined) {
|
if (data.executionData !== undefined) {
|
||||||
Logger.debug(`Execution ID ${executionId} had Execution data. Running with payload.`, {executionId});
|
Logger.debug(`Execution ID ${executionId} had Execution data. Running with payload.`, {executionId});
|
||||||
|
@ -468,6 +470,9 @@ export class WorkflowRunner {
|
||||||
clearTimeout(executionTimeout);
|
clearTimeout(executionTimeout);
|
||||||
this.activeExecutions.remove(executionId!, message.data.runData);
|
this.activeExecutions.remove(executionId!, message.data.runData);
|
||||||
|
|
||||||
|
} else if (message.type === 'sendMessageToUI') {
|
||||||
|
WorkflowExecuteAdditionalData.sendMessageToUI.bind({ sessionId: data.sessionId })(message.data.source, message.data.message);
|
||||||
|
|
||||||
} else if (message.type === 'processError') {
|
} else if (message.type === 'processError') {
|
||||||
clearTimeout(executionTimeout);
|
clearTimeout(executionTimeout);
|
||||||
const executionError = message.data.executionError as ExecutionError;
|
const executionError = message.data.executionError as ExecutionError;
|
||||||
|
|
|
@ -137,6 +137,17 @@ export class WorkflowRunnerProcess {
|
||||||
const additionalData = await WorkflowExecuteAdditionalData.getBase(this.data.credentials, undefined, workflowTimeout <= 0 ? undefined : Date.now() + workflowTimeout * 1000);
|
const additionalData = await WorkflowExecuteAdditionalData.getBase(this.data.credentials, undefined, workflowTimeout <= 0 ? undefined : Date.now() + workflowTimeout * 1000);
|
||||||
additionalData.hooks = this.getProcessForwardHooks();
|
additionalData.hooks = this.getProcessForwardHooks();
|
||||||
|
|
||||||
|
additionalData.sendMessageToUI = async (source: string, message: string) => {
|
||||||
|
if (workflowRunner.data!.executionMode !== 'manual') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sendToParentProcess('sendMessageToUI', { source, message });
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`There was a problem sending UI data to parent process: "${error.message}"`);
|
||||||
|
}
|
||||||
|
};
|
||||||
const executeWorkflowFunction = additionalData.executeWorkflow;
|
const executeWorkflowFunction = additionalData.executeWorkflow;
|
||||||
additionalData.executeWorkflow = async (workflowInfo: IExecuteWorkflowInfo, additionalData: IWorkflowExecuteAdditionalData, inputData?: INodeExecutionData[] | undefined): Promise<Array<INodeExecutionData[] | null> | IRun> => {
|
additionalData.executeWorkflow = async (workflowInfo: IExecuteWorkflowInfo, additionalData: IWorkflowExecuteAdditionalData, inputData?: INodeExecutionData[] | undefined): Promise<Array<INodeExecutionData[] | null> | IRun> => {
|
||||||
const workflowData = await WorkflowExecuteAdditionalData.getWorkflowData(workflowInfo);
|
const workflowData = await WorkflowExecuteAdditionalData.getWorkflowData(workflowInfo);
|
||||||
|
|
|
@ -749,6 +749,18 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx
|
||||||
return workflow.getStaticData(type, node);
|
return workflow.getStaticData(type, node);
|
||||||
},
|
},
|
||||||
prepareOutputData: NodeHelpers.prepareOutputData,
|
prepareOutputData: NodeHelpers.prepareOutputData,
|
||||||
|
sendMessageToUI(message: string): void {
|
||||||
|
if (mode !== 'manual') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (additionalData.sendMessageToUI) {
|
||||||
|
additionalData.sendMessageToUI(node.name, message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Logger.warn(`There was a problem sending messsage to UI: ${error.message}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
helpers: {
|
helpers: {
|
||||||
prepareBinaryData,
|
prepareBinaryData,
|
||||||
request: requestPromiseWithDefaults,
|
request: requestPromiseWithDefaults,
|
||||||
|
|
|
@ -752,6 +752,7 @@ export function WorkflowExecuteAdditionalData(waitPromise: IDeferredPromise<IRun
|
||||||
credentialsHelper: new CredentialsHelper({}, ''),
|
credentialsHelper: new CredentialsHelper({}, ''),
|
||||||
hooks: new WorkflowHooks(hookFunctions, 'trigger', '1', workflowData),
|
hooks: new WorkflowHooks(hookFunctions, 'trigger', '1', workflowData),
|
||||||
executeWorkflow: async (workflowInfo: IExecuteWorkflowInfo): Promise<any> => {}, // tslint:disable-line:no-any
|
executeWorkflow: async (workflowInfo: IExecuteWorkflowInfo): Promise<any> => {}, // tslint:disable-line:no-any
|
||||||
|
sendMessageToUI: (message: string) => {},
|
||||||
restApiUrl: '',
|
restApiUrl: '',
|
||||||
encryptionKey: 'test',
|
encryptionKey: 'test',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
|
|
|
@ -356,11 +356,11 @@ export interface IExecutionDeleteFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPushData {
|
export interface IPushData {
|
||||||
data: IPushDataExecutionFinished | IPushDataNodeExecuteAfter | IPushDataNodeExecuteBefore | IPushDataTestWebhook;
|
data: IPushDataExecutionFinished | IPushDataNodeExecuteAfter | IPushDataNodeExecuteBefore | IPushDataTestWebhook | IPushDataConsoleMessage;
|
||||||
type: IPushDataType;
|
type: IPushDataType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IPushDataType = 'executionFinished' | 'executionStarted' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'testWebhookDeleted' | 'testWebhookReceived';
|
export type IPushDataType = 'executionFinished' | 'executionStarted' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'sendConsoleMessage' | 'testWebhookDeleted' | 'testWebhookReceived';
|
||||||
|
|
||||||
export interface IPushDataExecutionStarted {
|
export interface IPushDataExecutionStarted {
|
||||||
executionId: string;
|
executionId: string;
|
||||||
|
@ -397,6 +397,11 @@ export interface IPushDataTestWebhook {
|
||||||
workflowId: string;
|
workflowId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IPushDataConsoleMessage {
|
||||||
|
source: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IN8nUISettings {
|
export interface IN8nUISettings {
|
||||||
endpointWebhook: string;
|
endpointWebhook: string;
|
||||||
endpointWebhookTest: string;
|
endpointWebhookTest: string;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
IExecutionsCurrentSummaryExtended,
|
IExecutionsCurrentSummaryExtended,
|
||||||
IPushData,
|
IPushData,
|
||||||
|
IPushDataConsoleMessage,
|
||||||
IPushDataExecutionFinished,
|
IPushDataExecutionFinished,
|
||||||
IPushDataExecutionStarted,
|
IPushDataExecutionStarted,
|
||||||
IPushDataNodeExecuteAfter,
|
IPushDataNodeExecuteAfter,
|
||||||
|
@ -161,6 +162,12 @@ export const pushConnection = mixins(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (receivedData.type === 'sendConsoleMessage') {
|
||||||
|
const pushData = receivedData.data as IPushDataConsoleMessage;
|
||||||
|
console.log(pushData.source, pushData.message); // eslint-disable-line no-console
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!['testWebhookReceived'].includes(receivedData.type) && isRetry !== true && this.pushMessageQueue.length) {
|
if (!['testWebhookReceived'].includes(receivedData.type) && isRetry !== true && this.pushMessageQueue.length) {
|
||||||
// If there are already messages in the queue add the new one that all of them
|
// If there are already messages in the queue add the new one that all of them
|
||||||
// get executed in order
|
// get executed in order
|
||||||
|
|
|
@ -60,8 +60,10 @@ export class Function implements INodeType {
|
||||||
// By default use data from first item
|
// By default use data from first item
|
||||||
Object.assign(sandbox, sandbox.$item(0));
|
Object.assign(sandbox, sandbox.$item(0));
|
||||||
|
|
||||||
|
const mode = this.getMode();
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
console: 'inherit',
|
console: (mode === 'manual') ? 'redirect' : 'inherit',
|
||||||
sandbox,
|
sandbox,
|
||||||
require: {
|
require: {
|
||||||
external: false as boolean | { modules: string[] },
|
external: false as boolean | { modules: string[] },
|
||||||
|
@ -77,9 +79,12 @@ export class Function implements INodeType {
|
||||||
options.require.external = { modules: process.env.NODE_FUNCTION_ALLOW_EXTERNAL.split(',') };
|
options.require.external = { modules: process.env.NODE_FUNCTION_ALLOW_EXTERNAL.split(',') };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const vm = new NodeVM(options);
|
const vm = new NodeVM(options);
|
||||||
|
|
||||||
|
if (mode === 'manual') {
|
||||||
|
vm.on('console.log', this.sendMessageToUI);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the code to execute
|
// Get the code to execute
|
||||||
const functionCode = this.getNodeParameter('functionCode', 0) as string;
|
const functionCode = this.getNodeParameter('functionCode', 0) as string;
|
||||||
|
|
||||||
|
|
|
@ -73,8 +73,10 @@ export class FunctionItem implements INodeType {
|
||||||
const dataProxy = this.getWorkflowDataProxy(itemIndex);
|
const dataProxy = this.getWorkflowDataProxy(itemIndex);
|
||||||
Object.assign(sandbox, dataProxy);
|
Object.assign(sandbox, dataProxy);
|
||||||
|
|
||||||
|
const mode = this.getMode();
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
console: 'inherit',
|
console: (mode === 'manual') ? 'redirect' : 'inherit',
|
||||||
sandbox,
|
sandbox,
|
||||||
require: {
|
require: {
|
||||||
external: false as boolean | { modules: string[] },
|
external: false as boolean | { modules: string[] },
|
||||||
|
@ -92,11 +94,13 @@ export class FunctionItem implements INodeType {
|
||||||
|
|
||||||
const vm = new NodeVM(options);
|
const vm = new NodeVM(options);
|
||||||
|
|
||||||
|
if (mode === 'manual') {
|
||||||
|
vm.on('console.log', this.sendMessageToUI);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the code to execute
|
// Get the code to execute
|
||||||
const functionCode = this.getNodeParameter('functionCode', itemIndex) as string;
|
const functionCode = this.getNodeParameter('functionCode', itemIndex) as string;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let jsonData: IDataObject;
|
let jsonData: IDataObject;
|
||||||
try {
|
try {
|
||||||
// Execute the function code
|
// Execute the function code
|
||||||
|
|
|
@ -223,6 +223,7 @@ export interface IExecuteFunctions {
|
||||||
getTimezone(): string;
|
getTimezone(): string;
|
||||||
getWorkflow(): IWorkflowMetadata;
|
getWorkflow(): IWorkflowMetadata;
|
||||||
prepareOutputData(outputData: INodeExecutionData[], outputIndex?: number): Promise<INodeExecutionData[][]>;
|
prepareOutputData(outputData: INodeExecutionData[], outputIndex?: number): Promise<INodeExecutionData[][]>;
|
||||||
|
sendMessageToUI(message: string): void;
|
||||||
helpers: {
|
helpers: {
|
||||||
[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
|
[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
|
||||||
};
|
};
|
||||||
|
@ -744,6 +745,7 @@ export interface IWorkflowExecuteAdditionalData {
|
||||||
httpResponse?: express.Response;
|
httpResponse?: express.Response;
|
||||||
httpRequest?: express.Request;
|
httpRequest?: express.Request;
|
||||||
restApiUrl: string;
|
restApiUrl: string;
|
||||||
|
sendMessageToUI?: (source: string, message: string) => void;
|
||||||
timezone: string;
|
timezone: string;
|
||||||
webhookBaseUrl: string;
|
webhookBaseUrl: string;
|
||||||
webhookTestBaseUrl: string;
|
webhookTestBaseUrl: string;
|
||||||
|
|
Loading…
Reference in a new issue