refactor(editor): Refactor workflowHelpers mixin to composable (no-changelog) (#8600)

This commit is contained in:
oleg
2024-02-12 10:45:05 +01:00
committed by GitHub
parent a6211c9a5d
commit 510bf8905d
47 changed files with 1487 additions and 1403 deletions

View File

@@ -4,7 +4,7 @@ import InputTriple from '@/components/InputTriple/InputTriple.vue';
import ParameterInputFull from '@/components/ParameterInputFull.vue';
import ParameterInputHint from '@/components/ParameterInputHint.vue';
import ParameterIssues from '@/components/ParameterIssues.vue';
import { resolveParameter } from '@/mixins/workflowHelpers';
import { resolveParameter } from '@/composables/useWorkflowHelpers';
import { isExpression } from '@/utils/expressions';
import { isObject } from '@jsplumb/util';
import type { AssignmentValue, INodeProperties } from 'n8n-workflow';

View File

@@ -3,7 +3,7 @@ import { useNDVStore } from '@/stores/ndv.store';
import { createTestingPinia } from '@pinia/testing';
import userEvent from '@testing-library/user-event';
import { fireEvent, within } from '@testing-library/vue';
import * as workflowHelpers from '@/mixins/workflowHelpers';
import * as workflowHelpers from '@/composables/useWorkflowHelpers';
import AssignmentCollection from '../AssignmentCollection.vue';
import { createPinia, setActivePinia } from 'pinia';

View File

@@ -1,6 +1,6 @@
import { isObject } from 'lodash-es';
import type { AssignmentValue, IDataObject } from 'n8n-workflow';
import { resolveParameter } from '@/mixins/workflowHelpers';
import { resolveParameter } from '@/composables/useWorkflowHelpers';
import { v4 as uuid } from 'uuid';
export function nameFromExpression(expression: string): string {

View File

@@ -63,7 +63,6 @@ import { python } from '@codemirror/lang-python';
import type { CodeExecutionMode, CodeNodeEditorLanguage } from 'n8n-workflow';
import { CODE_EXECUTION_MODES, CODE_LANGUAGES } from 'n8n-workflow';
import { workflowHelpers } from '@/mixins/workflowHelpers'; // for json field completions
import { ASK_AI_EXPERIMENT, CODE_NODE_TYPE } from '@/constants';
import { codeNodeEditorEventBus } from '@/event-bus';
import { useRootStore } from '@/stores/n8nRoot.store';
@@ -83,7 +82,7 @@ export default defineComponent({
components: {
AskAI,
},
mixins: [linterExtension, completerExtension, workflowHelpers],
mixins: [linterExtension, completerExtension],
props: {
aiButtonEnabled: {
type: Boolean,

View File

@@ -37,7 +37,6 @@ import { defineComponent } from 'vue';
import { mapStores } from 'pinia';
import type { IN8nPromptResponse } from '@/Interface';
import { VALID_EMAIL_REGEX } from '@/constants';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import Modal from '@/components/Modal.vue';
import { useSettingsStore } from '@/stores/settings.store';
import { useRootStore } from '@/stores/n8nRoot.store';
@@ -47,7 +46,6 @@ import { useToast } from '@/composables/useToast';
export default defineComponent({
name: 'ContactPromptModal',
components: { Modal },
mixins: [workflowHelpers],
props: ['modalName'],
setup() {
return {

View File

@@ -51,7 +51,6 @@
import { defineComponent } from 'vue';
import { mapStores } from 'pinia';
import { MAX_WORKFLOW_NAME_LENGTH, PLACEHOLDER_EMPTY_WORKFLOW_ID } from '@/constants';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useToast } from '@/composables/useToast';
import TagsDropdown from '@/components/TagsDropdown.vue';
import Modal from '@/components/Modal.vue';
@@ -63,15 +62,20 @@ import { getWorkflowPermissions } from '@/permissions';
import { useUsersStore } from '@/stores/users.store';
import { createEventBus } from 'n8n-design-system/utils';
import { useCredentialsStore } from '@/stores/credentials.store';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
import { useRouter } from 'vue-router';
export default defineComponent({
name: 'DuplicateWorkflow',
components: { TagsDropdown, Modal },
mixins: [workflowHelpers],
props: ['modalName', 'isActive', 'data'],
setup() {
const router = useRouter();
const workflowHelpers = useWorkflowHelpers(router);
return {
...useToast(),
workflowHelpers,
};
},
data() {
@@ -156,13 +160,13 @@ export default defineComponent({
await this.workflowsStore.fetchWorkflow(this.data.id);
workflowToUpdate = workflow;
this.removeForeignCredentialsFromWorkflow(
this.workflowHelpers.removeForeignCredentialsFromWorkflow(
workflowToUpdate,
this.credentialsStore.allCredentials,
);
}
const saved = await this.saveAsNewWorkflow({
const saved = await this.workflowHelpers.saveAsNewWorkflow({
name,
data: workflowToUpdate,
tags: this.currentTagIds,

View File

@@ -45,7 +45,8 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
import { PLACEHOLDER_EMPTY_WORKFLOW_ID, WORKFLOW_SETTINGS_MODAL_KEY } from '@/constants';
import type { IWorkflowSettings } from 'n8n-workflow';
import { deepCopy } from 'n8n-workflow';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
import { useRouter } from 'vue-router';
interface IWorkflowSaveSettings {
saveFailedExecutions: boolean;
@@ -55,13 +56,20 @@ interface IWorkflowSaveSettings {
export default defineComponent({
name: 'ExecutionsInfoAccordion',
mixins: [workflowHelpers],
props: {
initiallyExpanded: {
type: Boolean,
default: false,
},
},
setup() {
const router = useRouter();
const workflowHelpers = useWorkflowHelpers(router);
return {
workflowHelpers,
};
},
data() {
return {
defaultValues: {
@@ -211,7 +219,7 @@ export default defineComponent({
} else if (this.$route.params.name && this.$route.params.name !== 'new') {
currentId = this.$route.params.name;
}
const saved = await this.saveCurrentWorkflow({
const saved = await this.workflowHelpers.saveCurrentWorkflow({
id: currentId,
name: this.workflowName,
tags: this.currentWorkflowTagIds,

View File

@@ -55,12 +55,11 @@ import { NodeHelpers } from 'n8n-workflow';
import { useMessage } from '@/composables/useMessage';
import { useToast } from '@/composables/useToast';
import { v4 as uuid } from 'uuid';
import type { Route } from 'vue-router';
import { useRouter, type Route } from 'vue-router';
import { executionHelpers } from '@/mixins/executionsHelpers';
import { range as _range } from 'lodash-es';
import { NO_NETWORK_ERROR_CODE } from '@/utils/apiUtils';
import { getNodeViewTab } from '@/utils/canvasUtils';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useUIStore } from '@/stores/ui.store';
import { useSettingsStore } from '@/stores/settings.store';
@@ -69,6 +68,7 @@ import { useTagsStore } from '@/stores/tags.store';
import { executionFilterToQueryFilter } from '@/utils/executionUtils';
import { useExternalHooks } from '@/composables/useExternalHooks';
import { useDebounce } from '@/composables/useDebounce';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
// Number of execution pages that are fetched before temporary execution card is shown
const MAX_LOADING_ATTEMPTS = 5;
@@ -80,13 +80,16 @@ export default defineComponent({
components: {
ExecutionsSidebar,
},
mixins: [executionHelpers, workflowHelpers],
mixins: [executionHelpers],
setup() {
const externalHooks = useExternalHooks();
const router = useRouter();
const workflowHelpers = useWorkflowHelpers(router);
const { callDebounced } = useDebounce();
return {
externalHooks,
workflowHelpers,
callDebounced,
...useToast(),
...useMessage(),
@@ -168,7 +171,7 @@ export default defineComponent({
);
if (confirmModal === MODAL_CONFIRM) {
const saved = await this.saveCurrentWorkflow({}, false);
const saved = await this.workflowHelpers.saveCurrentWorkflow({}, false);
if (saved) {
await this.settingsStore.fetchPromptsData();
}

View File

@@ -8,7 +8,6 @@ import { EditorView, keymap } from '@codemirror/view';
import { EditorState, Prec } from '@codemirror/state';
import { history, redo, undo } from '@codemirror/commands';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { expressionManager } from '@/mixins/expressionManager';
import { completionManager } from '@/mixins/completionManager';
import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler';
@@ -22,7 +21,7 @@ import type { IVariableItemSelected } from '@/Interface';
export default defineComponent({
name: 'ExpressionEditorModalInput',
mixins: [expressionManager, completionManager, workflowHelpers],
mixins: [expressionManager, completionManager],
props: {
modelValue: {
type: String,

View File

@@ -4,7 +4,7 @@ import InputTriple from '@/components/InputTriple/InputTriple.vue';
import ParameterInputFull from '@/components/ParameterInputFull.vue';
import ParameterIssues from '@/components/ParameterIssues.vue';
import { useI18n } from '@/composables/useI18n';
import { resolveParameter } from '@/mixins/workflowHelpers';
import { resolveParameter } from '@/composables/useWorkflowHelpers';
import { DateTime } from 'luxon';
import {
FilterError,
@@ -240,7 +240,7 @@ const onBlur = (): void => {
@operatorChange="onOperatorChange"
></OperatorSelect>
</template>
<template #right="{ breakpoint }" v-if="!operator.singleValue">
<template v-if="!operator.singleValue" #right="{ breakpoint }">
<ParameterInputFull
:key="rightParameter.type"
display-options

View File

@@ -21,7 +21,7 @@ import { useI18n } from '@/composables/useI18n';
import { useDebounce } from '@/composables/useDebounce';
import Condition from './Condition.vue';
import CombinatorSelect from './CombinatorSelect.vue';
import { resolveParameter } from '@/mixins/workflowHelpers';
import { resolveParameter } from '@/composables/useWorkflowHelpers';
import { v4 as uuid } from 'uuid';
interface Props {

View File

@@ -12,7 +12,6 @@ import { history, redo, undo } from '@codemirror/commands';
import { acceptCompletion, autocompletion, completionStatus } from '@codemirror/autocomplete';
import { useNDVStore } from '@/stores/ndv.store';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { expressionManager } from '@/mixins/expressionManager';
import { highlighter } from '@/plugins/codemirror/resolvableHighlighter';
import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler';
@@ -25,7 +24,7 @@ const editableConf = new Compartment();
export default defineComponent({
name: 'InlineExpressionEditorInput',
mixins: [completionManager, expressionManager, workflowHelpers],
mixins: [completionManager, expressionManager],
props: {
modelValue: {
type: String,

View File

@@ -174,7 +174,6 @@ import type {
Workflow,
} from 'n8n-workflow';
import RunData from './RunData.vue';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import NodeExecuteButton from './NodeExecuteButton.vue';
import WireMeUp from './WireMeUp.vue';
import {
@@ -186,13 +185,13 @@ import {
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useNDVStore } from '@/stores/ndv.store';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { useUIStore } from '@/stores/ui.store';
type MappingMode = 'debugging' | 'mapping';
export default defineComponent({
name: 'InputPanel',
components: { RunData, NodeExecuteButton, WireMeUp },
mixins: [workflowHelpers],
props: {
currentNodeName: {
type: String,
@@ -236,7 +235,7 @@ export default defineComponent({
};
},
computed: {
...mapStores(useNodeTypesStore, useNDVStore, useWorkflowsStore),
...mapStores(useNodeTypesStore, useNDVStore, useWorkflowsStore, useUIStore),
focusedMappableInput(): string {
return this.ndvStore.focusedMappableInput;
},

View File

@@ -29,7 +29,6 @@ import {
VIEWS,
} from '@/constants';
import type { INodeUi, ITabBarItem } from '@/Interface';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useNDVStore } from '@/stores/ndv.store';
import { useSourceControlStore } from '@/stores/sourceControl.store';
import { useUIStore } from '@/stores/ui.store';
@@ -40,13 +39,11 @@ export default defineComponent({
WorkflowDetails,
TabBar,
},
mixins: [pushConnection, workflowHelpers],
mixins: [pushConnection],
setup(props, ctx) {
return {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
...pushConnection.setup?.(props, ctx),
// eslint-disable-next-line @typescript-eslint/no-misused-promises
...workflowHelpers.setup?.(props, ctx),
};
},
data() {

View File

@@ -158,7 +158,6 @@ import ShortenName from '@/components/ShortenName.vue';
import TagsContainer from '@/components/TagsContainer.vue';
import PushConnectionTracker from '@/components/PushConnectionTracker.vue';
import WorkflowActivator from '@/components/WorkflowActivator.vue';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import SaveButton from '@/components/SaveButton.vue';
import TagsDropdown from '@/components/TagsDropdown.vue';
import InlineTextEdit from '@/components/InlineTextEdit.vue';
@@ -185,6 +184,8 @@ import { createEventBus } from 'n8n-design-system/utils';
import { nodeViewEventBus } from '@/event-bus';
import { hasPermission } from '@/rbac/permissions';
import { useCanvasStore } from '@/stores/canvas.store';
import { useRouter } from 'vue-router';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
const hasChanged = (prev: string[], curr: string[]) => {
if (prev.length !== curr.length) {
@@ -208,7 +209,6 @@ export default defineComponent({
BreakpointsObserver,
CollaborationPane,
},
mixins: [workflowHelpers],
props: {
readOnly: {
type: Boolean,
@@ -216,7 +216,11 @@ export default defineComponent({
},
},
setup() {
const router = useRouter();
const workflowHelpers = useWorkflowHelpers(router);
return {
workflowHelpers,
...useTitleChange(),
...useToast(),
...useMessage(),
@@ -390,7 +394,7 @@ export default defineComponent({
} else if (this.$route.params.name && this.$route.params.name !== 'new') {
currentId = this.$route.params.name;
}
const saved = await this.saveCurrentWorkflow({
const saved = await this.workflowHelpers.saveCurrentWorkflow({
id: currentId,
name: this.workflowName,
tags: this.currentWorkflowTagIds,
@@ -443,7 +447,7 @@ export default defineComponent({
}
this.tagsSaving = true;
const saved = await this.saveCurrentWorkflow({ tags });
const saved = await this.workflowHelpers.saveCurrentWorkflow({ tags });
this.$telemetry.track('User edited workflow tags', {
workflow_id: this.currentWorkflowId,
new_tag_count: tags.length,
@@ -494,7 +498,7 @@ export default defineComponent({
return;
}
const saved = await this.saveCurrentWorkflow({ name });
const saved = await this.workflowHelpers.saveCurrentWorkflow({ name });
if (saved) {
this.isNameEditEnabled = false;
}
@@ -539,7 +543,7 @@ export default defineComponent({
break;
}
case WORKFLOW_MENU_ACTIONS.DOWNLOAD: {
const workflowData = await this.getWorkflowDataToSave();
const workflowData = await this.workflowHelpers.getWorkflowDataToSave();
const { tags, ...data } = workflowData;
const exportData: IWorkflowToShare = {
...data,

View File

@@ -189,7 +189,6 @@ import {
WAIT_TIME_UNLIMITED,
} from '@/constants';
import { nodeBase } from '@/mixins/nodeBase';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import type {
ConnectionTypes,
IExecutionsSummary,
@@ -227,7 +226,7 @@ export default defineComponent({
FontAwesomeIcon,
NodeIcon,
},
mixins: [nodeBase, workflowHelpers],
mixins: [nodeBase],
props: {
isProductionExecutionPreview: {
type: Boolean,

View File

@@ -146,7 +146,6 @@ import type {
} from 'n8n-workflow';
import { jsonParse, NodeHelpers, NodeConnectionType } from 'n8n-workflow';
import type { IExecutionResponse, INodeUi, IUpdateInformation, TargetItem } from '@/Interface';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import NodeSettings from '@/components/NodeSettings.vue';
import NDVDraggablePanels from './NDVDraggablePanels.vue';
@@ -174,6 +173,8 @@ import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { useMessage } from '@/composables/useMessage';
import { useExternalHooks } from '@/composables/useExternalHooks';
import { usePinnedData } from '@/composables/usePinnedData';
import { useRouter } from 'vue-router';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
export default defineComponent({
name: 'NodeDetailsView',
@@ -184,7 +185,7 @@ export default defineComponent({
NDVDraggablePanels,
TriggerPanel,
},
mixins: [workflowHelpers, workflowActivate],
mixins: [workflowActivate],
props: {
readOnly: {
type: Boolean,
@@ -203,11 +204,14 @@ export default defineComponent({
const nodeHelpers = useNodeHelpers();
const { activeNode } = storeToRefs(ndvStore);
const pinnedData = usePinnedData(activeNode);
const router = useRouter();
const workflowHelpers = useWorkflowHelpers(router);
return {
externalHooks,
nodeHelpers,
pinnedData,
workflowHelpers,
...useDeviceSupport(),
...useMessage(),
// eslint-disable-next-line @typescript-eslint/no-misused-promises
@@ -289,7 +293,7 @@ export default defineComponent({
);
},
workflow(): Workflow {
return this.getCurrentWorkflow();
return this.workflowHelpers.getCurrentWorkflow();
},
hasOutputConnection() {
if (!this.activeNode) return false;
@@ -482,7 +486,7 @@ export default defineComponent({
nodeSubtitle: this.nodeHelpers.getNodeSubtitle(
node,
this.activeNodeType,
this.getCurrentWorkflow(),
this.workflowHelpers.getCurrentWorkflow(),
),
});

View File

@@ -14,8 +14,8 @@
:size="size"
:icon="!isListeningForEvents && 'flask'"
:transparent-background="transparent"
@click="onClick"
:title="!isTriggerNode ? $locale.baseText('ndv.execute.testNode.description') : ''"
@click="onClick"
/>
</div>
</n8n-tooltip>

View File

@@ -29,7 +29,7 @@
<div v-if="isWebhookMethodVisible(webhook)" class="webhook-wrapper">
<div class="http-field">
<div class="http-method">
{{ getWebhookExpressionValue(webhook, 'httpMethod') }}<br />
{{ workflowHelpers.getWebhookExpressionValue(webhook, 'httpMethod') }}<br />
</div>
</div>
<div class="url-field">
@@ -62,21 +62,23 @@ import {
OPEN_URL_PANEL_TRIGGER_NODE_TYPES,
PRODUCTION_ONLY_TRIGGER_NODE_TYPES,
} from '@/constants';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useClipboard } from '@/composables/useClipboard';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
import { useRouter } from 'vue-router';
export default defineComponent({
name: 'NodeWebhooks',
mixins: [workflowHelpers],
props: [
'node', // NodeUi
'nodeType', // INodeTypeDescription
],
setup() {
const router = useRouter();
const clipboard = useClipboard();
const workflowHelpers = useWorkflowHelpers(router);
return {
clipboard,
workflowHelpers,
...useToast(),
};
},
@@ -102,7 +104,7 @@ export default defineComponent({
visibleWebhookUrls(): IWebhookDescription[] {
return this.webhooksNode.filter((webhook) => {
if (typeof webhook.ndvHideUrl === 'string') {
return !this.getWebhookExpressionValue(webhook, 'ndvHideUrl');
return !this.workflowHelpers.getWebhookExpressionValue(webhook, 'ndvHideUrl');
}
return !webhook.ndvHideUrl;
@@ -184,7 +186,7 @@ export default defineComponent({
},
getWebhookUrlDisplay(webhookData: IWebhookDescription): string {
if (this.node) {
return this.getWebhookUrl(
return this.workflowHelpers.getWebhookUrl(
webhookData,
this.node,
this.isProductionOnly ? 'production' : this.showUrlFor,
@@ -194,7 +196,7 @@ export default defineComponent({
},
isWebhookMethodVisible(webhook: IWebhookDescription): boolean {
if (typeof webhook.ndvHideMethod === 'string') {
return !this.getWebhookExpressionValue(webhook, 'ndvHideMethod');
return !this.workflowHelpers.getWebhookExpressionValue(webhook, 'ndvHideMethod');
}
return !webhook.ndvHideMethod;

View File

@@ -502,7 +502,6 @@ import JsEditor from '@/components/JsEditor/JsEditor.vue';
import JsonEditor from '@/components/JsonEditor/JsonEditor.vue';
import SqlEditor from '@/components/SqlEditor/SqlEditor.vue';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { hasExpressionMapping, isValueExpression } from '@/utils/nodeTypesUtils';
import { isResourceLocatorValue } from '@/utils/typeGuards';
@@ -523,6 +522,8 @@ import type { N8nInput } from 'n8n-design-system';
import { isCredentialOnlyNodeType } from '@/utils/credentialOnlyNodes';
import { useExternalHooks } from '@/composables/useExternalHooks';
import { useDebounce } from '@/composables/useDebounce';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
import { useRouter } from 'vue-router';
type Picker = { $emit: (arg0: string, arg1: Date) => void };
@@ -541,7 +542,6 @@ export default defineComponent({
ResourceLocator,
TextEdit,
},
mixins: [workflowHelpers],
props: {
additionalExpressionData: {
type: Object as PropType<IDataObject>,
@@ -618,11 +618,14 @@ export default defineComponent({
const i18n = useI18n();
const nodeHelpers = useNodeHelpers();
const { callDebounced } = useDebounce();
const router = useRouter();
const workflowHelpers = useWorkflowHelpers(router);
return {
externalHooks,
i18n,
nodeHelpers,
workflowHelpers,
callDebounced,
};
},
@@ -724,7 +727,7 @@ export default defineComponent({
// Get the resolved parameter values of the current node
const currentNodeParameters = this.ndvStore.activeNode?.parameters;
try {
const resolvedNodeParameters = this.resolveParameter(currentNodeParameters);
const resolvedNodeParameters = this.workflowHelpers.resolveParameter(currentNodeParameters);
const returnValues: string[] = [];
for (const parameterPath of loadOptionsDependsOn) {
@@ -969,7 +972,7 @@ export default defineComponent({
return shortPath.join('.');
},
workflow(): Workflow {
return this.getCurrentWorkflow();
return this.workflowHelpers.getCurrentWorkflow();
},
isResourceLocatorParameter(): boolean {
return this.parameter.type === 'resourceLocator';
@@ -1092,7 +1095,7 @@ export default defineComponent({
try {
const currentNodeParameters = (this.ndvStore.activeNode as INodeUi).parameters;
const resolvedNodeParameters = this.resolveRequiredParameters(
const resolvedNodeParameters = this.workflowHelpers.resolveRequiredParameters(
this.parameter,
currentNodeParameters,
) as INodeParameters;

View File

@@ -182,7 +182,6 @@ import ResourceMapper from '@/components/ResourceMapper/ResourceMapper.vue';
import FilterConditions from '@/components/FilterConditions/FilterConditions.vue';
import AssignmentCollection from '@/components/AssignmentCollection/AssignmentCollection.vue';
import { KEEP_AUTH_IN_NDV_FOR_NODES } from '@/constants';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useNDVStore } from '@/stores/ndv.store';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import {
@@ -192,6 +191,8 @@ import {
} from '@/utils/nodeTypesUtils';
import { get, set } from 'lodash-es';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { useRouter } from 'vue-router';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
const FixedCollectionParameter = defineAsyncComponent(
async () => await import('./FixedCollectionParameter.vue'),
@@ -212,7 +213,6 @@ export default defineComponent({
FilterConditions,
AssignmentCollection,
},
mixins: [workflowHelpers],
props: {
nodeValues: {
type: Object as PropType<INodeParameters>,
@@ -250,6 +250,8 @@ export default defineComponent({
setup() {
const nodeHelpers = useNodeHelpers();
const asyncLoadingError = ref(false);
const router = useRouter();
const workflowHelpers = useWorkflowHelpers(router);
// This will catch errors in async components
onErrorCaptured((e, component) => {
@@ -274,6 +276,7 @@ export default defineComponent({
return {
nodeHelpers,
asyncLoadingError,
workflowHelpers,
};
},
computed: {
@@ -482,7 +485,7 @@ export default defineComponent({
} else {
// Contains probably no expression with a missing parameter so resolve
try {
nodeValues[key] = this.resolveExpression(
nodeValues[key] = this.workflowHelpers.resolveExpression(
rawValues[key],
nodeValues,
) as NodeParameterValue;
@@ -559,7 +562,7 @@ export default defineComponent({
// Get the resolved parameter values of the current node
const currentNodeParameters = this.ndvStore.activeNode?.parameters;
try {
const resolvedNodeParameters = this.resolveParameter(currentNodeParameters);
const resolvedNodeParameters = this.workflowHelpers.resolveParameter(currentNodeParameters);
const returnValues: string[] = [];
for (const parameterPath of loadOptionsDependsOn) {

View File

@@ -53,7 +53,6 @@ import { defineComponent } from 'vue';
import type { INodeUi, IUpdateInformation, TargetItem } from '@/Interface';
import ParameterInput from '@/components/ParameterInput.vue';
import InputHint from '@/components/ParameterInputHint.vue';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useEnvironmentsStore } from '@/stores/environments.ee.store';
import { useExternalSecretsStore } from '@/stores/externalSecrets.ee.store';
import { useNDVStore } from '@/stores/ndv.store';
@@ -71,6 +70,8 @@ import { isResourceLocatorValue } from 'n8n-workflow';
import { get } from 'lodash-es';
import type { EventBus } from 'n8n-design-system/utils';
import { createEventBus } from 'n8n-design-system/utils';
import { useRouter } from 'vue-router';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
export default defineComponent({
name: 'ParameterInputWrapper',
@@ -78,7 +79,6 @@ export default defineComponent({
ParameterInput,
InputHint,
},
mixins: [workflowHelpers],
props: {
additionalExpressionData: {
type: Object as PropType<IDataObject>,
@@ -149,6 +149,14 @@ export default defineComponent({
default: () => createEventBus(),
},
},
setup() {
const router = useRouter();
const workflowHelpers = useWorkflowHelpers(router);
return {
workflowHelpers,
};
},
computed: {
...mapStores(useNDVStore, useExternalSecretsStore, useEnvironmentsStore),
isValueExpression() {
@@ -209,7 +217,7 @@ export default defineComponent({
};
}
return { ok: true, result: this.resolveExpression(value, undefined, opts) };
return { ok: true, result: this.workflowHelpers.resolveExpression(value, undefined, opts) };
} catch (error) {
return { ok: false, error };
}

View File

@@ -139,7 +139,6 @@ import {
REPORTED_SOURCE_OTHER_KEY,
VIEWS,
} from '@/constants';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useToast } from '@/composables/useToast';
import Modal from '@/components/Modal.vue';
import type { IFormInputs, IPersonalizationLatestVersion, IUser } from '@/Interface';
@@ -160,7 +159,6 @@ const SURVEY_VERSION = 'v4';
export default defineComponent({
name: 'PersonalizationModal',
components: { Modal },
mixins: [workflowHelpers],
props: {
teleported: {
type: Boolean,

View File

@@ -148,7 +148,6 @@ import type { IResourceLocatorReqParams, IResourceLocatorResultExpanded } from '
import DraggableTarget from '@/components/DraggableTarget.vue';
import ExpressionParameterInput from '@/components/ExpressionParameterInput.vue';
import ParameterIssues from '@/components/ParameterIssues.vue';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useRootStore } from '@/stores/n8nRoot.store';
import { useNDVStore } from '@/stores/ndv.store';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
@@ -174,6 +173,8 @@ import type { PropType } from 'vue';
import { defineComponent } from 'vue';
import ResourceLocatorDropdown from './ResourceLocatorDropdown.vue';
import { useDebounce } from '@/composables/useDebounce';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
import { useRouter } from 'vue-router';
interface IResourceLocatorQuery {
results: INodeListSearchItems[];
@@ -190,7 +191,6 @@ export default defineComponent({
ParameterIssues,
ResourceLocatorDropdown,
},
mixins: [workflowHelpers],
props: {
parameter: {
type: Object as PropType<INodeProperties>,
@@ -257,6 +257,14 @@ export default defineComponent({
default: () => createEventBus(),
},
},
setup() {
const router = useRouter();
const workflowHelpers = useWorkflowHelpers(router);
const { callDebounced } = useDebounce();
return { callDebounced, workflowHelpers };
},
data() {
return {
resourceDropdownVisible: false,
@@ -267,11 +275,6 @@ export default defineComponent({
width: 0,
};
},
setup() {
const { callDebounced } = useDebounce();
return { callDebounced };
},
computed: {
...mapStores(useNodeTypesStore, useNDVStore, useRootStore, useUIStore, useWorkflowsStore),
appName(): string {
@@ -370,7 +373,7 @@ export default defineComponent({
const value = this.isValueExpression ? this.expressionComputedValue : this.valueToDisplay;
if (typeof value === 'string') {
const expression = this.currentMode.url.replace(/\{\{\$value\}\}/g, value);
const resolved = this.resolveExpression(expression);
const resolved = this.workflowHelpers.resolveExpression(expression);
return typeof resolved === 'string' ? resolved : null;
}
@@ -683,7 +686,7 @@ export default defineComponent({
});
}
const resolvedNodeParameters = this.resolveRequiredParameters(
const resolvedNodeParameters = this.workflowHelpers.resolveRequiredParameters(
this.parameter,
params.parameters,
) as INodeParameters;

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { IUpdateInformation, ResourceMapperReqParams } from '@/Interface';
import { resolveRequiredParameters } from '@/mixins/workflowHelpers';
import { resolveRequiredParameters } from '@/composables/useWorkflowHelpers';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import type {
INode,

View File

@@ -106,7 +106,6 @@ import { defineComponent, ref } from 'vue';
import { mapStores } from 'pinia';
import { nodeBase } from '@/mixins/nodeBase';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { isNumber, isString } from '@/utils/typeGuards';
import type {
INodeUi,
@@ -126,7 +125,7 @@ import { useDeviceSupport } from 'n8n-design-system';
export default defineComponent({
name: 'Sticky',
mixins: [nodeBase, workflowHelpers],
mixins: [nodeBase],
props: {
nodeViewScale: {
type: Number,
@@ -320,7 +319,7 @@ export default defineComponent({
this.workflowsStore.updateNodeProperties(updateInformation);
},
touchStart() {
if (this.deviceSupport.isTouchDevice === true && !this.isMacOs && !this.isTouchActive) {
if (this.deviceSupport.isTouchDevice && !this.isMacOs && !this.isTouchActive) {
this.isTouchActive = true;
setTimeout(() => {
this.isTouchActive = false;

View File

@@ -46,7 +46,7 @@
</n8n-text>
</div>
<div v-if="displayChatButton">
<n8n-button @click="openWebhookUrl()" class="mb-xl">
<n8n-button class="mb-xl" @click="openWebhookUrl()">
{{ $locale.baseText('ndv.trigger.chatTrigger.openChat') }}
</n8n-button>
</div>
@@ -121,7 +121,6 @@ import type { INodeUi } from '@/Interface';
import type { INodeTypeDescription } from 'n8n-workflow';
import { getTriggerNodeServiceName } from '@/utils/nodeTypesUtils';
import NodeExecuteButton from '@/components/NodeExecuteButton.vue';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import CopyInput from '@/components/CopyInput.vue';
import NodeIcon from '@/components/NodeIcon.vue';
import { useUIStore } from '@/stores/ui.store';
@@ -129,6 +128,8 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
import { useNDVStore } from '@/stores/ndv.store';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { createEventBus } from 'n8n-design-system/utils';
import { useRouter } from 'vue-router';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
export default defineComponent({
name: 'TriggerPanel',
@@ -137,7 +138,6 @@ export default defineComponent({
CopyInput,
NodeIcon,
},
mixins: [workflowHelpers],
props: {
nodeName: {
type: String,
@@ -146,6 +146,14 @@ export default defineComponent({
type: String,
},
},
setup() {
const router = useRouter();
const workflowHelpers = useWorkflowHelpers(router);
return {
workflowHelpers,
};
},
data: () => {
return {
executionsHelpEventBus: createEventBus(),
@@ -178,12 +186,9 @@ export default defineComponent({
}
if (this.node) {
const hideContentValue = this.getCurrentWorkflow().expression.getSimpleParameterValue(
this.node,
hideContent,
'internal',
{},
);
const hideContentValue = this.workflowHelpers
.getCurrentWorkflow()
.expression.getSimpleParameterValue(this.node, hideContent, 'internal', {});
if (typeof hideContentValue === 'boolean') {
return hideContentValue;
@@ -220,14 +225,17 @@ export default defineComponent({
return undefined;
}
return this.getWebhookExpressionValue(this.nodeType.webhooks[0], 'httpMethod');
return this.workflowHelpers.getWebhookExpressionValue(
this.nodeType.webhooks[0],
'httpMethod',
);
},
webhookTestUrl(): string | undefined {
if (!this.node || !this.nodeType?.webhooks?.length) {
return undefined;
}
return this.getWebhookUrl(this.nodeType.webhooks[0], this.node, 'test');
return this.workflowHelpers.getWebhookUrl(this.nodeType.webhooks[0], this.node, 'test');
},
isWebhookBasedNode(): boolean {
return Boolean(this.nodeType?.webhooks?.length);

View File

@@ -62,7 +62,6 @@ import type { IN8nPromptResponse } from '@/Interface';
import ModalDrawer from '@/components/ModalDrawer.vue';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useSettingsStore } from '@/stores/settings.store';
import { useRootStore } from '@/stores/n8nRoot.store';
import { createEventBus } from 'n8n-design-system/utils';
@@ -79,7 +78,6 @@ export default defineComponent({
components: {
ModalDrawer,
},
mixins: [workflowHelpers],
props: ['isActive'],
setup() {
return {

View File

@@ -45,11 +45,11 @@ import { NodeConnectionType, WorkflowDataProxy } from 'n8n-workflow';
import VariableSelectorItem from '@/components/VariableSelectorItem.vue';
import type { INodeUi, IVariableItemSelected, IVariableSelectorOption } from '@/Interface';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useRootStore } from '@/stores/n8nRoot.store';
import { useNDVStore } from '@/stores/ndv.store';
import { useRouter } from 'vue-router';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
// Node types that should not be displayed in variable selector
const SKIPPED_NODE_TYPES = [STICKY_NODE_TYPE];
@@ -59,8 +59,15 @@ export default defineComponent({
components: {
VariableSelectorItem,
},
mixins: [workflowHelpers],
props: ['path', 'redactValues'],
setup() {
const router = useRouter();
const workflowHelpers = useWorkflowHelpers(router);
return {
workflowHelpers,
};
},
data() {
return {
variableFilter: '',
@@ -74,7 +81,10 @@ export default defineComponent({
if (!activeNode) {
return null;
}
return this.getParentMainInputNode(this.getCurrentWorkflow(), activeNode);
return this.workflowHelpers.getParentMainInputNode(
this.workflowHelpers.getCurrentWorkflow(),
activeNode,
);
},
extendAll(): boolean {
if (this.variableFilter) {
@@ -87,7 +97,7 @@ export default defineComponent({
return this.getFilterResults(this.variableFilter.toLowerCase(), 0);
},
workflow(): Workflow {
return this.getCurrentWorkflow();
return this.workflowHelpers.getCurrentWorkflow();
},
},
methods: {
@@ -482,7 +492,7 @@ export default defineComponent({
parentNode[0],
inputName,
);
const connectionInputData = this.connectionInputData(
const connectionInputData = this.workflowHelpers.connectionInputData(
parentNode,
nodeName,
inputName,

View File

@@ -64,7 +64,7 @@
</div>
</div>
</div>
<MessageTyping ref="messageContainer" v-if="isLoading" />
<MessageTyping v-if="isLoading" ref="messageContainer" />
</div>
<div v-if="node" class="logs-wrapper" data-test-id="lm-chat-logs">
<n8n-text class="logs-title" tag="p" size="large">{{
@@ -149,6 +149,9 @@ import { useExternalHooks } from '@/composables/useExternalHooks';
// eslint-disable-next-line import/no-unresolved
import MessageTyping from '@n8n/chat/components/MessageTyping.vue';
import { useRouter } from 'vue-router';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
const RunDataAi = defineAsyncComponent(
async () => await import('@/components/RunDataAi/RunDataAi.vue'),
@@ -181,9 +184,12 @@ export default defineComponent({
mixins: [workflowRun],
setup(props, ctx) {
const externalHooks = useExternalHooks();
const router = useRouter();
const workflowHelpers = useWorkflowHelpers(router);
return {
externalHooks,
workflowHelpers,
...useToast(),
// eslint-disable-next-line @typescript-eslint/no-misused-promises
...workflowRun.setup?.(props, ctx),
@@ -201,7 +207,7 @@ export default defineComponent({
},
computed: {
...mapStores(useWorkflowsStore, useUIStore),
...mapStores(useWorkflowsStore, useUIStore, useNodeTypesStore),
isLoading(): boolean {
return this.uiStore.isActionActive('workflowRunning');
},
@@ -213,7 +219,7 @@ export default defineComponent({
},
methods: {
displayExecution(executionId: string) {
const workflow = this.getCurrentWorkflow();
const workflow = this.workflowHelpers.getCurrentWorkflow();
const route = this.$router.resolve({
name: VIEWS.EXECUTION_PREVIEW,
params: { name: workflow.id, executionId },
@@ -264,9 +270,9 @@ export default defineComponent({
);
return;
}
const workflow = this.getCurrentWorkflow();
const workflow = this.workflowHelpers.getCurrentWorkflow();
const chatNode = this.workflowsStore.getNodes().find((node: INodeUi): boolean => {
const chatNode = this.workflowHelpers.getNodes().find((node: INodeUi): boolean => {
const nodeType = this.nodeTypesStore.getNodeType(node.type, node.typeVersion);
if (!nodeType) return false;
@@ -317,7 +323,7 @@ export default defineComponent({
getChatMessages(): ChatMessage[] {
if (!this.connectedNode) return [];
const workflow = this.getCurrentWorkflow();
const workflow = this.workflowHelpers.getCurrentWorkflow();
const connectedMemoryInputs =
workflow.connectionsByDestinationNode[this.connectedNode.name][NodeConnectionType.AiMemory];
if (!connectedMemoryInputs) return [];
@@ -369,7 +375,7 @@ export default defineComponent({
return;
}
const workflow = this.getCurrentWorkflow();
const workflow = this.workflowHelpers.getCurrentWorkflow();
const childNodes = workflow.getChildNodes(triggerNode.name);
for (const childNode of childNodes) {
@@ -389,7 +395,7 @@ export default defineComponent({
},
getTriggerNode(): INode | null {
const workflow = this.getCurrentWorkflow();
const workflow = this.workflowHelpers.getCurrentWorkflow();
const triggerNode = workflow.queryNodes((nodeType: INodeType) =>
[CHAT_TRIGGER_NODE_TYPE, MANUAL_CHAT_TRIGGER_NODE_TYPE].includes(nodeType.description.name),
);

View File

@@ -6,7 +6,7 @@ import {
} from './utils/ResourceMapper.utils';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { waitAllPromises } from '@/__tests__/utils';
import * as workflowHelpers from '@/mixins/workflowHelpers';
import * as workflowHelpers from '@/composables/useWorkflowHelpers';
import ResourceMapper from '@/components/ResourceMapper/ResourceMapper.vue';
import userEvent from '@testing-library/user-event';
import { createComponentRenderer } from '@/__tests__/render';