refactor(editor): Refactor nodeHelpers mixin to composable (#7810)

- Convert `nodeHelpers` mixin into composable and fix types
- Replace usage of the mixin with the new composable
- Add missing store imports in components that were dependent on opaque
imports from nodeHelpers mixin
- Refactor the `CollectionParameter` component to the modern script
setup syntax
Github issue / Community forum post (link here to close automatically):

---------

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
oleg
2023-12-08 16:59:03 +01:00
committed by GitHub
parent e8a493f718
commit 35fbc37c8e
20 changed files with 1017 additions and 977 deletions

View File

@@ -25,16 +25,22 @@ import type { IBinaryData, IRunData } from 'n8n-workflow';
import BinaryDataDisplayEmbed from '@/components/BinaryDataDisplayEmbed.vue';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
export default defineComponent({
name: 'BinaryDataDisplay',
mixins: [nodeHelpers],
components: {
BinaryDataDisplayEmbed,
},
setup() {
const nodeHelpers = useNodeHelpers();
return {
nodeHelpers,
};
},
props: [
'displayData', // IBinaryData
'windowVisible', // boolean
@@ -42,7 +48,7 @@ export default defineComponent({
computed: {
...mapStores(useWorkflowsStore),
binaryData(): IBinaryData | null {
const binaryData = this.getBinaryData(
const binaryData = this.nodeHelpers.getBinaryData(
this.workflowRunData,
this.displayData.node,
this.displayData.runIndex,

View File

@@ -70,6 +70,7 @@ import { completerExtension } from './completer';
import { codeNodeEditorTheme } from './theme';
import AskAI from './AskAI/AskAI.vue';
import { useMessage } from '@/composables/useMessage';
import { useSettingsStore } from '@/stores/settings.store';
export default defineComponent({
name: 'code-node-editor',
@@ -156,7 +157,7 @@ export default defineComponent({
},
},
computed: {
...mapStores(useRootStore, usePostHog),
...mapStores(useRootStore, usePostHog, useSettingsStore),
aiEnabled(): boolean {
const isAiExperimentEnabled = [ASK_AI_EXPERIMENT.gpt3, ASK_AI_EXPERIMENT.gpt4].includes(
(this.posthogStore.getVariant(ASK_AI_EXPERIMENT.name) ?? '') as string,

View File

@@ -19,10 +19,10 @@
<div v-if="parameterOptions.length > 0 && !isReadOnly" class="param-options">
<n8n-button
v-if="parameter.options.length === 1"
v-if="(parameter.options ?? []).length === 1"
type="tertiary"
block
@click="optionSelected(parameter.options[0].name)"
@click="optionSelected((parameter.options ?? [])[0].name)"
:label="getPlaceholderText"
/>
<div v-else class="add-option">
@@ -36,7 +36,11 @@
<n8n-option
v-for="item in parameterOptions"
:key="item.name"
:label="$locale.nodeText().collectionOptionDisplayName(parameter, item, path)"
:label="
isNodePropertyCollection(item)
? i18n.nodeText().collectionOptionDisplayName(parameter, item, path)
: item.name
"
:value="item.name"
>
</n8n-option>
@@ -47,157 +51,155 @@
</div>
</template>
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
import { mapStores } from 'pinia';
import type { INodeUi, IUpdateInformation } from '@/Interface';
<script lang="ts" setup>
import { ref, computed, defineAsyncComponent } from 'vue';
import type { IUpdateInformation } from '@/Interface';
import type { INodeProperties, INodePropertyOptions } from 'n8n-workflow';
import type {
INodeParameters,
INodeProperties,
INodePropertyCollection,
INodePropertyOptions,
} from 'n8n-workflow';
import { deepCopy } from 'n8n-workflow';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import { get } from 'lodash-es';
import { useNDVStore } from '@/stores/ndv.store';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { useI18n } from '@/composables/useI18n';
const ParameterInputList = defineAsyncComponent(async () => import('./ParameterInputList.vue'));
export default defineComponent({
name: 'CollectionParameter',
mixins: [nodeHelpers],
props: [
'hideDelete', // boolean
'nodeValues', // NodeParameters
'parameter', // INodeProperties
'path', // string
'values', // NodeParameters
'isReadOnly', // boolean
],
components: {
ParameterInputList,
},
data() {
return {
selectedOption: undefined,
};
},
computed: {
...mapStores(useNDVStore),
getPlaceholderText(): string {
const placeholder = this.$locale.nodeText().placeholder(this.parameter, this.path);
return placeholder ? placeholder : this.$locale.baseText('collectionParameter.choose');
},
getProperties(): INodeProperties[] {
const returnProperties = [];
let tempProperties;
for (const name of this.propertyNames) {
tempProperties = this.getOptionProperties(name);
if (tempProperties !== undefined) {
returnProperties.push(...tempProperties);
}
}
return returnProperties;
},
// Returns all the options which should be displayed
filteredOptions(): Array<INodePropertyOptions | INodeProperties> {
return (this.parameter.options as Array<INodePropertyOptions | INodeProperties>).filter(
(option) => {
return this.displayNodeParameter(option as INodeProperties);
},
);
},
node(): INodeUi | null {
return this.ndvStore.activeNode;
},
// Returns all the options which did not get added already
parameterOptions(): Array<INodePropertyOptions | INodeProperties> {
return this.filteredOptions.filter((option) => {
return !this.propertyNames.includes(option.name);
});
},
propertyNames(): string[] {
if (this.values) {
return Object.keys(this.values);
}
return [];
},
},
methods: {
getArgument(argumentName: string): string | number | boolean | undefined {
if (this.parameter.typeOptions === undefined) {
return undefined;
}
const selectedOption = ref<string | undefined>(undefined);
export interface Props {
hideDelete?: boolean;
nodeValues: INodeParameters;
parameter: INodeProperties;
path: string;
values: INodeProperties;
isReadOnly?: boolean;
}
const emit = defineEmits<{
(event: 'valueChanged', value: IUpdateInformation): void;
}>();
const props = defineProps<Props>();
const ndvStore = useNDVStore();
const i18n = useI18n();
const nodeHelpers = useNodeHelpers();
if (this.parameter.typeOptions[argumentName] === undefined) {
return undefined;
}
return this.parameter.typeOptions[argumentName];
},
getOptionProperties(optionName: string): INodeProperties[] {
const properties: INodeProperties[] = [];
for (const option of this.parameter.options) {
if (option.name === optionName) {
properties.push(option);
}
}
return properties;
},
displayNodeParameter(parameter: INodeProperties) {
if (parameter.displayOptions === undefined) {
// If it is not defined no need to do a proper check
return true;
}
return this.displayParameter(this.nodeValues, parameter, this.path, this.node);
},
optionSelected(optionName: string) {
const options = this.getOptionProperties(optionName);
if (options.length === 0) {
return;
}
const option = options[0];
const name = `${this.path}.${option.name}`;
let parameterData;
if (option.typeOptions !== undefined && option.typeOptions.multipleValues === true) {
// Multiple values are allowed
let newValue;
if (option.type === 'fixedCollection') {
// The "fixedCollection" entries are different as they save values
// in an object and then underneath there is an array. So initialize
// them differently.
newValue = get(this.nodeValues, `${this.path}.${optionName}`, {});
} else {
// Everything else saves them directly as an array.
newValue = get(this.nodeValues, `${this.path}.${optionName}`, []);
newValue.push(deepCopy(option.default));
}
parameterData = {
name,
value: newValue,
};
} else {
// Add a new option
parameterData = {
name,
value: deepCopy(option.default),
};
}
this.$emit('valueChanged', parameterData);
this.selectedOption = undefined;
},
valueChanged(parameterData: IUpdateInformation) {
this.$emit('valueChanged', parameterData);
},
},
const getPlaceholderText = computed(() => {
return (
i18n.nodeText().placeholder(props.parameter, props.path) ??
i18n.baseText('collectionParameter.choose')
);
});
function isNodePropertyCollection(
object: INodePropertyOptions | INodeProperties | INodePropertyCollection,
): object is INodePropertyCollection {
return 'values' in object;
}
function displayNodeParameter(parameter: INodeProperties) {
if (parameter.displayOptions === undefined) {
// If it is not defined no need to do a proper check
return true;
}
return nodeHelpers.displayParameter(props.nodeValues, parameter, props.path, ndvStore.activeNode);
}
function getOptionProperties(optionName: string) {
const properties = [];
for (const option of props.parameter.options ?? []) {
if (option.name === optionName) {
properties.push(option);
}
}
return properties;
}
const propertyNames = computed<string[]>(() => {
if (props.values) {
return Object.keys(props.values);
}
return [];
});
const getProperties = computed(() => {
const returnProperties = [];
let tempProperties;
for (const name of propertyNames.value) {
tempProperties = getOptionProperties(name);
if (tempProperties !== undefined) {
returnProperties.push(...tempProperties);
}
}
return returnProperties;
});
const filteredOptions = computed<Array<INodePropertyOptions | INodeProperties>>(() => {
return (props.parameter.options as Array<INodePropertyOptions | INodeProperties>).filter(
(option) => {
return displayNodeParameter(option as INodeProperties);
},
);
});
const parameterOptions = computed(() => {
return filteredOptions.value.filter((option) => {
return !propertyNames.value.includes(option.name);
});
});
function optionSelected(optionName: string) {
const options = getOptionProperties(optionName);
if (options.length === 0) {
return;
}
const option = options[0];
const name = `${props.path}.${option.name}`;
let parameterData;
if (
'typeOptions' in option &&
option.typeOptions !== undefined &&
option.typeOptions.multipleValues === true
) {
// Multiple values are allowed
let newValue;
if (option.type === 'fixedCollection') {
// The "fixedCollection" entries are different as they save values
// in an object and then underneath there is an array. So initialize
// them differently.
const retrievedObjectValue = get(props.nodeValues, `${props.path}.${optionName}`, {});
newValue = retrievedObjectValue;
} else {
// Everything else saves them directly as an array.
const retrievedArrayValue = get(props.nodeValues, `${props.path}.${optionName}`, []) as Array<
typeof option.default
>;
if (Array.isArray(retrievedArrayValue)) {
newValue = retrievedArrayValue;
newValue.push(deepCopy(option.default));
}
}
parameterData = {
name,
value: newValue,
};
} else {
// Add a new option
parameterData = {
name,
value: 'default' in option ? deepCopy(option.default) : null,
};
}
emit('valueChanged', parameterData);
selectedOption.value = undefined;
}
function valueChanged(parameterData: IUpdateInformation) {
emit('valueChanged', parameterData);
}
</script>
<style lang="scss">

View File

@@ -135,10 +135,9 @@ import type {
import { NodeHelpers } from 'n8n-workflow';
import CredentialIcon from '@/components/CredentialIcon.vue';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import { useMessage } from '@/composables/useMessage';
import { useToast } from '@/composables/useToast';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { useMessage } from '@/composables/useMessage';
import CredentialConfig from '@/components/CredentialEdit/CredentialConfig.vue';
import CredentialInfo from '@/components/CredentialEdit/CredentialInfo.vue';
import CredentialSharing from '@/components/CredentialEdit/CredentialSharing.ee.vue';
@@ -157,6 +156,8 @@ import { useUsersStore } from '@/stores/users.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useNDVStore } from '@/stores/ndv.store';
import { useCredentialsStore } from '@/stores/credentials.store';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import {
getNodeAuthOptions,
getNodeCredentialForSelectedAuthType,
@@ -172,7 +173,6 @@ interface NodeAccessMap {
export default defineComponent({
name: 'CredentialEdit',
mixins: [nodeHelpers],
components: {
CredentialSharing,
CredentialConfig,
@@ -197,10 +197,13 @@ export default defineComponent({
},
},
setup() {
const nodeHelpers = useNodeHelpers();
return {
externalHooks: useExternalHooks(),
...useToast(),
...useMessage(),
nodeHelpers,
};
},
data() {
@@ -296,6 +299,7 @@ export default defineComponent({
useUIStore,
useUsersStore,
useWorkflowsStore,
useNodeTypesStore,
),
activeNodeType(): INodeTypeDescription | null {
const activeNode = this.ndvStore.activeNode;
@@ -577,7 +581,12 @@ export default defineComponent({
return true;
}
return this.displayParameter(this.credentialData as INodeParameters, parameter, '', null);
return this.nodeHelpers.displayParameter(
this.credentialData as INodeParameters,
parameter,
'',
null,
);
},
getCredentialProperties(name: string): INodeProperties[] {
const credentialTypeData = this.credentialsStore.getCredentialTypeByName(name);
@@ -957,7 +966,7 @@ export default defineComponent({
// Now that the credentials changed check if any nodes use credentials
// which have now a different name
this.updateNodesCredentialsIssues();
this.nodeHelpers.updateNodesCredentialsIssues();
return credential;
},
@@ -1004,7 +1013,7 @@ export default defineComponent({
this.isDeleting = false;
// Now that the credentials were removed check if any nodes used them
this.updateNodesCredentialsIssues();
this.nodeHelpers.updateNodesCredentialsIssues();
this.credentialData = {};
this.showMessage({

View File

@@ -157,10 +157,8 @@ import {
WAIT_TIME_UNLIMITED,
} from '@/constants';
import { nodeBase } from '@/mixins/nodeBase';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { pinData } from '@/mixins/pinData';
import type {
ConnectionTypes,
IExecutionsSummary,
@@ -186,6 +184,7 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { EnableNodeToggleCommand } from '@/models/history';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { type ContextMenuTarget, useContextMenu } from '@/composables/useContextMenu';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { useExternalHooks } from '@/composables/useExternalHooks';
export default defineComponent({
@@ -193,10 +192,11 @@ export default defineComponent({
setup() {
const contextMenu = useContextMenu();
const externalHooks = useExternalHooks();
const nodeHelpers = useNodeHelpers();
return { contextMenu, externalHooks };
return { contextMenu, externalHooks, nodeHelpers };
},
mixins: [nodeBase, nodeHelpers, workflowHelpers, pinData, debounceHelper],
mixins: [nodeBase, workflowHelpers, pinData, debounceHelper],
components: {
TitledList,
FontAwesomeIcon,
@@ -631,7 +631,9 @@ export default defineComponent({
// and ends up bogging down the UI with big workflows, for example when pasting a workflow or even opening a node...
// so we only update it when necessary (when node is mounted and when it's opened and closed (isActive))
try {
const nodeSubtitle = this.getNodeSubtitle(this.data, this.nodeType, this.workflow) || '';
const nodeSubtitle =
this.nodeHelpers.getNodeSubtitle(this.data, this.nodeType, this.getCurrentWorkflow()) ||
'';
this.nodeSubtitle = nodeSubtitle.includes(CUSTOM_API_CALL_KEY) ? '' : nodeSubtitle;
} catch (e) {
@@ -640,7 +642,7 @@ export default defineComponent({
},
disableNode() {
if (this.data !== null) {
this.disableNodes([this.data]);
this.nodeHelpers.disableNodes([this.data]);
this.historyStore.pushCommandToUndo(
new EnableNodeToggleCommand(
this.data.name,

View File

@@ -118,7 +118,7 @@ import type {
} from 'n8n-workflow';
import { genericHelpers } from '@/mixins/genericHelpers';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { useToast } from '@/composables/useToast';
import TitledList from '@/components/TitledList.vue';
@@ -144,7 +144,7 @@ interface CredentialDropdownOption extends ICredentialsResponse {
export default defineComponent({
name: 'NodeCredentials',
mixins: [genericHelpers, nodeHelpers],
mixins: [genericHelpers],
props: {
readonly: {
type: Boolean,
@@ -170,8 +170,11 @@ export default defineComponent({
TitledList,
},
setup() {
const nodeHelpers = useNodeHelpers();
return {
...useToast(),
nodeHelpers,
};
},
data() {
@@ -291,8 +294,6 @@ export default defineComponent({
});
},
credentialTypesNodeDescription(): INodeCredentialDescription[] {
const node = this.node;
const credType = this.credentialsStore.getCredentialTypeByName(this.overrideCredType);
if (credType) return [credType];
@@ -433,7 +434,7 @@ export default defineComponent({
this.$telemetry.track('User selected credential from node modal', {
credential_type: credentialType,
node_type: this.node.type,
...(this.hasProxyAuth(this.node) ? { is_service_specific: true } : {}),
...(this.nodeHelpers.hasProxyAuth(this.node) ? { is_service_specific: true } : {}),
workflow_id: this.workflowsStore.workflowId,
credential_id: credentialId,
});
@@ -461,7 +462,7 @@ export default defineComponent({
invalid: oldCredentials,
type: selectedCredentialsType,
});
this.updateNodesCredentialsIssues();
this.nodeHelpers.updateNodesCredentialsIssues();
this.showMessage({
title: this.$locale.baseText('nodeCredentials.showMessage.title'),
message: this.$locale.baseText('nodeCredentials.showMessage.message', {
@@ -512,7 +513,12 @@ export default defineComponent({
// If it is not defined no need to do a proper check
return true;
}
return this.displayParameter(this.node.parameters, credentialTypeDescription, '', this.node);
return this.nodeHelpers.displayParameter(
this.node.parameters,
credentialTypeDescription,
'',
this.node,
);
},
getIssues(credentialTypeName: string): string[] {

View File

@@ -146,8 +146,6 @@ import type {
} from 'n8n-workflow';
import { jsonParse, NodeHelpers, NodeConnectionType } from 'n8n-workflow';
import type { IExecutionResponse, INodeUi, IUpdateInformation, TargetItem } from '@/Interface';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import NodeSettings from '@/components/NodeSettings.vue';
@@ -173,12 +171,13 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { useUIStore } from '@/stores/ui.store';
import { useSettingsStore } from '@/stores/settings.store';
import { useDeviceSupport } from 'n8n-design-system/composables/useDeviceSupport';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { useMessage } from '@/composables/useMessage';
import { useExternalHooks } from '@/composables/useExternalHooks';
export default defineComponent({
name: 'NodeDetailsView',
mixins: [nodeHelpers, workflowHelpers, workflowActivate, pinData],
mixins: [workflowHelpers, workflowActivate, pinData],
components: {
NodeSettings,
InputPanel,
@@ -200,9 +199,11 @@ export default defineComponent({
},
setup(props, ctx) {
const externalHooks = useExternalHooks();
const nodeHelpers = useNodeHelpers();
return {
externalHooks,
nodeHelpers,
...useDeviceSupport(),
...useMessage(),
// eslint-disable-next-line @typescript-eslint/no-misused-promises
@@ -471,14 +472,18 @@ export default defineComponent({
setTimeout(() => this.ndvStore.setNDVSessionId(), 0);
void this.externalHooks.run('dataDisplay.nodeTypeChanged', {
nodeSubtitle: this.getNodeSubtitle(node, this.activeNodeType, this.getCurrentWorkflow()),
nodeSubtitle: this.nodeHelpers.getNodeSubtitle(
node,
this.activeNodeType,
this.getCurrentWorkflow(),
),
});
setTimeout(() => {
if (this.activeNode) {
const outgoingConnections = this.workflowsStore.outgoingConnectionsByNodeName(
this.activeNode.name,
) as INodeConnections;
);
this.$telemetry.track('User opened node modal', {
node_type: this.activeNodeType ? this.activeNodeType.name : '',
@@ -493,8 +498,7 @@ export default defineComponent({
: this.ndvStore.inputPanelDisplayMode,
selected_view_outputs: this.ndvStore.outputPanelDisplayMode,
input_connectors: this.parentNodes.length,
output_connectors:
outgoingConnections && outgoingConnections.main && outgoingConnections.main.length,
output_connectors: outgoingConnections?.main?.length,
input_displayed_run_index: this.inputRun,
output_displayed_run_index: this.outputRun,
data_pinning_tooltip_presented: this.pinDataDiscoveryTooltipVisible,

View File

@@ -119,7 +119,7 @@
</div>
<div
v-if="isCustomApiCallSelected(nodeValues)"
v-if="nodeHelpers.isCustomApiCallSelected(nodeValues)"
class="parameter-item parameter-notice"
data-test-id="node-parameters-http-notice"
>
@@ -201,8 +201,6 @@ import NodeSettingsTabs from '@/components/NodeSettingsTabs.vue';
import NodeWebhooks from '@/components/NodeWebhooks.vue';
import { get, set, unset } from 'lodash-es';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import NodeExecuteButton from './NodeExecuteButton.vue';
import { isCommunityPackageName } from '@/utils/nodeTypesUtils';
import { useUIStore } from '@/stores/ui.store';
@@ -215,10 +213,10 @@ import useWorkflowsEEStore from '@/stores/workflows.ee.store';
import { useCredentialsStore } from '@/stores/credentials.store';
import type { EventBus } from 'n8n-design-system';
import { useExternalHooks } from '@/composables/useExternalHooks';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
export default defineComponent({
name: 'NodeSettings',
mixins: [nodeHelpers],
components: {
NodeTitle,
NodeCredentials,
@@ -228,9 +226,12 @@ export default defineComponent({
NodeExecuteButton,
},
setup() {
const nodeHelpers = useNodeHelpers();
const externalHooks = useExternalHooks();
return {
externalHooks,
nodeHelpers,
};
},
computed: {
@@ -679,7 +680,7 @@ export default defineComponent({
if (node) {
// Update the issues
this.updateNodeCredentialIssues(node);
this.nodeHelpers.updateNodeCredentialIssues(node);
}
void this.externalHooks.run('nodeSettings.credentialSelected', { updateInformation });
@@ -813,8 +814,8 @@ export default defineComponent({
this.workflowsStore.setNodeParameters(updateInformation);
this.updateNodeParameterIssuesByName(node.name);
this.updateNodeCredentialIssuesByName(node.name);
this.nodeHelpers.updateNodeParameterIssuesByName(node.name);
this.nodeHelpers.updateNodeCredentialIssuesByName(node.name);
}
} else if (parameterData.name.startsWith('parameters.')) {
// A node parameter changed
@@ -896,8 +897,8 @@ export default defineComponent({
oldNodeParameters,
});
this.updateNodeParameterIssuesByName(node.name);
this.updateNodeCredentialIssuesByName(node.name);
this.nodeHelpers.updateNodeParameterIssuesByName(node.name);
this.nodeHelpers.updateNodeCredentialIssuesByName(node.name);
this.$telemetry.trackNodeParametersValuesChange(nodeType.name, parameterData);
} else {
// A property on the node itself changed
@@ -1060,7 +1061,7 @@ export default defineComponent({
this.setNodeValues();
this.eventBus?.on('openSettings', this.openSettings);
this.updateNodeParameterIssues(this.node as INodeUi, this.nodeType);
this.nodeHelpers.updateNodeParameterIssues(this.node as INodeUi, this.nodeType);
},
beforeUnmount() {
this.eventBus?.off('openSettings', this.openSettings);

View File

@@ -395,7 +395,7 @@ import TextEdit from '@/components/TextEdit.vue';
import CodeNodeEditor from '@/components/CodeNodeEditor/CodeNodeEditor.vue';
import HtmlEditor from '@/components/HtmlEditor/HtmlEditor.vue';
import SqlEditor from '@/components/SqlEditor/SqlEditor.vue';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { hasExpressionMapping, isValueExpression } from '@/utils/nodeTypesUtils';
import { isResourceLocatorValue } from '@/utils/typeGuards';
@@ -417,6 +417,7 @@ import { useSettingsStore } from '@/stores/settings.store';
import { htmlEditorEventBus } from '@/event-bus';
import type { EventBus } from 'n8n-design-system/utils';
import { createEventBus } from 'n8n-design-system/utils';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { useI18n } from '@/composables/useI18n';
import type { N8nInput } from 'n8n-design-system';
import { isCredentialOnlyNodeType } from '@/utils/credentialOnlyNodes';
@@ -426,7 +427,7 @@ type Picker = { $emit: (arg0: string, arg1: Date) => void };
export default defineComponent({
name: 'parameter-input',
mixins: [nodeHelpers, workflowHelpers, debounceHelper],
mixins: [workflowHelpers, debounceHelper],
components: {
CodeNodeEditor,
HtmlEditor,
@@ -505,10 +506,12 @@ export default defineComponent({
setup() {
const externalHooks = useExternalHooks();
const i18n = useI18n();
const nodeHelpers = useNodeHelpers();
return {
externalHooks,
i18n,
nodeHelpers,
};
},
data() {
@@ -881,7 +884,7 @@ export default defineComponent({
if (node) {
// Update the issues
this.updateNodeCredentialIssues(node);
this.nodeHelpers.updateNodeCredentialIssues(node);
}
void this.externalHooks.run('nodeSettings.credentialSelected', { updateInformation });

View File

@@ -14,7 +14,7 @@
>
<multiple-parameter
:parameter="parameter"
:values="getParameterValue(nodeValues, parameter.name, path)"
:values="nodeHelpers.getParameterValue(nodeValues, parameter.name, path)"
:nodeValues="nodeValues"
:path="getPath(parameter.name)"
:isReadOnly="isReadOnly"
@@ -71,7 +71,7 @@
<collection-parameter
v-if="parameter.type === 'collection'"
:parameter="parameter"
:values="getParameterValue(nodeValues, parameter.name, path)"
:values="nodeHelpers.getParameterValue(nodeValues, parameter.name, path)"
:nodeValues="nodeValues"
:path="getPath(parameter.name)"
:isReadOnly="isReadOnly"
@@ -80,7 +80,7 @@
<fixed-collection-parameter
v-else-if="parameter.type === 'fixedCollection'"
:parameter="parameter"
:values="getParameterValue(nodeValues, parameter.name, path)"
:values="nodeHelpers.getParameterValue(nodeValues, parameter.name, path)"
:nodeValues="nodeValues"
:path="getPath(parameter.name)"
:isReadOnly="isReadOnly"
@@ -118,7 +118,7 @@
<parameter-input-full
:parameter="parameter"
:hide-issues="hiddenIssuesInputs.includes(parameter.name)"
:value="getParameterValue(nodeValues, parameter.name, path)"
:value="nodeHelpers.getParameterValue(nodeValues, parameter.name, path)"
:displayOptions="shouldShowOptions(parameter)"
:path="getPath(parameter.name)"
:isReadOnly="isReadOnly"
@@ -164,6 +164,7 @@ import {
} from '@/utils/nodeTypesUtils';
import { get, set } from 'lodash-es';
import { nodeViewEventBus } from '@/event-bus';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
const FixedCollectionParameter = defineAsyncComponent(
async () => import('./FixedCollectionParameter.vue'),
@@ -181,6 +182,13 @@ export default defineComponent({
ImportParameter,
ResourceMapper,
},
setup() {
const nodeHelpers = useNodeHelpers();
return {
nodeHelpers,
};
},
props: {
nodeValues: {
type: Object as PropType<INodeParameters>,
@@ -348,7 +356,7 @@ export default defineComponent({
}
if (
this.isCustomApiCallSelected(this.nodeValues) &&
this.nodeHelpers.isCustomApiCallSelected(this.nodeValues) &&
this.mustHideDuringCustomApiCall(parameter, this.nodeValues)
) {
return false;
@@ -422,13 +430,13 @@ export default defineComponent({
if (this.path) {
rawValues = deepCopy(this.nodeValues);
set(rawValues, this.path, nodeValues);
return this.displayParameter(rawValues, parameter, this.path, this.node);
return this.nodeHelpers.displayParameter(rawValues, parameter, this.path, this.node);
} else {
return this.displayParameter(nodeValues, parameter, '', this.node);
return this.nodeHelpers.displayParameter(nodeValues, parameter, '', this.node);
}
}
return this.displayParameter(this.nodeValues, parameter, this.path, this.node);
return this.nodeHelpers.displayParameter(this.nodeValues, parameter, this.path, this.node);
},
valueChanged(parameterData: IUpdateInformation): void {
this.$emit('valueChanged', parameterData);

View File

@@ -149,7 +149,6 @@ import DraggableTarget from '@/components/DraggableTarget.vue';
import ExpressionParameterInput from '@/components/ExpressionParameterInput.vue';
import ParameterIssues from '@/components/ParameterIssues.vue';
import { debounceHelper } from '@/mixins/debounce';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useRootStore } from '@/stores/n8nRoot.store';
import { useNDVStore } from '@/stores/ndv.store';
@@ -185,7 +184,7 @@ interface IResourceLocatorQuery {
export default defineComponent({
name: 'resource-locator',
mixins: [debounceHelper, workflowHelpers, nodeHelpers],
mixins: [debounceHelper, workflowHelpers],
components: {
DraggableTarget,
ExpressionParameterInput,

View File

@@ -606,7 +606,6 @@ import BinaryDataDisplay from '@/components/BinaryDataDisplay.vue';
import NodeErrorView from '@/components/Error/NodeErrorView.vue';
import { genericHelpers } from '@/mixins/genericHelpers';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import { pinData } from '@/mixins/pinData';
import type { PinDataSource } from '@/mixins/pinData';
import CodeNodeEditor from '@/components/CodeNodeEditor/CodeNodeEditor.vue';
@@ -617,6 +616,7 @@ import { searchInObject } from '@/utils/objectUtils';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useNDVStore } from '@/stores/ndv.store';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { useToast } from '@/composables/useToast';
import { isObject } from 'lodash-es';
import { useExternalHooks } from '@/composables/useExternalHooks';
@@ -633,7 +633,7 @@ export type EnterEditModeArgs = {
export default defineComponent({
name: 'RunData',
mixins: [genericHelpers, nodeHelpers, pinData],
mixins: [genericHelpers, pinData],
components: {
BinaryDataDisplay,
NodeErrorView,
@@ -699,11 +699,13 @@ export default defineComponent({
},
},
setup() {
const nodeHelpers = useNodeHelpers();
const externalHooks = useExternalHooks();
return {
externalHooks,
...useToast(),
externalHooks,
nodeHelpers,
};
},
data() {
@@ -931,7 +933,7 @@ export default defineComponent({
return [];
}
const binaryData = this.getBinaryData(
const binaryData = this.nodeHelpers.getBinaryData(
this.workflowRunData,
this.node.name,
this.runIndex,
@@ -1141,7 +1143,7 @@ export default defineComponent({
this.$telemetry.track('User clicked pin data icon', telemetryPayload);
}
this.updateNodeParameterIssues(this.node);
this.nodeHelpers.updateNodeParameterIssues(this.node);
if (this.hasPinData) {
this.unsetPinData(this.node, source);
@@ -1282,7 +1284,7 @@ export default defineComponent({
let inputData: INodeExecutionData[] = [];
if (this.node) {
inputData = this.getNodeInputData(
inputData = this.nodeHelpers.getNodeInputData(
this.node,
runIndex,
outputIndex,
@@ -1365,7 +1367,7 @@ export default defineComponent({
},
clearExecutionData() {
this.workflowsStore.setWorkflowExecutionData(null);
this.updateNodesExecutionIssues();
this.nodeHelpers.updateNodesExecutionIssues();
},
isViewable(index: number, key: string): boolean {
const { fileType } = this.binaryData[index][key];

View File

@@ -43,14 +43,14 @@ import type { INodeUi } from '@/Interface';
import type { IDataObject } from 'n8n-workflow';
import { copyPaste } from '@/mixins/copyPaste';
import { pinData } from '@/mixins/pinData';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import { genericHelpers } from '@/mixins/genericHelpers';
import { clearJsonKey, convertPath } from '@/utils/typesUtils';
import { executionDataToJson } from '@/utils/nodeTypesUtils';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useNDVStore } from '@/stores/ndv.store';
import { useI18n } from '@/composables/useI18n';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { useToast } from '@/composables/useToast';
import { useI18n } from '@/composables/useI18n';
import { nonExistingJsonPath } from '@/constants';
type JsonPathData = {
@@ -60,7 +60,7 @@ type JsonPathData = {
export default defineComponent({
name: 'run-data-json-actions',
mixins: [genericHelpers, nodeHelpers, pinData, copyPaste],
mixins: [genericHelpers, pinData, copyPaste],
props: {
node: {
@@ -95,9 +95,10 @@ export default defineComponent({
},
setup() {
const i18n = useI18n();
const nodeHelpers = useNodeHelpers();
return {
i18n,
nodeHelpers,
...useToast(),
};
},
@@ -121,7 +122,7 @@ export default defineComponent({
selectedValue = clearJsonKey(this.pinData as object);
} else {
selectedValue = executionDataToJson(
this.getNodeInputData(this.node, this.runIndex, this.currentOutputIndex),
this.nodeHelpers.getNodeInputData(this.node, this.runIndex, this.currentOutputIndex),
);
}
}

View File

@@ -105,7 +105,6 @@ import { defineComponent, ref } from 'vue';
import { mapStores } from 'pinia';
import { nodeBase } from '@/mixins/nodeBase';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { isNumber, isString } from '@/utils/typeGuards';
import type {
@@ -125,7 +124,7 @@ import { useContextMenu } from '@/composables/useContextMenu';
export default defineComponent({
name: 'Sticky',
mixins: [nodeBase, nodeHelpers, workflowHelpers],
mixins: [nodeBase, workflowHelpers],
setup() {
const colorPopoverTrigger = ref<HTMLDivElement>();
const forceActions = ref(false);