2023-04-24 03:18:24 -07:00
|
|
|
|
import type {
|
2023-02-17 01:54:07 -08:00
|
|
|
|
IExecutionResponse,
|
|
|
|
|
IExecutionsCurrentSummaryExtended,
|
|
|
|
|
IPushData,
|
|
|
|
|
IPushDataExecutionFinished,
|
|
|
|
|
} from '@/Interface';
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
2022-11-23 04:41:53 -08:00
|
|
|
|
import { externalHooks } from '@/mixins/externalHooks';
|
|
|
|
|
import { nodeHelpers } from '@/mixins/nodeHelpers';
|
2023-05-15 09:41:13 -07:00
|
|
|
|
import { useTitleChange, useToast } from '@/composables';
|
2022-11-23 04:41:53 -08:00
|
|
|
|
import { workflowHelpers } from '@/mixins/workflowHelpers';
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
2023-04-24 03:18:24 -07:00
|
|
|
|
import type {
|
2022-09-29 14:02:25 -07:00
|
|
|
|
ExpressionError,
|
|
|
|
|
IDataObject,
|
2022-07-20 07:24:03 -07:00
|
|
|
|
INodeTypeNameVersion,
|
2023-02-17 01:54:07 -08:00
|
|
|
|
IRun,
|
|
|
|
|
IRunExecutionData,
|
2022-09-29 14:02:25 -07:00
|
|
|
|
IWorkflowBase,
|
feat(editor, core, cli): implement new workflow experience (#4358)
* feat(ExecuteWorkflowTrigger node): Implement ExecuteWorkflowTrigger node (#4108)
* feat(ExecuteWorkflowTrigger node): Implement ExecuteWorkflowTrigger node
* feat(editor): Do not show duplicate button if canvas contains `maxNodes` amount of nodes
* feat(ManualTrigger node): Implement ManualTrigger node (#4110)
* feat(ManualTrigger node): Implement ManualTrigger node
* :memo: Remove generics doc items from ManualTrigger node
* feat(editor-ui): Trigger tab redesign (#4150)
* :construction: Begin with TriggerPanel implementation, add Other Trigger Nodes subcategory
* :construction: Extracted categorized categories/subcategory/nodes rendering into its own component — CategorizedItems, removed SubcategoryPanel, added translations
* :sparkles: Implement MainPanel background scrim
* :recycle: Move `categoriesWithNodes`, 'visibleNodeTypes` and 'categorizedItems` to store, implemented dynamic categories count based on `selectedType`
* :bug: Fix SlideTransition for all the NodeCreato panels
* :lipstick: Fix cursos for CategoryItem and NodeItem
* :bug: Make sure ALL_NODE_FILTER is always set when MainPanel is mounted
* :art: Address PR comments
* label: Use Array type for CategorizedItems props
* :label: Add proper types for Vue props
* 🎨 Use standard component registration for CategorizedItems inside TriggerHelperPanel
* 🎨 Use kebab case for main-panel and icon component
* :label: Improve types
* feat(editor-ui): Redesign search input inside node creator panel (#4204)
* :construction: Begin with TriggerPanel implementation, add Other Trigger Nodes subcategory
* :construction: Extracted categorized categories/subcategory/nodes rendering into its own component — CategorizedItems, removed SubcategoryPanel, added translations
* :sparkles: Implement MainPanel background scrim
* :recycle: Move `categoriesWithNodes`, 'visibleNodeTypes` and 'categorizedItems` to store, implemented dynamic categories count based on `selectedType`
* :bug: Fix SlideTransition for all the NodeCreato panels
* :lipstick: Fix cursos for CategoryItem and NodeItem
* :bug: Make sure ALL_NODE_FILTER is always set when MainPanel is mounted
* :art: Address PR comments
* label: Use Array type for CategorizedItems props
* :label: Add proper types for Vue props
* 🎨 Use standard component registration for CategorizedItems inside TriggerHelperPanel
* :sparkles: Redesign search input and unify usage of categorized items
* :label: Use lowercase "Boolean" as `isSearchVisible` computed return type
* :fire: Remove useless emit
* :sparkles: Implement no result view based on subcategory, minor fixes
* :art: Remove unused properties
* feat(node-email): Change EmailReadImap display name and name (#4239)
* feat(editor-ui): Implement "Choose a Triger" action and related behaviour (#4226)
* :sparkles: Implement "Choose a Triger" action and related behaviour
* :mute: Lint fix
* :recycle: Remove PlaceholderTrigger node, add a button instead
* :art: Merge onMouseEnter and onMouseLeave to a single function
* :bulb: Add comment
* :fire: Remove PlaceholderNode registration
* :art: Rename TriggerPlaceholderButton to CanvasAddButton
* :sparkles: Add method to unregister custom action and rework CanvasAddButton centering logic
* :art: Run `setRecenteredCanvasAddButtonPosition` on `CanvasAddButton` mount
* fix(editor): Fix selecting of node from node-creator panel by clicking
* :twisted_rightwards_arrows: Merge fixes
* fix(editor): Show execute workflow trigger instead of workflow trigger in the trigger helper panel
* feat(editor): Fix node creator panel slide transition (#4261)
* fix(editor): Fix node creator panel slide-in/slide-out transitions
* :art: Fix naming
* :art: Use kebab-case for transition component name
* feat(editor): Disable execution and show notice when user tries to run workflow without enabled triggers
* fix(editor): Address first batch of new WF experience review (#4279)
* fix(editor): Fix first batch of review items
* bug(editor): Fix nodeview canvas add button centering
* :mute: Fix linter errors
* bug(ManualTrigger Node): Fix manual trigger node execution
* fix(editor): Do not show canvas add button in execution or demo mode and prevent clicking if creator is open
* fix(editor): do not show pin data tooltip for manual trigger node
* fix(editor): do not use nodeViewOffset on zoomToFit
* :lipstick: Add margin for last node creator item and set font-weight to 700 for category title
* :sparkles: Position welcome note next to the added trigger node
* :bug: Remve always true welcome note
* feat(editor): Minor UI and UX tweaks (#4328)
* :lipstick: Make top viewport buttons less prominent
* :sparkles: Allow user to switch to all tabs if it contains filter results, move nodecreator state props to its own module
* :mute: Fix linting errors
* :mute: Fix linting errors
* :mute: Fix linting errors
* chore(build): Ping Turbo version to 1.5.5
* :lipstick: Minor traigger panel and node view style changes
* :speech_balloon: Update display name of execute workflow trigger
* feat(core, editor): Update subworkflow execution logic (#4269)
* :sparkles: Implement `findWorkflowStart`
* :zap: Extend `WorkflowOperationError`
* :zap: Add `WorkflowOperationError` to toast
* :blue_book: Extend interface
* :sparkles: Add `subworkflowExecutionError` to store
* :sparkles: Create `SubworkflowOperationError`
* :zap: Render subworkflow error as node error
* :truck: Move subworkflow start validation to `cli`
* :zap: Reset subworkflow execution error state
* :fire: Remove unused import
* :zap: Adjust CLI commands
* :fire: Remove unneeded check
* :fire: Remove stray log
* :zap: Simplify syntax
* :zap: Sort in case both Start and EWT present
* :recycle: Address Omar's feedback
* :fire: Remove unneeded lint exception
* :pencil2: Fix copy
* :shirt: Fix lint
* fix: moved find start node function to catchable place
Co-authored-by: Omar Ajoue <krynble@gmail.com>
* :lipstick: Change ExecuteWorkflow node to primary
* :sparkles: Allow user to navigate to all tab if it contains search results
* :bug: Fixed canvas control button while in demo, disable workflow activation for non-activavle nodes and revert zoomToFit bottom offset
* :fix: Do not chow request text if there's results
* :speech_balloon: Update noResults text
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
Co-authored-by: Omar Ajoue <krynble@gmail.com>
2022-10-18 05:23:22 -07:00
|
|
|
|
SubworkflowOperationError,
|
2023-05-15 09:41:13 -07:00
|
|
|
|
IExecuteContextData,
|
2022-07-20 07:24:03 -07:00
|
|
|
|
} from 'n8n-workflow';
|
2023-04-24 03:18:24 -07:00
|
|
|
|
import { TelemetryHelpers } from 'n8n-workflow';
|
2022-07-20 07:24:03 -07:00
|
|
|
|
|
2021-10-18 20:57:49 -07:00
|
|
|
|
import { WORKFLOW_SETTINGS_MODAL_KEY } from '@/constants';
|
2022-11-23 04:41:53 -08:00
|
|
|
|
import { getTriggerNodeServiceName } from '@/utils';
|
2023-04-06 06:32:45 -07:00
|
|
|
|
import { codeNodeEditorEventBus } from '@/event-bus';
|
2022-11-04 06:04:31 -07:00
|
|
|
|
import { mapStores } from 'pinia';
|
2023-05-05 01:41:54 -07:00
|
|
|
|
import { useUIStore } from '@/stores/ui.store';
|
|
|
|
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
|
|
|
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
|
|
|
|
import { useCredentialsStore } from '@/stores/credentials.store';
|
|
|
|
|
import { useSettingsStore } from '@/stores/settings.store';
|
2023-02-17 01:54:07 -08:00
|
|
|
|
import { parse } from 'flatted';
|
2023-05-05 01:41:54 -07:00
|
|
|
|
import { useSegment } from '@/stores/segment.store';
|
2023-05-15 09:41:13 -07:00
|
|
|
|
import { defineComponent } from 'vue';
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
2023-05-15 09:41:13 -07:00
|
|
|
|
export const pushConnection = defineComponent({
|
2023-04-21 06:48:07 -07:00
|
|
|
|
setup() {
|
|
|
|
|
return {
|
|
|
|
|
...useTitleChange(),
|
2023-05-15 09:41:13 -07:00
|
|
|
|
...useToast(),
|
2023-04-21 06:48:07 -07:00
|
|
|
|
};
|
|
|
|
|
},
|
2023-05-15 09:41:13 -07:00
|
|
|
|
mixins: [externalHooks, nodeHelpers, workflowHelpers],
|
2022-12-14 01:04:10 -08:00
|
|
|
|
data() {
|
|
|
|
|
return {
|
2023-02-10 06:02:47 -08:00
|
|
|
|
pushSource: null as WebSocket | EventSource | null,
|
2022-12-14 01:04:10 -08:00
|
|
|
|
reconnectTimeout: null as NodeJS.Timeout | null,
|
|
|
|
|
retryTimeout: null as NodeJS.Timeout | null,
|
|
|
|
|
pushMessageQueue: [] as Array<{ event: Event; retriesLeft: number }>,
|
2023-01-13 03:33:42 -08:00
|
|
|
|
connectRetries: 0,
|
|
|
|
|
lostConnection: false,
|
2022-12-14 01:04:10 -08:00
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
2023-02-10 06:02:47 -08:00
|
|
|
|
...mapStores(
|
|
|
|
|
useCredentialsStore,
|
|
|
|
|
useNodeTypesStore,
|
|
|
|
|
useUIStore,
|
|
|
|
|
useWorkflowsStore,
|
|
|
|
|
useSettingsStore,
|
2023-02-28 02:44:37 -08:00
|
|
|
|
useSegment,
|
2023-02-10 06:02:47 -08:00
|
|
|
|
),
|
2022-12-14 01:04:10 -08:00
|
|
|
|
sessionId(): string {
|
|
|
|
|
return this.rootStore.sessionId;
|
2019-06-23 03:35:23 -07:00
|
|
|
|
},
|
2022-12-14 01:04:10 -08:00
|
|
|
|
},
|
|
|
|
|
methods: {
|
2023-02-10 06:02:47 -08:00
|
|
|
|
attemptReconnect() {
|
|
|
|
|
this.pushConnect();
|
2019-06-23 03:35:23 -07:00
|
|
|
|
},
|
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
/**
|
2023-02-10 06:02:47 -08:00
|
|
|
|
* Connect to server to receive data via a WebSocket or EventSource
|
2022-12-14 01:04:10 -08:00
|
|
|
|
*/
|
|
|
|
|
pushConnect(): void {
|
2023-02-10 06:02:47 -08:00
|
|
|
|
// always close the previous connection so that we do not end up with multiple connections
|
2022-12-14 01:04:10 -08:00
|
|
|
|
this.pushDisconnect();
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
2023-02-10 06:02:47 -08:00
|
|
|
|
if (this.reconnectTimeout) {
|
|
|
|
|
clearTimeout(this.reconnectTimeout);
|
|
|
|
|
this.reconnectTimeout = null;
|
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
2023-02-10 06:02:47 -08:00
|
|
|
|
const useWebSockets = this.settingsStore.pushBackend === 'websocket';
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
2023-02-10 06:02:47 -08:00
|
|
|
|
const { getRestUrl: restUrl } = this.rootStore;
|
|
|
|
|
const url = `/push?sessionId=${this.sessionId}`;
|
2023-01-13 03:33:42 -08:00
|
|
|
|
|
2023-02-10 06:02:47 -08:00
|
|
|
|
if (useWebSockets) {
|
|
|
|
|
const { protocol, host } = window.location;
|
|
|
|
|
const baseUrl = restUrl.startsWith('http')
|
|
|
|
|
? restUrl.replace(/^http/, 'ws')
|
|
|
|
|
: `${protocol === 'https:' ? 'wss' : 'ws'}://${host + restUrl}`;
|
|
|
|
|
this.pushSource = new WebSocket(`${baseUrl}${url}`);
|
|
|
|
|
} else {
|
|
|
|
|
this.pushSource = new EventSource(`${restUrl}${url}`, { withCredentials: true });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.pushSource.addEventListener('open', this.onConnectionSuccess, false);
|
|
|
|
|
this.pushSource.addEventListener('message', this.pushMessageReceived, false);
|
|
|
|
|
this.pushSource.addEventListener(
|
|
|
|
|
useWebSockets ? 'close' : 'error',
|
|
|
|
|
this.onConnectionError,
|
2022-12-14 01:04:10 -08:00
|
|
|
|
false,
|
|
|
|
|
);
|
2023-02-10 06:02:47 -08:00
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
2023-02-10 06:02:47 -08:00
|
|
|
|
onConnectionSuccess() {
|
|
|
|
|
this.connectRetries = 0;
|
|
|
|
|
this.lostConnection = false;
|
|
|
|
|
this.rootStore.pushConnectionActive = true;
|
2023-02-17 01:54:07 -08:00
|
|
|
|
this.clearAllStickyNotifications();
|
2023-02-10 06:02:47 -08:00
|
|
|
|
this.pushSource?.removeEventListener('open', this.onConnectionSuccess);
|
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
2023-02-10 06:02:47 -08:00
|
|
|
|
onConnectionError() {
|
|
|
|
|
this.pushDisconnect();
|
|
|
|
|
this.connectRetries++;
|
2023-02-17 01:54:07 -08:00
|
|
|
|
this.reconnectTimeout = setTimeout(
|
|
|
|
|
this.attemptReconnect,
|
2023-05-03 05:28:38 -07:00
|
|
|
|
Math.min(this.connectRetries * 2000, 8000), // maximum 8 seconds backoff
|
2023-02-17 01:54:07 -08:00
|
|
|
|
);
|
2022-12-14 01:04:10 -08:00
|
|
|
|
},
|
2019-07-25 10:08:19 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
/**
|
|
|
|
|
* Close connection to server
|
|
|
|
|
*/
|
|
|
|
|
pushDisconnect(): void {
|
2023-02-10 06:02:47 -08:00
|
|
|
|
if (this.pushSource !== null) {
|
|
|
|
|
this.pushSource.removeEventListener('error', this.onConnectionError);
|
|
|
|
|
this.pushSource.removeEventListener('close', this.onConnectionError);
|
|
|
|
|
this.pushSource.removeEventListener('message', this.pushMessageReceived);
|
|
|
|
|
if (this.pushSource.readyState < 2) this.pushSource.close();
|
|
|
|
|
this.pushSource = null;
|
2022-12-14 01:04:10 -08:00
|
|
|
|
}
|
2023-02-10 06:02:47 -08:00
|
|
|
|
|
|
|
|
|
this.rootStore.pushConnectionActive = false;
|
2022-12-14 01:04:10 -08:00
|
|
|
|
},
|
2020-05-01 01:02:34 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
/**
|
|
|
|
|
* Sometimes the push message is faster as the result from
|
|
|
|
|
* the REST API so we do not know yet what execution ID
|
|
|
|
|
* is currently active. So internally resend the message
|
|
|
|
|
* a few more times
|
|
|
|
|
*/
|
|
|
|
|
queuePushMessage(event: Event, retryAttempts: number) {
|
|
|
|
|
this.pushMessageQueue.push({ event, retriesLeft: retryAttempts });
|
|
|
|
|
|
|
|
|
|
if (this.retryTimeout === null) {
|
|
|
|
|
this.retryTimeout = setTimeout(this.processWaitingPushMessages, 20);
|
|
|
|
|
}
|
|
|
|
|
},
|
2020-05-01 01:02:34 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
/**
|
|
|
|
|
* Process the push messages which are waiting in the queue
|
|
|
|
|
*/
|
|
|
|
|
processWaitingPushMessages() {
|
|
|
|
|
if (this.retryTimeout !== null) {
|
|
|
|
|
clearTimeout(this.retryTimeout);
|
|
|
|
|
this.retryTimeout = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const queueLength = this.pushMessageQueue.length;
|
|
|
|
|
for (let i = 0; i < queueLength; i++) {
|
|
|
|
|
const messageData = this.pushMessageQueue.shift();
|
|
|
|
|
|
|
|
|
|
if (this.pushMessageReceived(messageData!.event, true) === false) {
|
|
|
|
|
// Was not successful
|
|
|
|
|
messageData!.retriesLeft -= 1;
|
|
|
|
|
|
|
|
|
|
if (messageData!.retriesLeft > 0) {
|
|
|
|
|
// If still retries are left add it back and stop execution
|
|
|
|
|
this.pushMessageQueue.unshift(messageData!);
|
2020-05-01 01:02:34 -07:00
|
|
|
|
}
|
2022-12-14 01:04:10 -08:00
|
|
|
|
break;
|
2020-05-01 01:02:34 -07:00
|
|
|
|
}
|
2022-12-14 01:04:10 -08:00
|
|
|
|
}
|
2020-05-01 01:02:34 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
if (this.pushMessageQueue.length !== 0 && this.retryTimeout === null) {
|
|
|
|
|
this.retryTimeout = setTimeout(this.processWaitingPushMessages, 25);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Process a newly received message
|
|
|
|
|
*/
|
2023-02-17 01:54:07 -08:00
|
|
|
|
async pushMessageReceived(event: Event, isRetry?: boolean): Promise<boolean> {
|
2022-12-14 01:04:10 -08:00
|
|
|
|
const retryAttempts = 5;
|
|
|
|
|
let receivedData: IPushData;
|
|
|
|
|
try {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
receivedData = JSON.parse(event.data);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (receivedData.type === 'sendConsoleMessage') {
|
|
|
|
|
const pushData = receivedData.data;
|
|
|
|
|
console.log(pushData.source, ...pushData.messages); // 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
|
|
|
|
|
this.queuePushMessage(event, retryAttempts);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (receivedData.type === 'nodeExecuteAfter' || receivedData.type === 'nodeExecuteBefore') {
|
|
|
|
|
if (!this.uiStore.isActionActive('workflowRunning')) {
|
|
|
|
|
// No workflow is running so ignore the messages
|
|
|
|
|
return false;
|
2020-05-01 01:02:34 -07:00
|
|
|
|
}
|
2022-12-14 01:04:10 -08:00
|
|
|
|
const pushData = receivedData.data;
|
|
|
|
|
if (this.workflowsStore.activeExecutionId !== pushData.executionId) {
|
|
|
|
|
// The data is not for the currently active execution or
|
|
|
|
|
// we do not have the execution id yet.
|
|
|
|
|
if (isRetry !== true) {
|
|
|
|
|
this.queuePushMessage(event, retryAttempts);
|
|
|
|
|
}
|
2020-05-01 01:02:34 -07:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-12-14 01:04:10 -08:00
|
|
|
|
}
|
2020-05-01 01:02:34 -07:00
|
|
|
|
|
2023-02-17 01:54:07 -08:00
|
|
|
|
// recovered execution data is handled like executionFinished data, however for security reasons
|
|
|
|
|
// we need to fetch the data from the server again rather than push it to all clients
|
|
|
|
|
let recoveredPushData: IPushDataExecutionFinished | undefined = undefined;
|
|
|
|
|
if (receivedData.type === 'executionRecovered') {
|
|
|
|
|
const recoveredExecutionId = receivedData.data?.executionId;
|
|
|
|
|
const isWorkflowRunning = this.uiStore.isActionActive('workflowRunning');
|
|
|
|
|
if (isWorkflowRunning && this.workflowsStore.activeExecutionId === recoveredExecutionId) {
|
|
|
|
|
// pull execution data for the recovered execution from the server
|
|
|
|
|
const executionData = await this.workflowsStore.fetchExecutionDataById(
|
|
|
|
|
this.workflowsStore.activeExecutionId,
|
|
|
|
|
);
|
|
|
|
|
if (executionData?.data) {
|
|
|
|
|
// data comes in as 'flatten' object, so we need to parse it
|
|
|
|
|
executionData.data = parse(
|
|
|
|
|
executionData.data as unknown as string,
|
|
|
|
|
) as IRunExecutionData;
|
|
|
|
|
const iRunExecutionData: IRunExecutionData = {
|
|
|
|
|
startData: executionData.data?.startData,
|
|
|
|
|
resultData: executionData.data?.resultData ?? { runData: {} },
|
|
|
|
|
executionData: executionData.data?.executionData,
|
|
|
|
|
};
|
|
|
|
|
if (
|
|
|
|
|
this.workflowsStore.workflowExecutionData?.workflowId === executionData.workflowId
|
|
|
|
|
) {
|
|
|
|
|
const activeRunData =
|
|
|
|
|
this.workflowsStore.workflowExecutionData?.data?.resultData?.runData;
|
|
|
|
|
if (activeRunData) {
|
|
|
|
|
for (const key of Object.keys(activeRunData)) {
|
|
|
|
|
iRunExecutionData.resultData.runData[key] = activeRunData[key];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const iRun: IRun = {
|
|
|
|
|
data: iRunExecutionData,
|
|
|
|
|
finished: executionData.finished,
|
|
|
|
|
mode: executionData.mode,
|
|
|
|
|
waitTill: executionData.data?.waitTill,
|
|
|
|
|
startedAt: executionData.startedAt,
|
|
|
|
|
stoppedAt: executionData.stoppedAt,
|
|
|
|
|
status: 'crashed',
|
|
|
|
|
};
|
|
|
|
|
if (executionData.data) {
|
|
|
|
|
recoveredPushData = {
|
|
|
|
|
executionId: executionData.id,
|
|
|
|
|
data: iRun,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (receivedData.type === 'executionFinished' || receivedData.type === 'executionRecovered') {
|
2022-12-14 01:04:10 -08:00
|
|
|
|
// The workflow finished executing
|
2023-02-17 01:54:07 -08:00
|
|
|
|
let pushData: IPushDataExecutionFinished;
|
|
|
|
|
if (receivedData.type === 'executionRecovered' && recoveredPushData !== undefined) {
|
|
|
|
|
pushData = recoveredPushData as IPushDataExecutionFinished;
|
|
|
|
|
} else {
|
|
|
|
|
pushData = receivedData.data as IPushDataExecutionFinished;
|
|
|
|
|
}
|
2022-12-14 01:04:10 -08:00
|
|
|
|
|
2023-02-17 01:54:07 -08:00
|
|
|
|
if (this.workflowsStore.activeExecutionId === pushData.executionId) {
|
|
|
|
|
const activeRunData =
|
|
|
|
|
this.workflowsStore.workflowExecutionData?.data?.resultData?.runData;
|
|
|
|
|
if (activeRunData) {
|
|
|
|
|
for (const key of Object.keys(activeRunData)) {
|
|
|
|
|
if (
|
|
|
|
|
pushData.data.data.resultData.runData[key]?.[0]?.data?.main?.[0]?.[0]?.json
|
2023-04-03 06:04:59 -07:00
|
|
|
|
?.isArtificialRecoveredEventItem === true &&
|
2023-02-17 01:54:07 -08:00
|
|
|
|
activeRunData[key].length > 0
|
|
|
|
|
)
|
|
|
|
|
pushData.data.data.resultData.runData[key] = activeRunData[key];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this.workflowsStore.finishActiveExecution(pushData);
|
|
|
|
|
}
|
2021-05-29 11:41:25 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
if (!this.uiStore.isActionActive('workflowRunning')) {
|
|
|
|
|
// No workflow is running so ignore the messages
|
2020-05-01 01:02:34 -07:00
|
|
|
|
return false;
|
2019-06-23 03:35:23 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
if (this.workflowsStore.activeExecutionId !== pushData.executionId) {
|
|
|
|
|
// The workflow which did finish execution did either not get started
|
|
|
|
|
// by this session or we do not have the execution id yet.
|
|
|
|
|
if (isRetry !== true) {
|
|
|
|
|
this.queuePushMessage(event, retryAttempts);
|
2019-07-25 10:08:19 -07:00
|
|
|
|
}
|
2022-12-14 01:04:10 -08:00
|
|
|
|
return false;
|
2019-06-23 03:35:23 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
const runDataExecuted = pushData.data;
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
2023-05-15 09:41:13 -07:00
|
|
|
|
let runDataExecutedErrorMessage = this.getExecutionError(runDataExecuted.data);
|
2023-02-17 01:54:07 -08:00
|
|
|
|
|
|
|
|
|
if (pushData.data.status === 'crashed') {
|
|
|
|
|
runDataExecutedErrorMessage = this.$locale.baseText(
|
|
|
|
|
'pushConnection.executionFailed.message',
|
|
|
|
|
);
|
|
|
|
|
}
|
2019-07-24 05:25:30 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
const lineNumber =
|
|
|
|
|
runDataExecuted &&
|
|
|
|
|
runDataExecuted.data &&
|
|
|
|
|
runDataExecuted.data.resultData &&
|
|
|
|
|
runDataExecuted.data.resultData.error &&
|
|
|
|
|
runDataExecuted.data.resultData.error.lineNumber;
|
2019-07-24 05:25:30 -07:00
|
|
|
|
|
2023-04-06 06:32:45 -07:00
|
|
|
|
codeNodeEditorEventBus.emit('error-line-number', lineNumber || 'final');
|
2019-07-24 05:25:30 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
const workflow = this.getCurrentWorkflow();
|
|
|
|
|
if (runDataExecuted.waitTill !== undefined) {
|
|
|
|
|
const activeExecutionId = this.workflowsStore.activeExecutionId;
|
|
|
|
|
const workflowSettings = this.workflowsStore.workflowSettings;
|
|
|
|
|
const saveManualExecutions = this.rootStore.saveManualExecutions;
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
const isSavingExecutions =
|
|
|
|
|
workflowSettings.saveManualExecutions === undefined
|
|
|
|
|
? saveManualExecutions
|
|
|
|
|
: workflowSettings.saveManualExecutions;
|
2021-07-10 02:34:41 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
let action;
|
|
|
|
|
if (!isSavingExecutions) {
|
|
|
|
|
this.$root.$emit('registerGlobalLinkAction', 'open-settings', async () => {
|
|
|
|
|
if (this.workflowsStore.isNewWorkflow) await this.saveAsNewWorkflow();
|
|
|
|
|
this.uiStore.openModal(WORKFLOW_SETTINGS_MODAL_KEY);
|
|
|
|
|
});
|
2022-10-13 05:28:02 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
action =
|
|
|
|
|
'<a data-action="open-settings">Turn on saving manual executions</a> and run again to see what happened after this node.';
|
|
|
|
|
} else {
|
|
|
|
|
action = `<a href="/workflow/${workflow.id}/executions/${activeExecutionId}">View the execution</a> to see what happened after this node.`;
|
|
|
|
|
}
|
2022-10-13 05:28:02 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
// Workflow did start but had been put to wait
|
2023-04-21 06:48:07 -07:00
|
|
|
|
this.titleSet(workflow.name as string, 'IDLE');
|
2023-05-15 09:41:13 -07:00
|
|
|
|
this.showToast({
|
2022-12-14 01:04:10 -08:00
|
|
|
|
title: 'Workflow started waiting',
|
|
|
|
|
message: `${action} <a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.wait/" target="_blank">More info</a>`,
|
|
|
|
|
type: 'success',
|
|
|
|
|
duration: 0,
|
|
|
|
|
});
|
|
|
|
|
} else if (runDataExecuted.finished !== true) {
|
2023-04-21 06:48:07 -07:00
|
|
|
|
this.titleSet(workflow.name as string, 'ERROR');
|
2022-12-14 01:04:10 -08:00
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
runDataExecuted.data.resultData.error?.name === 'ExpressionError' &&
|
|
|
|
|
(runDataExecuted.data.resultData.error as ExpressionError).context.functionality ===
|
|
|
|
|
'pairedItem'
|
|
|
|
|
) {
|
|
|
|
|
const error = runDataExecuted.data.resultData.error as ExpressionError;
|
|
|
|
|
|
2023-05-15 09:41:13 -07:00
|
|
|
|
void this.getWorkflowDataToSave().then((workflowData) => {
|
2022-12-14 01:04:10 -08:00
|
|
|
|
const eventData: IDataObject = {
|
|
|
|
|
caused_by_credential: false,
|
|
|
|
|
error_message: error.description,
|
|
|
|
|
error_title: error.message,
|
|
|
|
|
error_type: error.context.type,
|
|
|
|
|
node_graph_string: JSON.stringify(
|
|
|
|
|
TelemetryHelpers.generateNodesGraph(
|
|
|
|
|
workflowData as IWorkflowBase,
|
|
|
|
|
this.getNodeTypes(),
|
|
|
|
|
).nodeGraph,
|
|
|
|
|
),
|
|
|
|
|
workflow_id: this.workflowsStore.workflowId,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
error.context.nodeCause &&
|
|
|
|
|
['no pairing info', 'invalid pairing info'].includes(error.context.type as string)
|
|
|
|
|
) {
|
|
|
|
|
const node = workflow.getNode(error.context.nodeCause as string);
|
|
|
|
|
|
|
|
|
|
if (node) {
|
|
|
|
|
eventData.is_pinned = !!workflow.getPinDataOfNode(node.name);
|
|
|
|
|
eventData.mode = node.parameters.mode;
|
|
|
|
|
eventData.node_type = node.type;
|
|
|
|
|
eventData.operation = node.parameters.operation;
|
|
|
|
|
eventData.resource = node.parameters.resource;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-22 00:23:37 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
this.$telemetry.track('Instance FE emitted paired item error', eventData);
|
|
|
|
|
});
|
|
|
|
|
}
|
2021-10-06 10:13:39 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
if (runDataExecuted.data.resultData.error?.name === 'SubworkflowOperationError') {
|
|
|
|
|
const error = runDataExecuted.data.resultData.error as SubworkflowOperationError;
|
2022-09-13 08:39:47 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
this.workflowsStore.subWorkflowExecutionError = error;
|
2021-09-22 00:23:37 -07:00
|
|
|
|
|
2023-05-15 09:41:13 -07:00
|
|
|
|
this.showMessage({
|
2022-12-14 01:04:10 -08:00
|
|
|
|
title: error.message,
|
|
|
|
|
message: error.description,
|
|
|
|
|
type: 'error',
|
2021-09-22 00:23:37 -07:00
|
|
|
|
duration: 0,
|
2021-08-21 05:11:32 -07:00
|
|
|
|
});
|
2022-12-14 01:04:10 -08:00
|
|
|
|
} else {
|
|
|
|
|
let title: string;
|
|
|
|
|
if (runDataExecuted.data.resultData.lastNodeExecuted) {
|
|
|
|
|
title = `Problem in node ‘${runDataExecuted.data.resultData.lastNodeExecuted}‘`;
|
|
|
|
|
} else {
|
|
|
|
|
title = 'Problem executing workflow';
|
2022-09-29 14:02:25 -07:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-15 09:41:13 -07:00
|
|
|
|
this.showMessage({
|
2022-12-14 01:04:10 -08:00
|
|
|
|
title,
|
|
|
|
|
message: runDataExecutedErrorMessage,
|
|
|
|
|
type: 'error',
|
|
|
|
|
duration: 0,
|
2023-05-22 06:09:29 -07:00
|
|
|
|
dangerouslyUseHTMLString: true,
|
2022-12-14 01:04:10 -08:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Workflow did execute without a problem
|
2023-04-21 06:48:07 -07:00
|
|
|
|
this.titleSet(workflow.name as string, 'IDLE');
|
2022-12-14 01:04:10 -08:00
|
|
|
|
|
|
|
|
|
const execution = this.workflowsStore.getWorkflowExecution;
|
|
|
|
|
if (execution && execution.executedNode) {
|
|
|
|
|
const node = this.workflowsStore.getNodeByName(execution.executedNode);
|
|
|
|
|
const nodeType = node && this.nodeTypesStore.getNodeType(node.type, node.typeVersion);
|
|
|
|
|
const nodeOutput =
|
|
|
|
|
execution &&
|
|
|
|
|
execution.executedNode &&
|
|
|
|
|
execution.data &&
|
|
|
|
|
execution.data.resultData &&
|
|
|
|
|
execution.data.resultData.runData &&
|
|
|
|
|
execution.data.resultData.runData[execution.executedNode];
|
2023-05-10 02:58:51 -07:00
|
|
|
|
if (nodeType && nodeType.polling && !nodeOutput) {
|
2023-05-15 09:41:13 -07:00
|
|
|
|
this.showMessage({
|
2022-12-14 01:04:10 -08:00
|
|
|
|
title: this.$locale.baseText('pushConnection.pollingNode.dataNotFound', {
|
|
|
|
|
interpolate: {
|
|
|
|
|
service: getTriggerNodeServiceName(nodeType),
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
message: this.$locale.baseText('pushConnection.pollingNode.dataNotFound.message', {
|
|
|
|
|
interpolate: {
|
|
|
|
|
service: getTriggerNodeServiceName(nodeType),
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
type: 'success',
|
feat(editor, core, cli): implement new workflow experience (#4358)
* feat(ExecuteWorkflowTrigger node): Implement ExecuteWorkflowTrigger node (#4108)
* feat(ExecuteWorkflowTrigger node): Implement ExecuteWorkflowTrigger node
* feat(editor): Do not show duplicate button if canvas contains `maxNodes` amount of nodes
* feat(ManualTrigger node): Implement ManualTrigger node (#4110)
* feat(ManualTrigger node): Implement ManualTrigger node
* :memo: Remove generics doc items from ManualTrigger node
* feat(editor-ui): Trigger tab redesign (#4150)
* :construction: Begin with TriggerPanel implementation, add Other Trigger Nodes subcategory
* :construction: Extracted categorized categories/subcategory/nodes rendering into its own component — CategorizedItems, removed SubcategoryPanel, added translations
* :sparkles: Implement MainPanel background scrim
* :recycle: Move `categoriesWithNodes`, 'visibleNodeTypes` and 'categorizedItems` to store, implemented dynamic categories count based on `selectedType`
* :bug: Fix SlideTransition for all the NodeCreato panels
* :lipstick: Fix cursos for CategoryItem and NodeItem
* :bug: Make sure ALL_NODE_FILTER is always set when MainPanel is mounted
* :art: Address PR comments
* label: Use Array type for CategorizedItems props
* :label: Add proper types for Vue props
* 🎨 Use standard component registration for CategorizedItems inside TriggerHelperPanel
* 🎨 Use kebab case for main-panel and icon component
* :label: Improve types
* feat(editor-ui): Redesign search input inside node creator panel (#4204)
* :construction: Begin with TriggerPanel implementation, add Other Trigger Nodes subcategory
* :construction: Extracted categorized categories/subcategory/nodes rendering into its own component — CategorizedItems, removed SubcategoryPanel, added translations
* :sparkles: Implement MainPanel background scrim
* :recycle: Move `categoriesWithNodes`, 'visibleNodeTypes` and 'categorizedItems` to store, implemented dynamic categories count based on `selectedType`
* :bug: Fix SlideTransition for all the NodeCreato panels
* :lipstick: Fix cursos for CategoryItem and NodeItem
* :bug: Make sure ALL_NODE_FILTER is always set when MainPanel is mounted
* :art: Address PR comments
* label: Use Array type for CategorizedItems props
* :label: Add proper types for Vue props
* 🎨 Use standard component registration for CategorizedItems inside TriggerHelperPanel
* :sparkles: Redesign search input and unify usage of categorized items
* :label: Use lowercase "Boolean" as `isSearchVisible` computed return type
* :fire: Remove useless emit
* :sparkles: Implement no result view based on subcategory, minor fixes
* :art: Remove unused properties
* feat(node-email): Change EmailReadImap display name and name (#4239)
* feat(editor-ui): Implement "Choose a Triger" action and related behaviour (#4226)
* :sparkles: Implement "Choose a Triger" action and related behaviour
* :mute: Lint fix
* :recycle: Remove PlaceholderTrigger node, add a button instead
* :art: Merge onMouseEnter and onMouseLeave to a single function
* :bulb: Add comment
* :fire: Remove PlaceholderNode registration
* :art: Rename TriggerPlaceholderButton to CanvasAddButton
* :sparkles: Add method to unregister custom action and rework CanvasAddButton centering logic
* :art: Run `setRecenteredCanvasAddButtonPosition` on `CanvasAddButton` mount
* fix(editor): Fix selecting of node from node-creator panel by clicking
* :twisted_rightwards_arrows: Merge fixes
* fix(editor): Show execute workflow trigger instead of workflow trigger in the trigger helper panel
* feat(editor): Fix node creator panel slide transition (#4261)
* fix(editor): Fix node creator panel slide-in/slide-out transitions
* :art: Fix naming
* :art: Use kebab-case for transition component name
* feat(editor): Disable execution and show notice when user tries to run workflow without enabled triggers
* fix(editor): Address first batch of new WF experience review (#4279)
* fix(editor): Fix first batch of review items
* bug(editor): Fix nodeview canvas add button centering
* :mute: Fix linter errors
* bug(ManualTrigger Node): Fix manual trigger node execution
* fix(editor): Do not show canvas add button in execution or demo mode and prevent clicking if creator is open
* fix(editor): do not show pin data tooltip for manual trigger node
* fix(editor): do not use nodeViewOffset on zoomToFit
* :lipstick: Add margin for last node creator item and set font-weight to 700 for category title
* :sparkles: Position welcome note next to the added trigger node
* :bug: Remve always true welcome note
* feat(editor): Minor UI and UX tweaks (#4328)
* :lipstick: Make top viewport buttons less prominent
* :sparkles: Allow user to switch to all tabs if it contains filter results, move nodecreator state props to its own module
* :mute: Fix linting errors
* :mute: Fix linting errors
* :mute: Fix linting errors
* chore(build): Ping Turbo version to 1.5.5
* :lipstick: Minor traigger panel and node view style changes
* :speech_balloon: Update display name of execute workflow trigger
* feat(core, editor): Update subworkflow execution logic (#4269)
* :sparkles: Implement `findWorkflowStart`
* :zap: Extend `WorkflowOperationError`
* :zap: Add `WorkflowOperationError` to toast
* :blue_book: Extend interface
* :sparkles: Add `subworkflowExecutionError` to store
* :sparkles: Create `SubworkflowOperationError`
* :zap: Render subworkflow error as node error
* :truck: Move subworkflow start validation to `cli`
* :zap: Reset subworkflow execution error state
* :fire: Remove unused import
* :zap: Adjust CLI commands
* :fire: Remove unneeded check
* :fire: Remove stray log
* :zap: Simplify syntax
* :zap: Sort in case both Start and EWT present
* :recycle: Address Omar's feedback
* :fire: Remove unneeded lint exception
* :pencil2: Fix copy
* :shirt: Fix lint
* fix: moved find start node function to catchable place
Co-authored-by: Omar Ajoue <krynble@gmail.com>
* :lipstick: Change ExecuteWorkflow node to primary
* :sparkles: Allow user to navigate to all tab if it contains search results
* :bug: Fixed canvas control button while in demo, disable workflow activation for non-activavle nodes and revert zoomToFit bottom offset
* :fix: Do not chow request text if there's results
* :speech_balloon: Update noResults text
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
Co-authored-by: Omar Ajoue <krynble@gmail.com>
2022-10-18 05:23:22 -07:00
|
|
|
|
});
|
2022-09-29 14:02:25 -07:00
|
|
|
|
} else {
|
2023-05-15 09:41:13 -07:00
|
|
|
|
this.showMessage({
|
2022-12-14 01:04:10 -08:00
|
|
|
|
title: this.$locale.baseText('pushConnection.nodeExecutedSuccessfully'),
|
2022-05-23 08:56:15 -07:00
|
|
|
|
type: 'success',
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-12-14 01:04:10 -08:00
|
|
|
|
} else {
|
2023-05-15 09:41:13 -07:00
|
|
|
|
this.showMessage({
|
2022-12-14 01:04:10 -08:00
|
|
|
|
title: this.$locale.baseText('pushConnection.workflowExecutedSuccessfully'),
|
|
|
|
|
type: 'success',
|
|
|
|
|
});
|
2019-06-23 03:35:23 -07:00
|
|
|
|
}
|
2022-12-14 01:04:10 -08:00
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
// It does not push the runData as it got already pushed with each
|
|
|
|
|
// node that did finish. For that reason copy in here the data
|
|
|
|
|
// which we already have.
|
|
|
|
|
if (this.workflowsStore.getWorkflowRunData) {
|
|
|
|
|
runDataExecuted.data.resultData.runData = this.workflowsStore.getWorkflowRunData;
|
|
|
|
|
}
|
2020-05-01 01:02:34 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
this.workflowsStore.executingNode = null;
|
|
|
|
|
this.workflowsStore.setWorkflowExecutionData(runDataExecuted as IExecutionResponse);
|
|
|
|
|
this.uiStore.removeActiveAction('workflowRunning');
|
|
|
|
|
|
|
|
|
|
// Set the node execution issues on all the nodes which produced an error so that
|
|
|
|
|
// it can be displayed in the node-view
|
|
|
|
|
this.updateNodesExecutionIssues();
|
|
|
|
|
|
|
|
|
|
const lastNodeExecuted: string | undefined =
|
|
|
|
|
runDataExecuted.data.resultData.lastNodeExecuted;
|
|
|
|
|
let itemsCount = 0;
|
|
|
|
|
if (
|
|
|
|
|
lastNodeExecuted &&
|
|
|
|
|
runDataExecuted.data.resultData.runData[lastNodeExecuted as string] &&
|
|
|
|
|
!runDataExecutedErrorMessage
|
|
|
|
|
) {
|
|
|
|
|
itemsCount =
|
|
|
|
|
runDataExecuted.data.resultData.runData[lastNodeExecuted as string][0].data!.main[0]!
|
|
|
|
|
.length;
|
|
|
|
|
}
|
2022-07-20 07:24:03 -07:00
|
|
|
|
|
2023-05-15 09:41:13 -07:00
|
|
|
|
void this.$externalHooks().run('pushConnection.executionFinished', {
|
2022-12-14 01:04:10 -08:00
|
|
|
|
itemsCount,
|
|
|
|
|
nodeName: runDataExecuted.data.resultData.lastNodeExecuted,
|
|
|
|
|
errorMessage: runDataExecutedErrorMessage,
|
|
|
|
|
runDataExecutedStartData: runDataExecuted.data.startData,
|
|
|
|
|
resultDataError: runDataExecuted.data.resultData.error,
|
|
|
|
|
});
|
2023-02-28 02:44:37 -08:00
|
|
|
|
if (!runDataExecuted.data.resultData.error) {
|
|
|
|
|
this.segmentStore.trackSuccessfulWorkflowExecution(runDataExecuted);
|
|
|
|
|
}
|
2022-12-14 01:04:10 -08:00
|
|
|
|
} else if (receivedData.type === 'executionStarted') {
|
|
|
|
|
const pushData = receivedData.data;
|
|
|
|
|
|
|
|
|
|
const executionData: IExecutionsCurrentSummaryExtended = {
|
|
|
|
|
id: pushData.executionId,
|
|
|
|
|
finished: false,
|
|
|
|
|
mode: pushData.mode,
|
|
|
|
|
startedAt: pushData.startedAt,
|
|
|
|
|
retryOf: pushData.retryOf,
|
|
|
|
|
workflowId: pushData.workflowId,
|
|
|
|
|
workflowName: pushData.workflowName,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.workflowsStore.addActiveExecution(executionData);
|
|
|
|
|
} else if (receivedData.type === 'nodeExecuteAfter') {
|
|
|
|
|
// A node finished to execute. Add its data
|
|
|
|
|
const pushData = receivedData.data;
|
|
|
|
|
this.workflowsStore.addNodeExecutionData(pushData);
|
|
|
|
|
} else if (receivedData.type === 'nodeExecuteBefore') {
|
|
|
|
|
// A node started to be executed. Set it as executing.
|
|
|
|
|
const pushData = receivedData.data;
|
|
|
|
|
this.workflowsStore.executingNode = pushData.nodeName;
|
|
|
|
|
} else if (receivedData.type === 'testWebhookDeleted') {
|
|
|
|
|
// A test-webhook was deleted
|
|
|
|
|
const pushData = receivedData.data;
|
|
|
|
|
|
|
|
|
|
if (pushData.workflowId === this.workflowsStore.workflowId) {
|
|
|
|
|
this.workflowsStore.executionWaitingForWebhook = false;
|
|
|
|
|
this.uiStore.removeActiveAction('workflowRunning');
|
|
|
|
|
}
|
|
|
|
|
} else if (receivedData.type === 'testWebhookReceived') {
|
|
|
|
|
// A test-webhook did get called
|
|
|
|
|
const pushData = receivedData.data;
|
2022-07-20 07:24:03 -07:00
|
|
|
|
|
2022-12-14 01:04:10 -08:00
|
|
|
|
if (pushData.workflowId === this.workflowsStore.workflowId) {
|
|
|
|
|
this.workflowsStore.executionWaitingForWebhook = false;
|
|
|
|
|
this.workflowsStore.activeExecutionId = pushData.executionId;
|
2019-06-23 03:35:23 -07:00
|
|
|
|
}
|
2022-12-14 01:04:10 -08:00
|
|
|
|
|
|
|
|
|
this.processWaitingPushMessages();
|
|
|
|
|
} else if (receivedData.type === 'reloadNodeType') {
|
2023-05-10 08:10:03 -07:00
|
|
|
|
await this.nodeTypesStore.getNodeTypes();
|
|
|
|
|
await this.nodeTypesStore.getFullNodesProperties([receivedData.data]);
|
2022-12-14 01:04:10 -08:00
|
|
|
|
} else if (receivedData.type === 'removeNodeType') {
|
|
|
|
|
const pushData = receivedData.data;
|
|
|
|
|
|
|
|
|
|
const nodesToBeRemoved: INodeTypeNameVersion[] = [pushData];
|
|
|
|
|
|
|
|
|
|
// Force reload of all credential types
|
2023-05-10 08:10:03 -07:00
|
|
|
|
await this.credentialsStore.fetchCredentialTypes(false).then(() => {
|
2022-12-14 01:04:10 -08:00
|
|
|
|
this.nodeTypesStore.removeNodeTypes(nodesToBeRemoved);
|
|
|
|
|
});
|
2023-02-08 10:26:07 -08:00
|
|
|
|
} else if (receivedData.type === 'nodeDescriptionUpdated') {
|
2023-05-10 08:10:03 -07:00
|
|
|
|
await this.nodeTypesStore.getNodeTypes();
|
|
|
|
|
await this.credentialsStore.fetchCredentialTypes(true);
|
2022-12-14 01:04:10 -08:00
|
|
|
|
}
|
|
|
|
|
return true;
|
2019-06-23 03:35:23 -07:00
|
|
|
|
},
|
2023-05-15 09:41:13 -07:00
|
|
|
|
getExecutionError(data: IRunExecutionData | IExecuteContextData) {
|
|
|
|
|
const error = data.resultData.error;
|
|
|
|
|
|
|
|
|
|
let errorMessage: string;
|
|
|
|
|
|
|
|
|
|
if (data.resultData.lastNodeExecuted && error) {
|
|
|
|
|
errorMessage = error.message || error.description;
|
|
|
|
|
} else {
|
|
|
|
|
errorMessage = this.$locale.baseText('pushConnection.executionError', {
|
|
|
|
|
interpolate: { error: '!' },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (error && error.message) {
|
|
|
|
|
let nodeName: string | undefined;
|
|
|
|
|
if ('node' in error) {
|
|
|
|
|
nodeName = typeof error.node === 'string' ? error.node : error.node!.name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const receivedError = nodeName ? `${nodeName}: ${error.message}` : error.message;
|
|
|
|
|
errorMessage = this.$locale.baseText('pushConnection.executionError', {
|
|
|
|
|
interpolate: {
|
|
|
|
|
error: `.${this.$locale.baseText('pushConnection.executionError.details', {
|
|
|
|
|
interpolate: {
|
|
|
|
|
details: receivedError,
|
|
|
|
|
},
|
|
|
|
|
})}`,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return errorMessage;
|
|
|
|
|
},
|
2022-12-14 01:04:10 -08:00
|
|
|
|
},
|
|
|
|
|
});
|