2022-04-11 06:12:13 -07:00
|
|
|
<template>
|
2023-08-10 01:09:55 -07:00
|
|
|
<div>
|
2023-07-28 07:59:06 -07:00
|
|
|
<n8n-tooltip placement="bottom" :disabled="!disabledHint">
|
|
|
|
<template #content>
|
|
|
|
<div>{{ disabledHint }}</div>
|
|
|
|
</template>
|
|
|
|
<div>
|
|
|
|
<n8n-button
|
|
|
|
v-bind="$attrs"
|
|
|
|
:loading="nodeRunning && !isListeningForEvents && !isListeningForWorkflowEvents"
|
|
|
|
:disabled="disabled || !!disabledHint"
|
|
|
|
:label="buttonLabel"
|
|
|
|
:type="type"
|
|
|
|
:size="size"
|
2024-02-27 01:29:16 -08:00
|
|
|
:icon="!isListeningForEvents && !hideIcon && 'flask'"
|
2023-12-28 00:49:58 -08:00
|
|
|
:transparent-background="transparent"
|
2024-01-05 07:23:51 -08:00
|
|
|
:title="!isTriggerNode ? $locale.baseText('ndv.execute.testNode.description') : ''"
|
2024-02-12 01:45:05 -08:00
|
|
|
@click="onClick"
|
2023-07-28 07:59:06 -07:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</n8n-tooltip>
|
2023-08-10 01:09:55 -07:00
|
|
|
</div>
|
2022-04-11 06:12:13 -07:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<script lang="ts">
|
2023-05-19 06:31:16 -07:00
|
|
|
import { defineComponent } from 'vue';
|
|
|
|
import { mapStores } from 'pinia';
|
2023-10-16 21:09:30 -07:00
|
|
|
import {
|
|
|
|
WEBHOOK_NODE_TYPE,
|
|
|
|
MANUAL_TRIGGER_NODE_TYPE,
|
|
|
|
MODAL_CONFIRM,
|
|
|
|
FORM_TRIGGER_NODE_TYPE,
|
2024-01-09 03:11:39 -08:00
|
|
|
CHAT_TRIGGER_NODE_TYPE,
|
2023-10-16 21:09:30 -07:00
|
|
|
} from '@/constants';
|
2023-04-24 03:18:24 -07:00
|
|
|
import type { INodeUi } from '@/Interface';
|
|
|
|
import type { INodeTypeDescription } from 'n8n-workflow';
|
2022-11-23 04:41:53 -08:00
|
|
|
import { workflowRun } from '@/mixins/workflowRun';
|
2023-05-05 01:41:54 -07:00
|
|
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
|
|
|
import { useNDVStore } from '@/stores/ndv.store';
|
|
|
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
2023-11-28 03:15:08 -08:00
|
|
|
import { useMessage } from '@/composables/useMessage';
|
|
|
|
import { useToast } from '@/composables/useToast';
|
2023-12-06 07:28:09 -08:00
|
|
|
import { useExternalHooks } from '@/composables/useExternalHooks';
|
2024-01-09 03:11:39 -08:00
|
|
|
import { nodeViewEventBus } from '@/event-bus';
|
2024-01-04 01:22:56 -08:00
|
|
|
import { usePinnedData } from '@/composables/usePinnedData';
|
2022-04-11 06:12:13 -07:00
|
|
|
|
2023-05-15 09:41:13 -07:00
|
|
|
export default defineComponent({
|
2024-01-04 01:22:56 -08:00
|
|
|
mixins: [workflowRun],
|
2023-12-28 00:49:58 -08:00
|
|
|
inheritAttrs: false,
|
2022-04-11 06:12:13 -07:00
|
|
|
props: {
|
|
|
|
nodeName: {
|
|
|
|
type: String,
|
2024-01-04 01:22:56 -08:00
|
|
|
required: true,
|
2022-04-11 06:12:13 -07:00
|
|
|
},
|
2022-07-20 08:50:39 -07:00
|
|
|
disabled: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
2022-05-23 08:56:15 -07:00
|
|
|
label: {
|
|
|
|
type: String,
|
|
|
|
},
|
|
|
|
type: {
|
|
|
|
type: String,
|
|
|
|
},
|
|
|
|
size: {
|
|
|
|
type: String,
|
|
|
|
},
|
|
|
|
transparent: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
2022-06-20 12:39:24 -07:00
|
|
|
telemetrySource: {
|
|
|
|
type: String,
|
|
|
|
},
|
2024-02-27 01:29:16 -08:00
|
|
|
hideIcon: {
|
|
|
|
type: Boolean,
|
|
|
|
},
|
2022-04-11 06:12:13 -07:00
|
|
|
},
|
2023-12-06 07:28:09 -08:00
|
|
|
setup(props, ctx) {
|
2024-01-04 01:22:56 -08:00
|
|
|
const workflowsStore = useWorkflowsStore();
|
|
|
|
const node = workflowsStore.getNodeByName(props.nodeName);
|
|
|
|
const pinnedData = usePinnedData(node);
|
2023-12-06 07:28:09 -08:00
|
|
|
const externalHooks = useExternalHooks();
|
|
|
|
|
2023-05-15 09:41:13 -07:00
|
|
|
return {
|
2023-12-06 07:28:09 -08:00
|
|
|
externalHooks,
|
2024-01-04 01:22:56 -08:00
|
|
|
pinnedData,
|
2023-05-15 09:41:13 -07:00
|
|
|
...useToast(),
|
|
|
|
...useMessage(),
|
2023-07-28 00:51:07 -07:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
2023-12-06 07:28:09 -08:00
|
|
|
...workflowRun.setup?.(props, ctx),
|
2023-05-15 09:41:13 -07:00
|
|
|
};
|
|
|
|
},
|
2022-04-11 06:12:13 -07:00
|
|
|
computed: {
|
2022-11-04 06:04:31 -07:00
|
|
|
...mapStores(useNodeTypesStore, useNDVStore, useWorkflowsStore),
|
|
|
|
node(): INodeUi | null {
|
|
|
|
return this.workflowsStore.getNodeByName(this.nodeName);
|
2022-04-11 06:12:13 -07:00
|
|
|
},
|
|
|
|
nodeType(): INodeTypeDescription | null {
|
|
|
|
if (this.node) {
|
2022-11-04 06:04:31 -07:00
|
|
|
return this.nodeTypesStore.getNodeType(this.node.type, this.node.typeVersion);
|
2022-04-11 06:12:13 -07:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
2022-05-23 08:56:15 -07:00
|
|
|
nodeRunning(): boolean {
|
2022-11-04 06:04:31 -07:00
|
|
|
const triggeredNode = this.workflowsStore.executedNode;
|
2022-06-02 07:13:07 -07:00
|
|
|
return (
|
|
|
|
this.workflowRunning &&
|
2023-10-02 08:33:43 -07:00
|
|
|
(this.workflowsStore.isNodeExecuting(this.node.name) || triggeredNode === this.node.name)
|
2022-06-02 07:13:07 -07:00
|
|
|
);
|
2022-05-23 08:56:15 -07:00
|
|
|
},
|
2022-04-11 06:12:13 -07:00
|
|
|
workflowRunning(): boolean {
|
2022-11-04 06:04:31 -07:00
|
|
|
return this.uiStore.isActionActive('workflowRunning');
|
2022-04-11 06:12:13 -07:00
|
|
|
},
|
|
|
|
isTriggerNode(): boolean {
|
2023-10-20 01:52:56 -07:00
|
|
|
if (!this.node) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-11-04 06:04:31 -07:00
|
|
|
return this.nodeTypesStore.isTriggerNode(this.node.type);
|
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
|
|
|
},
|
|
|
|
isManualTriggerNode(): boolean {
|
|
|
|
return Boolean(this.nodeType && this.nodeType.name === MANUAL_TRIGGER_NODE_TYPE);
|
2022-04-11 06:12:13 -07:00
|
|
|
},
|
2024-01-09 03:11:39 -08:00
|
|
|
isChatNode(): boolean {
|
|
|
|
return Boolean(this.nodeType && this.nodeType.name === CHAT_TRIGGER_NODE_TYPE);
|
|
|
|
},
|
2023-10-16 21:09:30 -07:00
|
|
|
isFormTriggerNode(): boolean {
|
|
|
|
return Boolean(this.nodeType && this.nodeType.name === FORM_TRIGGER_NODE_TYPE);
|
|
|
|
},
|
2022-04-11 06:12:13 -07:00
|
|
|
isPollingTypeNode(): boolean {
|
2023-08-29 03:53:29 -07:00
|
|
|
return !!this.nodeType?.polling;
|
2022-04-11 06:12:13 -07:00
|
|
|
},
|
|
|
|
isScheduleTrigger(): boolean {
|
2023-08-29 03:53:29 -07:00
|
|
|
return !!this.nodeType?.group.includes('schedule');
|
2022-04-11 06:12:13 -07:00
|
|
|
},
|
2022-06-20 12:39:24 -07:00
|
|
|
isWebhookNode(): boolean {
|
|
|
|
return Boolean(this.nodeType && this.nodeType.name === WEBHOOK_NODE_TYPE);
|
|
|
|
},
|
|
|
|
isListeningForEvents(): boolean {
|
2022-11-04 06:04:31 -07:00
|
|
|
const waitingOnWebhook = this.workflowsStore.executionWaitingForWebhook;
|
|
|
|
const executedNode = this.workflowsStore.executedNode;
|
2022-06-20 12:39:24 -07:00
|
|
|
|
|
|
|
return (
|
|
|
|
this.node &&
|
|
|
|
!this.node.disabled &&
|
|
|
|
this.isTriggerNode &&
|
|
|
|
waitingOnWebhook &&
|
|
|
|
(!executedNode || executedNode === this.nodeName)
|
|
|
|
);
|
|
|
|
},
|
2022-10-31 10:59:53 -07:00
|
|
|
isListeningForWorkflowEvents(): boolean {
|
|
|
|
return (
|
|
|
|
this.nodeRunning &&
|
|
|
|
this.isTriggerNode &&
|
|
|
|
!this.isScheduleTrigger &&
|
|
|
|
!this.isManualTriggerNode
|
|
|
|
);
|
|
|
|
},
|
2022-06-20 12:39:24 -07:00
|
|
|
hasIssues(): boolean {
|
|
|
|
return Boolean(
|
2023-08-29 03:53:29 -07:00
|
|
|
this.node?.issues && (this.node.issues.parameters || this.node.issues.credentials),
|
2022-06-20 12:39:24 -07:00
|
|
|
);
|
|
|
|
},
|
|
|
|
disabledHint(): string {
|
|
|
|
if (this.isListeningForEvents) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.isTriggerNode && this.node.disabled) {
|
|
|
|
return this.$locale.baseText('ndv.execute.nodeIsDisabled');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.isTriggerNode && this.hasIssues) {
|
2022-11-04 06:04:31 -07:00
|
|
|
const activeNode = this.ndvStore.activeNode;
|
|
|
|
if (activeNode && activeNode.name !== this.nodeName) {
|
2022-06-20 12:39:24 -07:00
|
|
|
return this.$locale.baseText('ndv.execute.fixPrevious');
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.$locale.baseText('ndv.execute.requiredFieldsMissing');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.workflowRunning && !this.nodeRunning) {
|
|
|
|
return this.$locale.baseText('ndv.execute.workflowAlreadyRunning');
|
|
|
|
}
|
|
|
|
|
|
|
|
return '';
|
|
|
|
},
|
2022-05-23 08:56:15 -07:00
|
|
|
buttonLabel(): string {
|
2022-10-31 10:59:53 -07:00
|
|
|
if (this.isListeningForEvents || this.isListeningForWorkflowEvents) {
|
2022-06-20 12:39:24 -07:00
|
|
|
return this.$locale.baseText('ndv.execute.stopListening');
|
|
|
|
}
|
|
|
|
|
2022-05-23 08:56:15 -07:00
|
|
|
if (this.label) {
|
|
|
|
return this.label;
|
|
|
|
}
|
2022-06-20 12:39:24 -07:00
|
|
|
|
2024-01-09 03:11:39 -08:00
|
|
|
if (this.isChatNode) {
|
|
|
|
return this.$locale.baseText('ndv.execute.testChat');
|
|
|
|
}
|
|
|
|
|
2022-06-20 12:39:24 -07:00
|
|
|
if (this.isWebhookNode) {
|
|
|
|
return this.$locale.baseText('ndv.execute.listenForTestEvent');
|
|
|
|
}
|
|
|
|
|
2023-10-16 21:09:30 -07:00
|
|
|
if (this.isFormTriggerNode) {
|
|
|
|
return this.$locale.baseText('ndv.execute.testStep');
|
|
|
|
}
|
|
|
|
|
2023-08-29 03:53:29 -07:00
|
|
|
if (this.isPollingTypeNode || this.nodeType?.mockManualExecution) {
|
2022-04-11 06:12:13 -07:00
|
|
|
return this.$locale.baseText('ndv.execute.fetchEvent');
|
|
|
|
}
|
|
|
|
|
2024-01-05 07:23:51 -08:00
|
|
|
return this.$locale.baseText('ndv.execute.testNode');
|
2022-04-11 06:12:13 -07:00
|
|
|
},
|
|
|
|
},
|
|
|
|
methods: {
|
2022-06-20 12:39:24 -07:00
|
|
|
async stopWaitingForWebhook() {
|
|
|
|
try {
|
2023-04-24 01:50:49 -07:00
|
|
|
await this.workflowsStore.removeTestWebhook(this.workflowsStore.workflowId);
|
2022-06-20 12:39:24 -07:00
|
|
|
} catch (error) {
|
2023-05-15 09:41:13 -07:00
|
|
|
this.showError(error, this.$locale.baseText('ndv.execute.stopWaitingForWebhook.error'));
|
2022-06-20 12:39:24 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2022-07-20 08:50:39 -07:00
|
|
|
async onClick() {
|
2024-01-09 03:11:39 -08:00
|
|
|
if (this.isChatNode) {
|
|
|
|
this.ndvStore.setActiveNodeName(null);
|
|
|
|
nodeViewEventBus.emit('openChat');
|
|
|
|
} else if (this.isListeningForEvents) {
|
2023-05-10 08:10:03 -07:00
|
|
|
await this.stopWaitingForWebhook();
|
2022-10-31 10:59:53 -07:00
|
|
|
} else if (this.isListeningForWorkflowEvents) {
|
|
|
|
this.$emit('stopExecution');
|
2022-07-20 08:50:39 -07:00
|
|
|
} else {
|
|
|
|
let shouldUnpinAndExecute = false;
|
2024-01-04 01:22:56 -08:00
|
|
|
if (this.pinnedData.hasData.value) {
|
2023-05-15 09:41:13 -07:00
|
|
|
const confirmResult = await this.confirm(
|
2022-07-20 08:50:39 -07:00
|
|
|
this.$locale.baseText('ndv.pinData.unpinAndExecute.description'),
|
|
|
|
this.$locale.baseText('ndv.pinData.unpinAndExecute.title'),
|
2023-05-15 09:41:13 -07:00
|
|
|
{
|
|
|
|
confirmButtonText: this.$locale.baseText('ndv.pinData.unpinAndExecute.confirm'),
|
|
|
|
cancelButtonText: this.$locale.baseText('ndv.pinData.unpinAndExecute.cancel'),
|
|
|
|
},
|
2022-07-20 08:50:39 -07:00
|
|
|
);
|
2023-05-15 09:41:13 -07:00
|
|
|
shouldUnpinAndExecute = confirmResult === MODAL_CONFIRM;
|
2022-07-20 08:50:39 -07:00
|
|
|
|
2023-11-02 01:43:02 -07:00
|
|
|
if (shouldUnpinAndExecute && this.node) {
|
2024-01-04 01:22:56 -08:00
|
|
|
this.pinnedData.unsetData('unpin-and-execute-modal');
|
2022-07-20 08:50:39 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-04 01:22:56 -08:00
|
|
|
if (!this.pinnedData.hasData.value || shouldUnpinAndExecute) {
|
2022-08-19 06:35:39 -07:00
|
|
|
const telemetryPayload = {
|
|
|
|
node_type: this.nodeType ? this.nodeType.name : null,
|
2022-11-04 06:04:31 -07:00
|
|
|
workflow_id: this.workflowsStore.workflowId,
|
2022-08-19 06:35:39 -07:00
|
|
|
source: this.telemetrySource,
|
2023-08-29 03:53:29 -07:00
|
|
|
session_id: this.ndvStore.sessionId,
|
2022-08-19 06:35:39 -07:00
|
|
|
};
|
|
|
|
this.$telemetry.track('User clicked execute node button', telemetryPayload);
|
2023-12-06 07:28:09 -08:00
|
|
|
await this.externalHooks.run('nodeExecuteButton.onClick', telemetryPayload);
|
2022-08-19 06:35:39 -07:00
|
|
|
|
2023-10-02 08:33:43 -07:00
|
|
|
await this.runWorkflow({
|
|
|
|
destinationNode: this.nodeName,
|
|
|
|
source: 'RunData.ExecuteNodeButton',
|
|
|
|
});
|
2022-07-20 08:50:39 -07:00
|
|
|
this.$emit('execute');
|
|
|
|
}
|
2022-06-20 12:39:24 -07:00
|
|
|
}
|
2022-04-11 06:12:13 -07:00
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
</script>
|