mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -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 {
|
||||
data: IPushDataExecutionFinished | IPushDataNodeExecuteAfter | IPushDataNodeExecuteBefore | IPushDataTestWebhook;
|
||||
data: IPushDataExecutionFinished | IPushDataNodeExecuteAfter | IPushDataNodeExecuteBefore | IPushDataTestWebhook | IPushDataConsoleMessage;
|
||||
type: IPushDataType;
|
||||
}
|
||||
|
||||
export type IPushDataType = 'executionFinished' | 'executionStarted' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'testWebhookDeleted' | 'testWebhookReceived';
|
||||
export type IPushDataType = 'executionFinished' | 'executionStarted' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'sendConsoleMessage' | 'testWebhookDeleted' | 'testWebhookReceived';
|
||||
|
||||
export interface IPushDataExecutionFinished {
|
||||
data: IRun;
|
||||
|
@ -376,6 +376,10 @@ export interface IPushDataTestWebhook {
|
|||
workflowId: string;
|
||||
}
|
||||
|
||||
export interface IPushDataConsoleMessage {
|
||||
source: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface IResponseCallbackData {
|
||||
data?: IDataObject | IDataObject[];
|
||||
|
|
|
@ -627,7 +627,7 @@ export async function executeWorkflow(workflowInfo: IExecuteWorkflowInfo, additi
|
|||
// If no timeout was given from the parent, then we use our timeout.
|
||||
subworkflowTimeout = Math.min(additionalData.executionTimeoutTimestamp || Number.MAX_SAFE_INTEGER, Date.now() + (workflowData.settings.executionTimeout as number * 1000));
|
||||
}
|
||||
|
||||
|
||||
additionalDataIntegrated.executionTimeoutTimestamp = subworkflowTimeout;
|
||||
|
||||
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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 });
|
||||
}
|
||||
|
||||
|
|
|
@ -182,6 +182,8 @@ export class WorkflowRunner {
|
|||
|
||||
additionalData.hooks = WorkflowExecuteAdditionalData.getWorkflowHooksMain(data, executionId, true);
|
||||
|
||||
additionalData.sendMessageToUI = WorkflowExecuteAdditionalData.sendMessageToUI.bind({sessionId: data.sessionId});
|
||||
|
||||
let workflowExecution: PCancelable<IRun>;
|
||||
if (data.executionData !== undefined) {
|
||||
Logger.debug(`Execution ID ${executionId} had Execution data. Running with payload.`, {executionId});
|
||||
|
@ -468,6 +470,9 @@ export class WorkflowRunner {
|
|||
clearTimeout(executionTimeout);
|
||||
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') {
|
||||
clearTimeout(executionTimeout);
|
||||
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);
|
||||
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;
|
||||
additionalData.executeWorkflow = async (workflowInfo: IExecuteWorkflowInfo, additionalData: IWorkflowExecuteAdditionalData, inputData?: INodeExecutionData[] | undefined): Promise<Array<INodeExecutionData[] | null> | IRun> => {
|
||||
const workflowData = await WorkflowExecuteAdditionalData.getWorkflowData(workflowInfo);
|
||||
|
|
|
@ -749,6 +749,18 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx
|
|||
return workflow.getStaticData(type, node);
|
||||
},
|
||||
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: {
|
||||
prepareBinaryData,
|
||||
request: requestPromiseWithDefaults,
|
||||
|
|
|
@ -752,6 +752,7 @@ export function WorkflowExecuteAdditionalData(waitPromise: IDeferredPromise<IRun
|
|||
credentialsHelper: new CredentialsHelper({}, ''),
|
||||
hooks: new WorkflowHooks(hookFunctions, 'trigger', '1', workflowData),
|
||||
executeWorkflow: async (workflowInfo: IExecuteWorkflowInfo): Promise<any> => {}, // tslint:disable-line:no-any
|
||||
sendMessageToUI: (message: string) => {},
|
||||
restApiUrl: '',
|
||||
encryptionKey: 'test',
|
||||
timezone: 'America/New_York',
|
||||
|
|
|
@ -356,11 +356,11 @@ export interface IExecutionDeleteFilter {
|
|||
}
|
||||
|
||||
export interface IPushData {
|
||||
data: IPushDataExecutionFinished | IPushDataNodeExecuteAfter | IPushDataNodeExecuteBefore | IPushDataTestWebhook;
|
||||
data: IPushDataExecutionFinished | IPushDataNodeExecuteAfter | IPushDataNodeExecuteBefore | IPushDataTestWebhook | IPushDataConsoleMessage;
|
||||
type: IPushDataType;
|
||||
}
|
||||
|
||||
export type IPushDataType = 'executionFinished' | 'executionStarted' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'testWebhookDeleted' | 'testWebhookReceived';
|
||||
export type IPushDataType = 'executionFinished' | 'executionStarted' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'sendConsoleMessage' | 'testWebhookDeleted' | 'testWebhookReceived';
|
||||
|
||||
export interface IPushDataExecutionStarted {
|
||||
executionId: string;
|
||||
|
@ -397,6 +397,11 @@ export interface IPushDataTestWebhook {
|
|||
workflowId: string;
|
||||
}
|
||||
|
||||
export interface IPushDataConsoleMessage {
|
||||
source: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface IN8nUISettings {
|
||||
endpointWebhook: string;
|
||||
endpointWebhookTest: string;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
IExecutionsCurrentSummaryExtended,
|
||||
IPushData,
|
||||
IPushDataConsoleMessage,
|
||||
IPushDataExecutionFinished,
|
||||
IPushDataExecutionStarted,
|
||||
IPushDataNodeExecuteAfter,
|
||||
|
@ -161,6 +162,12 @@ export const pushConnection = mixins(
|
|||
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 there are already messages in the queue add the new one that all of them
|
||||
// get executed in order
|
||||
|
|
|
@ -60,8 +60,10 @@ export class Function implements INodeType {
|
|||
// By default use data from first item
|
||||
Object.assign(sandbox, sandbox.$item(0));
|
||||
|
||||
const mode = this.getMode();
|
||||
|
||||
const options = {
|
||||
console: 'inherit',
|
||||
console: (mode === 'manual') ? 'redirect' : 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
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(',') };
|
||||
}
|
||||
|
||||
|
||||
const vm = new NodeVM(options);
|
||||
|
||||
if (mode === 'manual') {
|
||||
vm.on('console.log', this.sendMessageToUI);
|
||||
}
|
||||
|
||||
// Get the code to execute
|
||||
const functionCode = this.getNodeParameter('functionCode', 0) as string;
|
||||
|
||||
|
|
|
@ -73,8 +73,10 @@ export class FunctionItem implements INodeType {
|
|||
const dataProxy = this.getWorkflowDataProxy(itemIndex);
|
||||
Object.assign(sandbox, dataProxy);
|
||||
|
||||
const mode = this.getMode();
|
||||
|
||||
const options = {
|
||||
console: 'inherit',
|
||||
console: (mode === 'manual') ? 'redirect' : 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: false as boolean | { modules: string[] },
|
||||
|
@ -92,11 +94,13 @@ export class FunctionItem implements INodeType {
|
|||
|
||||
const vm = new NodeVM(options);
|
||||
|
||||
if (mode === 'manual') {
|
||||
vm.on('console.log', this.sendMessageToUI);
|
||||
}
|
||||
|
||||
// Get the code to execute
|
||||
const functionCode = this.getNodeParameter('functionCode', itemIndex) as string;
|
||||
|
||||
|
||||
|
||||
let jsonData: IDataObject;
|
||||
try {
|
||||
// Execute the function code
|
||||
|
|
|
@ -223,6 +223,7 @@ export interface IExecuteFunctions {
|
|||
getTimezone(): string;
|
||||
getWorkflow(): IWorkflowMetadata;
|
||||
prepareOutputData(outputData: INodeExecutionData[], outputIndex?: number): Promise<INodeExecutionData[][]>;
|
||||
sendMessageToUI(message: string): void;
|
||||
helpers: {
|
||||
[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
|
||||
};
|
||||
|
@ -744,6 +745,7 @@ export interface IWorkflowExecuteAdditionalData {
|
|||
httpResponse?: express.Response;
|
||||
httpRequest?: express.Request;
|
||||
restApiUrl: string;
|
||||
sendMessageToUI?: (source: string, message: string) => void;
|
||||
timezone: string;
|
||||
webhookBaseUrl: string;
|
||||
webhookTestBaseUrl: string;
|
||||
|
|
Loading…
Reference in a new issue