mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-10 06:34:05 -08:00
refactor(editor): Migrate nodeTypes.store
to use composition API (no-changelog) (#9795)
This commit is contained in:
parent
15d631c412
commit
89b8d04fcd
|
@ -1,46 +1,37 @@
|
|||
import {
|
||||
getNodeParameterOptions,
|
||||
getNodesInformation,
|
||||
getNodeTranslationHeaders,
|
||||
getNodeTypes,
|
||||
getResourceLocatorResults,
|
||||
getResourceMapperFields,
|
||||
} from '@/api/nodeTypes';
|
||||
import * as nodeTypesApi from '@/api/nodeTypes';
|
||||
import { HTTP_REQUEST_NODE_TYPE, STORES, CREDENTIAL_ONLY_HTTP_NODE_VERSION } from '@/constants';
|
||||
import type { INodeTypesState, DynamicNodeParameters } from '@/Interface';
|
||||
import type { DynamicNodeParameters, NodeTypesByTypeNameAndVersion } from '@/Interface';
|
||||
import { addHeaders, addNodeTranslation } from '@/plugins/i18n';
|
||||
import { omit } from '@/utils/typesUtils';
|
||||
import type {
|
||||
ConnectionTypes,
|
||||
INode,
|
||||
INodeListSearchResult,
|
||||
INodeOutputConfiguration,
|
||||
INodePropertyOptions,
|
||||
INodeTypeDescription,
|
||||
INodeTypeNameVersion,
|
||||
ResourceMapperFields,
|
||||
Workflow,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionType, NodeHelpers } from 'n8n-workflow';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useCredentialsStore } from './credentials.store';
|
||||
import { useRootStore } from './root.store';
|
||||
import {
|
||||
getCredentialOnlyNodeType,
|
||||
getCredentialTypeName,
|
||||
isCredentialOnlyNodeType,
|
||||
} from '@/utils/credentialOnlyNodes';
|
||||
import * as utils from '@/utils/credentialOnlyNodes';
|
||||
import { groupNodeTypesByNameAndType } from '@/utils/nodeTypes/nodeTypeTransforms';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
export type NodeTypesStore = ReturnType<typeof useNodeTypesStore>;
|
||||
|
||||
export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
||||
state: (): INodeTypesState => ({
|
||||
nodeTypes: {},
|
||||
}),
|
||||
getters: {
|
||||
allNodeTypes(): INodeTypeDescription[] {
|
||||
return Object.values(this.nodeTypes).reduce<INodeTypeDescription[]>(
|
||||
export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, () => {
|
||||
const nodeTypes = ref<NodeTypesByTypeNameAndVersion>({});
|
||||
|
||||
const rootStore = useRootStore();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// #region Computed
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const allNodeTypes = computed(() => {
|
||||
return Object.values(nodeTypes.value).reduce<INodeTypeDescription[]>(
|
||||
(allNodeTypes, nodeType) => {
|
||||
const versionNumbers = Object.keys(nodeType).map(Number);
|
||||
const allNodeVersions = versionNumbers.map((version) => nodeType[version]);
|
||||
|
@ -49,9 +40,10 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
|||
},
|
||||
[],
|
||||
);
|
||||
},
|
||||
allLatestNodeTypes(): INodeTypeDescription[] {
|
||||
return Object.values(this.nodeTypes).reduce<INodeTypeDescription[]>(
|
||||
});
|
||||
|
||||
const allLatestNodeTypes = computed(() => {
|
||||
return Object.values(nodeTypes.value).reduce<INodeTypeDescription[]>(
|
||||
(allLatestNodeTypes, nodeVersions) => {
|
||||
const versionNumbers = Object.keys(nodeVersions).map(Number);
|
||||
const latestNodeVersion = nodeVersions[Math.max(...versionNumbers)];
|
||||
|
@ -62,14 +54,15 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
|||
},
|
||||
[],
|
||||
);
|
||||
},
|
||||
getNodeType() {
|
||||
});
|
||||
|
||||
const getNodeType = computed(() => {
|
||||
return (nodeTypeName: string, version?: number): INodeTypeDescription | null => {
|
||||
if (isCredentialOnlyNodeType(nodeTypeName)) {
|
||||
return this.getCredentialOnlyNodeType(nodeTypeName, version);
|
||||
if (utils.isCredentialOnlyNodeType(nodeTypeName)) {
|
||||
return getCredentialOnlyNodeType.value(nodeTypeName, version);
|
||||
}
|
||||
|
||||
const nodeVersions = this.nodeTypes[nodeTypeName];
|
||||
const nodeVersions = nodeTypes.value[nodeTypeName];
|
||||
|
||||
if (!nodeVersions) return null;
|
||||
|
||||
|
@ -77,29 +70,32 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
|||
const nodeType = nodeVersions[version ?? Math.max(...versionNumbers)];
|
||||
return nodeType ?? null;
|
||||
};
|
||||
},
|
||||
getNodeVersions() {
|
||||
});
|
||||
|
||||
const getNodeVersions = computed(() => {
|
||||
return (nodeTypeName: string): number[] => {
|
||||
return Object.keys(this.nodeTypes[nodeTypeName] ?? {}).map(Number);
|
||||
return Object.keys(nodeTypes.value[nodeTypeName] ?? {}).map(Number);
|
||||
};
|
||||
},
|
||||
getCredentialOnlyNodeType() {
|
||||
});
|
||||
|
||||
const getCredentialOnlyNodeType = computed(() => {
|
||||
return (nodeTypeName: string, version?: number): INodeTypeDescription | null => {
|
||||
const credentialName = getCredentialTypeName(nodeTypeName);
|
||||
const httpNode = this.getNodeType(
|
||||
const credentialName = utils.getCredentialTypeName(nodeTypeName);
|
||||
const httpNode = getNodeType.value(
|
||||
HTTP_REQUEST_NODE_TYPE,
|
||||
version ?? CREDENTIAL_ONLY_HTTP_NODE_VERSION,
|
||||
);
|
||||
const credential = useCredentialsStore().getCredentialTypeByName(credentialName);
|
||||
return getCredentialOnlyNodeType(httpNode, credential) ?? null;
|
||||
return utils.getCredentialOnlyNodeType(httpNode, credential) ?? null;
|
||||
};
|
||||
},
|
||||
isConfigNode() {
|
||||
});
|
||||
|
||||
const isConfigNode = computed(() => {
|
||||
return (workflow: Workflow, node: INode, nodeTypeName: string): boolean => {
|
||||
if (!workflow.nodes[node.name]) {
|
||||
return false;
|
||||
}
|
||||
const nodeType = this.getNodeType(nodeTypeName);
|
||||
const nodeType = getNodeType.value(nodeTypeName);
|
||||
if (!nodeType) {
|
||||
return false;
|
||||
}
|
||||
|
@ -110,46 +106,34 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
|||
? outputTypes.filter((output) => output !== NodeConnectionType.Main).length > 0
|
||||
: false;
|
||||
};
|
||||
},
|
||||
isConfigurableNode() {
|
||||
return (workflow: Workflow, node: INode, nodeTypeName: string): boolean => {
|
||||
const nodeType = this.getNodeType(nodeTypeName);
|
||||
if (nodeType === null) {
|
||||
return false;
|
||||
}
|
||||
const inputs = NodeHelpers.getNodeInputs(workflow, node, nodeType);
|
||||
const inputTypes = NodeHelpers.getConnectionTypes(inputs);
|
||||
});
|
||||
|
||||
return inputTypes
|
||||
? inputTypes.filter((input) => input !== NodeConnectionType.Main).length > 0
|
||||
: false;
|
||||
};
|
||||
},
|
||||
isTriggerNode() {
|
||||
const isTriggerNode = computed(() => {
|
||||
return (nodeTypeName: string) => {
|
||||
const nodeType = this.getNodeType(nodeTypeName);
|
||||
const nodeType = getNodeType.value(nodeTypeName);
|
||||
return !!(nodeType && nodeType.group.includes('trigger'));
|
||||
};
|
||||
},
|
||||
isCoreNodeType() {
|
||||
});
|
||||
|
||||
const isCoreNodeType = computed(() => {
|
||||
return (nodeType: INodeTypeDescription) => {
|
||||
return nodeType.codex?.categories?.includes('Core Nodes');
|
||||
};
|
||||
},
|
||||
visibleNodeTypes(): INodeTypeDescription[] {
|
||||
return this.allLatestNodeTypes.filter((nodeType: INodeTypeDescription) => !nodeType.hidden);
|
||||
},
|
||||
/**
|
||||
* Getter for node default names ending with a number: `'S3'`, `'Magento 2'`, etc.
|
||||
*/
|
||||
nativelyNumberSuffixedDefaults(): string[] {
|
||||
return this.allNodeTypes.reduce<string[]>((acc, cur) => {
|
||||
});
|
||||
|
||||
const visibleNodeTypes = computed(() => {
|
||||
return allLatestNodeTypes.value.filter((nodeType: INodeTypeDescription) => !nodeType.hidden);
|
||||
});
|
||||
|
||||
const nativelyNumberSuffixedDefaults = computed(() => {
|
||||
return allNodeTypes.value.reduce<string[]>((acc, cur) => {
|
||||
if (/\d$/.test(cur.defaults.name as string)) acc.push(cur.defaults.name as string);
|
||||
return acc;
|
||||
}, []);
|
||||
},
|
||||
visibleNodeTypesByOutputConnectionTypeNames(): { [key: string]: string[] } {
|
||||
const nodesByOutputType = this.visibleNodeTypes.reduce(
|
||||
});
|
||||
|
||||
const visibleNodeTypesByOutputConnectionTypeNames = computed(() => {
|
||||
const nodesByOutputType = visibleNodeTypes.value.reduce(
|
||||
(acc, node) => {
|
||||
const outputTypes = node.outputs;
|
||||
if (Array.isArray(outputTypes)) {
|
||||
|
@ -188,9 +172,10 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
|||
);
|
||||
|
||||
return nodesByOutputType;
|
||||
},
|
||||
visibleNodeTypesByInputConnectionTypeNames(): { [key: string]: string[] } {
|
||||
const nodesByOutputType = this.visibleNodeTypes.reduce(
|
||||
});
|
||||
|
||||
const visibleNodeTypesByInputConnectionTypeNames = computed(() => {
|
||||
const nodesByOutputType = visibleNodeTypes.value.reduce(
|
||||
(acc, node) => {
|
||||
const inputTypes = node.inputs;
|
||||
if (Array.isArray(inputTypes)) {
|
||||
|
@ -209,28 +194,52 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
|||
);
|
||||
|
||||
return nodesByOutputType;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setNodeTypes(newNodeTypes: INodeTypeDescription[] = []): void {
|
||||
const nodeTypes = groupNodeTypesByNameAndType(newNodeTypes);
|
||||
this.nodeTypes = {
|
||||
...this.nodeTypes,
|
||||
...nodeTypes,
|
||||
});
|
||||
|
||||
const isConfigurableNode = computed(() => {
|
||||
return (workflow: Workflow, node: INode, nodeTypeName: string): boolean => {
|
||||
const nodeType = getNodeType.value(nodeTypeName);
|
||||
if (nodeType === null) {
|
||||
return false;
|
||||
}
|
||||
const inputs = NodeHelpers.getNodeInputs(workflow, node, nodeType);
|
||||
const inputTypes = NodeHelpers.getConnectionTypes(inputs);
|
||||
|
||||
return inputTypes
|
||||
? inputTypes.filter((input) => input !== NodeConnectionType.Main).length > 0
|
||||
: false;
|
||||
};
|
||||
},
|
||||
removeNodeTypes(nodeTypesToRemove: INodeTypeDescription[]): void {
|
||||
this.nodeTypes = nodeTypesToRemove.reduce(
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// #region Methods
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const setNodeTypes = (newNodeTypes: INodeTypeDescription[] = []) => {
|
||||
const groupedNodeTypes = groupNodeTypesByNameAndType(newNodeTypes);
|
||||
nodeTypes.value = {
|
||||
...nodeTypes.value,
|
||||
...groupedNodeTypes,
|
||||
};
|
||||
};
|
||||
|
||||
const removeNodeTypes = (nodeTypesToRemove: INodeTypeDescription[]) => {
|
||||
nodeTypes.value = nodeTypesToRemove.reduce(
|
||||
(oldNodes, newNodeType) => omit(newNodeType.name, oldNodes),
|
||||
this.nodeTypes,
|
||||
nodeTypes.value,
|
||||
);
|
||||
},
|
||||
async getNodesInformation(
|
||||
};
|
||||
|
||||
const getNodesInformation = async (
|
||||
nodeInfos: INodeTypeNameVersion[],
|
||||
replace = true,
|
||||
): Promise<INodeTypeDescription[]> {
|
||||
const rootStore = useRootStore();
|
||||
const nodesInformation = await getNodesInformation(rootStore.restApiContext, nodeInfos);
|
||||
): Promise<INodeTypeDescription[]> => {
|
||||
const nodesInformation = await nodeTypesApi.getNodesInformation(
|
||||
rootStore.restApiContext,
|
||||
nodeInfos,
|
||||
);
|
||||
|
||||
nodesInformation.forEach((nodeInformation) => {
|
||||
if (nodeInformation.translation) {
|
||||
|
@ -239,59 +248,84 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
|||
addNodeTranslation({ [nodeType]: nodeInformation.translation }, rootStore.defaultLocale);
|
||||
}
|
||||
});
|
||||
if (replace) this.setNodeTypes(nodesInformation);
|
||||
if (replace) setNodeTypes(nodesInformation);
|
||||
|
||||
return nodesInformation;
|
||||
},
|
||||
async getFullNodesProperties(nodesToBeFetched: INodeTypeNameVersion[]): Promise<void> {
|
||||
};
|
||||
|
||||
const getFullNodesProperties = async (nodesToBeFetched: INodeTypeNameVersion[]) => {
|
||||
const credentialsStore = useCredentialsStore();
|
||||
await credentialsStore.fetchCredentialTypes(true);
|
||||
await this.getNodesInformation(nodesToBeFetched);
|
||||
},
|
||||
async getNodeTypes(): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
const nodeTypes = await getNodeTypes(rootStore.baseUrl);
|
||||
await getNodesInformation(nodesToBeFetched);
|
||||
};
|
||||
|
||||
const getNodeTypes = async () => {
|
||||
const nodeTypes = await nodeTypesApi.getNodeTypes(rootStore.baseUrl);
|
||||
if (nodeTypes.length) {
|
||||
this.setNodeTypes(nodeTypes);
|
||||
setNodeTypes(nodeTypes);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Loads node types if they haven't been loaded yet
|
||||
*/
|
||||
async loadNodeTypesIfNotLoaded(): Promise<void> {
|
||||
if (Object.keys(this.nodeTypes).length === 0) {
|
||||
await this.getNodeTypes();
|
||||
};
|
||||
|
||||
const loadNodeTypesIfNotLoaded = async () => {
|
||||
if (Object.keys(nodeTypes.value).length === 0) {
|
||||
await getNodeTypes();
|
||||
}
|
||||
},
|
||||
async getNodeTranslationHeaders(): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
const headers = await getNodeTranslationHeaders(rootStore.restApiContext);
|
||||
};
|
||||
|
||||
const getNodeTranslationHeaders = async () => {
|
||||
const headers = await nodeTypesApi.getNodeTranslationHeaders(rootStore.restApiContext);
|
||||
|
||||
if (headers) {
|
||||
addHeaders(headers, rootStore.defaultLocale);
|
||||
}
|
||||
},
|
||||
async getNodeParameterOptions(
|
||||
sendData: DynamicNodeParameters.OptionsRequest,
|
||||
): Promise<INodePropertyOptions[]> {
|
||||
const rootStore = useRootStore();
|
||||
return await getNodeParameterOptions(rootStore.restApiContext, sendData);
|
||||
},
|
||||
async getResourceLocatorResults(
|
||||
};
|
||||
|
||||
const getNodeParameterOptions = async (sendData: DynamicNodeParameters.OptionsRequest) => {
|
||||
return await nodeTypesApi.getNodeParameterOptions(rootStore.restApiContext, sendData);
|
||||
};
|
||||
|
||||
const getResourceLocatorResults = async (
|
||||
sendData: DynamicNodeParameters.ResourceLocatorResultsRequest,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const rootStore = useRootStore();
|
||||
return await getResourceLocatorResults(rootStore.restApiContext, sendData);
|
||||
},
|
||||
async getResourceMapperFields(
|
||||
) => {
|
||||
return await nodeTypesApi.getResourceLocatorResults(rootStore.restApiContext, sendData);
|
||||
};
|
||||
|
||||
const getResourceMapperFields = async (
|
||||
sendData: DynamicNodeParameters.ResourceMapperFieldsRequest,
|
||||
): Promise<ResourceMapperFields | null> {
|
||||
const rootStore = useRootStore();
|
||||
) => {
|
||||
try {
|
||||
return await getResourceMapperFields(rootStore.restApiContext, sendData);
|
||||
return await nodeTypesApi.getResourceMapperFields(rootStore.restApiContext, sendData);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
return {
|
||||
nodeTypes,
|
||||
allNodeTypes,
|
||||
allLatestNodeTypes,
|
||||
getNodeType,
|
||||
getNodeVersions,
|
||||
getCredentialOnlyNodeType,
|
||||
isConfigNode,
|
||||
isTriggerNode,
|
||||
isCoreNodeType,
|
||||
visibleNodeTypes,
|
||||
nativelyNumberSuffixedDefaults,
|
||||
visibleNodeTypesByOutputConnectionTypeNames,
|
||||
visibleNodeTypesByInputConnectionTypeNames,
|
||||
isConfigurableNode,
|
||||
getResourceMapperFields,
|
||||
getResourceLocatorResults,
|
||||
getNodeParameterOptions,
|
||||
getNodesInformation,
|
||||
getFullNodesProperties,
|
||||
getNodeTypes,
|
||||
loadNodeTypesIfNotLoaded,
|
||||
getNodeTranslationHeaders,
|
||||
setNodeTypes,
|
||||
removeNodeTypes,
|
||||
};
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue