refactor(editor): Apply Prettier (no-changelog) (#4920)
* ⚡ Adjust `format` script * 🔥 Remove exemption for `editor-ui` * 🎨 Prettify * 👕 Fix lint
This commit is contained in:
@@ -7,15 +7,17 @@ import { useWorkflowsStore } from '@/stores/workflows';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||
import { useUIStore } from '@/stores/ui';
|
||||
import { INodeUi, XYPosition } from '@/Interface';
|
||||
import {
|
||||
scaleBigger,
|
||||
scaleReset,
|
||||
scaleSmaller,
|
||||
} from '@/utils';
|
||||
import { scaleBigger, scaleReset, scaleSmaller } from '@/utils';
|
||||
import { START_NODE_TYPE } from '@/constants';
|
||||
import '@/plugins/N8nCustomConnectorType';
|
||||
import '@/plugins/PlusEndpointType';
|
||||
import { DEFAULT_PLACEHOLDER_TRIGGER_BUTTON, getMidCanvasPosition, getNewNodePosition, getZoomToFit, PLACEHOLDER_TRIGGER_NODE_SIZE } from '@/utils/nodeViewUtils';
|
||||
import {
|
||||
DEFAULT_PLACEHOLDER_TRIGGER_BUTTON,
|
||||
getMidCanvasPosition,
|
||||
getNewNodePosition,
|
||||
getZoomToFit,
|
||||
PLACEHOLDER_TRIGGER_NODE_SIZE,
|
||||
} from '@/utils/nodeViewUtils';
|
||||
|
||||
export const useCanvasStore = defineStore('canvas', () => {
|
||||
const workflowStore = useWorkflowsStore();
|
||||
@@ -24,10 +26,10 @@ export const useCanvasStore = defineStore('canvas', () => {
|
||||
const jsPlumbInstance = jsPlumb.getInstance();
|
||||
|
||||
const nodes = computed<INodeUi[]>(() => workflowStore.allNodes);
|
||||
const triggerNodes = computed<INodeUi[]>(
|
||||
() => nodes.value.filter(
|
||||
node => node.type === START_NODE_TYPE || nodeTypesStore.isTriggerNode(node.type),
|
||||
),
|
||||
const triggerNodes = computed<INodeUi[]>(() =>
|
||||
nodes.value.filter(
|
||||
(node) => node.type === START_NODE_TYPE || nodeTypesStore.isTriggerNode(node.type),
|
||||
),
|
||||
);
|
||||
const isDemo = ref<boolean>(false);
|
||||
const nodeViewScale = ref<number>(1);
|
||||
@@ -62,7 +64,7 @@ export const useCanvasStore = defineStore('canvas', () => {
|
||||
};
|
||||
|
||||
const resetZoom = () => {
|
||||
const {scale, offset} = scaleReset({
|
||||
const { scale, offset } = scaleReset({
|
||||
scale: nodeViewScale.value,
|
||||
offset: uiStore.nodeViewOffsetPosition,
|
||||
});
|
||||
@@ -70,7 +72,7 @@ export const useCanvasStore = defineStore('canvas', () => {
|
||||
};
|
||||
|
||||
const zoomIn = () => {
|
||||
const {scale, offset} = scaleBigger({
|
||||
const { scale, offset } = scaleBigger({
|
||||
scale: nodeViewScale.value,
|
||||
offset: uiStore.nodeViewOffsetPosition,
|
||||
});
|
||||
@@ -78,7 +80,7 @@ export const useCanvasStore = defineStore('canvas', () => {
|
||||
};
|
||||
|
||||
const zoomOut = () => {
|
||||
const {scale, offset} = scaleSmaller({
|
||||
const { scale, offset } = scaleSmaller({
|
||||
scale: nodeViewScale.value,
|
||||
offset: uiStore.nodeViewOffsetPosition,
|
||||
});
|
||||
@@ -87,18 +89,21 @@ export const useCanvasStore = defineStore('canvas', () => {
|
||||
|
||||
const zoomToFit = () => {
|
||||
const nodes = getNodesWithPlaceholderNode();
|
||||
if (!nodes.length) { // some unknown workflow executions
|
||||
if (!nodes.length) {
|
||||
// some unknown workflow executions
|
||||
return;
|
||||
}
|
||||
const {zoomLevel, offset} = getZoomToFit(nodes, !isDemo.value);
|
||||
const { zoomLevel, offset } = getZoomToFit(nodes, !isDemo.value);
|
||||
setZoomLevel(zoomLevel, offset);
|
||||
};
|
||||
|
||||
const wheelMoveWorkflow = (e: WheelEvent) => {
|
||||
const normalized = normalizeWheel(e);
|
||||
const offsetPosition = uiStore.nodeViewOffsetPosition;
|
||||
const nodeViewOffsetPositionX = offsetPosition[0] - (e.shiftKey ? normalized.pixelY : normalized.pixelX);
|
||||
const nodeViewOffsetPositionY = offsetPosition[1] - (e.shiftKey ? normalized.pixelX : normalized.pixelY);
|
||||
const nodeViewOffsetPositionX =
|
||||
offsetPosition[0] - (e.shiftKey ? normalized.pixelY : normalized.pixelX);
|
||||
const nodeViewOffsetPositionY =
|
||||
offsetPosition[1] - (e.shiftKey ? normalized.pixelX : normalized.pixelY);
|
||||
uiStore.nodeViewOffsetPosition = [nodeViewOffsetPositionX, nodeViewOffsetPositionY];
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { getInstalledCommunityNodes, installNewPackage, uninstallPackage, updatePackage } from "@/api/communityNodes";
|
||||
import { getAvailableCommunityPackageCount } from "@/api/settings";
|
||||
import { defineStore } from "pinia";
|
||||
import { useRootStore } from "./n8nRootStore";
|
||||
import {
|
||||
getInstalledCommunityNodes,
|
||||
installNewPackage,
|
||||
uninstallPackage,
|
||||
updatePackage,
|
||||
} from '@/api/communityNodes';
|
||||
import { getAvailableCommunityPackageCount } from '@/api/settings';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useRootStore } from './n8nRootStore';
|
||||
import { PublicInstalledPackage } from 'n8n-workflow';
|
||||
import Vue from "vue";
|
||||
import { CommunityNodesState, CommunityPackageMap } from "@/Interface";
|
||||
import { STORES } from "@/constants";
|
||||
import Vue from 'vue';
|
||||
import { CommunityNodesState, CommunityPackageMap } from '@/Interface';
|
||||
import { STORES } from '@/constants';
|
||||
|
||||
const LOADER_DELAY = 300;
|
||||
|
||||
@@ -16,8 +21,10 @@ export const useCommunityNodesStore = defineStore(STORES.COMMUNITY_NODES, {
|
||||
installedPackages: {},
|
||||
}),
|
||||
getters: {
|
||||
getInstalledPackages() : PublicInstalledPackage[] {
|
||||
return Object.values(this.installedPackages).sort((a, b) => a.packageName.localeCompare(b.packageName));
|
||||
getInstalledPackages(): PublicInstalledPackage[] {
|
||||
return Object.values(this.installedPackages).sort((a, b) =>
|
||||
a.packageName.localeCompare(b.packageName),
|
||||
);
|
||||
},
|
||||
getInstalledPackageByName() {
|
||||
return (name: string): PublicInstalledPackage => this.installedPackages[name];
|
||||
@@ -33,7 +40,7 @@ export const useCommunityNodesStore = defineStore(STORES.COMMUNITY_NODES, {
|
||||
const rootStore = useRootStore();
|
||||
const installedPackages = await getInstalledCommunityNodes(rootStore.getRestApiContext);
|
||||
this.setInstalledPackages(installedPackages);
|
||||
const timeout = installedPackages.length > 0 ? 0: LOADER_DELAY;
|
||||
const timeout = installedPackages.length > 0 ? 0 : LOADER_DELAY;
|
||||
setTimeout(() => {
|
||||
return;
|
||||
}, timeout);
|
||||
@@ -44,7 +51,7 @@ export const useCommunityNodesStore = defineStore(STORES.COMMUNITY_NODES, {
|
||||
await installNewPackage(rootStore.getRestApiContext, packageName);
|
||||
await this.fetchInstalledPackages();
|
||||
} catch (error) {
|
||||
throw (error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
async uninstallPackage(packageName: string): Promise<void> {
|
||||
@@ -53,24 +60,30 @@ export const useCommunityNodesStore = defineStore(STORES.COMMUNITY_NODES, {
|
||||
await uninstallPackage(rootStore.getRestApiContext, packageName);
|
||||
this.removePackageByName(packageName);
|
||||
} catch (error) {
|
||||
throw (error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
async updatePackage(packageName: string): Promise<void> {
|
||||
try {
|
||||
const rootStore = useRootStore();
|
||||
const packageToUpdate: PublicInstalledPackage = this.getInstalledPackageByName(packageName);
|
||||
const updatedPackage: PublicInstalledPackage = await updatePackage(rootStore.getRestApiContext, packageToUpdate.packageName);
|
||||
const updatedPackage: PublicInstalledPackage = await updatePackage(
|
||||
rootStore.getRestApiContext,
|
||||
packageToUpdate.packageName,
|
||||
);
|
||||
this.updatePackageObject(updatedPackage);
|
||||
} catch (error) {
|
||||
throw (error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
setInstalledPackages(packages: PublicInstalledPackage[]) {
|
||||
this.installedPackages = packages.reduce((packageMap: CommunityPackageMap, pack: PublicInstalledPackage) => {
|
||||
packageMap[pack.packageName] = pack;
|
||||
return packageMap;
|
||||
}, {});
|
||||
this.installedPackages = packages.reduce(
|
||||
(packageMap: CommunityPackageMap, pack: PublicInstalledPackage) => {
|
||||
packageMap[pack.packageName] = pack;
|
||||
return packageMap;
|
||||
},
|
||||
{},
|
||||
);
|
||||
},
|
||||
removePackageByName(name: string): void {
|
||||
Vue.delete(this.installedPackages, name);
|
||||
|
||||
@@ -1,16 +1,40 @@
|
||||
import { createNewCredential, deleteCredential, getAllCredentials, getCredentialData, getCredentialsNewName, getCredentialTypes, oAuth1CredentialAuthorize, oAuth2CredentialAuthorize, testCredential, updateCredential } from "@/api/credentials";
|
||||
import { setCredentialSharedWith } from "@/api/credentials.ee";
|
||||
import { getAppNameFromCredType } from "@/utils";
|
||||
import { EnterpriseEditionFeature, STORES } from "@/constants";
|
||||
import { ICredentialMap, ICredentialsDecryptedResponse, ICredentialsResponse, ICredentialsState, ICredentialTypeMap } from "@/Interface";
|
||||
import { i18n } from "@/plugins/i18n";
|
||||
import { ICredentialsDecrypted, ICredentialType, INodeCredentialTestResult, INodeProperties, INodeTypeDescription, IUser } from "n8n-workflow";
|
||||
import { defineStore } from "pinia";
|
||||
import Vue from "vue";
|
||||
import { useRootStore } from "./n8nRootStore";
|
||||
import { useNodeTypesStore } from "./nodeTypes";
|
||||
import { useSettingsStore } from "./settings";
|
||||
import { useUsersStore } from "./users";
|
||||
import {
|
||||
createNewCredential,
|
||||
deleteCredential,
|
||||
getAllCredentials,
|
||||
getCredentialData,
|
||||
getCredentialsNewName,
|
||||
getCredentialTypes,
|
||||
oAuth1CredentialAuthorize,
|
||||
oAuth2CredentialAuthorize,
|
||||
testCredential,
|
||||
updateCredential,
|
||||
} from '@/api/credentials';
|
||||
import { setCredentialSharedWith } from '@/api/credentials.ee';
|
||||
import { getAppNameFromCredType } from '@/utils';
|
||||
import { EnterpriseEditionFeature, STORES } from '@/constants';
|
||||
import {
|
||||
ICredentialMap,
|
||||
ICredentialsDecryptedResponse,
|
||||
ICredentialsResponse,
|
||||
ICredentialsState,
|
||||
ICredentialTypeMap,
|
||||
} from '@/Interface';
|
||||
import { i18n } from '@/plugins/i18n';
|
||||
import {
|
||||
ICredentialsDecrypted,
|
||||
ICredentialType,
|
||||
INodeCredentialTestResult,
|
||||
INodeProperties,
|
||||
INodeTypeDescription,
|
||||
IUser,
|
||||
} from 'n8n-workflow';
|
||||
import { defineStore } from 'pinia';
|
||||
import Vue from 'vue';
|
||||
import { useRootStore } from './n8nRootStore';
|
||||
import { useNodeTypesStore } from './nodeTypes';
|
||||
import { useSettingsStore } from './settings';
|
||||
import { useUsersStore } from './users';
|
||||
|
||||
const DEFAULT_CREDENTIAL_NAME = 'Unnamed credential';
|
||||
const DEFAULT_CREDENTIAL_POSTFIX = 'account';
|
||||
@@ -26,22 +50,27 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
||||
return this.credentialTypes;
|
||||
},
|
||||
allCredentialTypes(): ICredentialType[] {
|
||||
return Object.values(this.credentialTypes)
|
||||
.sort((a, b) => a.displayName.localeCompare(b.displayName));
|
||||
return Object.values(this.credentialTypes).sort((a, b) =>
|
||||
a.displayName.localeCompare(b.displayName),
|
||||
);
|
||||
},
|
||||
allCredentials(): ICredentialsResponse[] {
|
||||
return Object.values(this.credentials)
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
return Object.values(this.credentials).sort((a, b) => a.name.localeCompare(b.name));
|
||||
},
|
||||
allCredentialsByType(): {[type: string]: ICredentialsResponse[]} {
|
||||
allCredentialsByType(): { [type: string]: ICredentialsResponse[] } {
|
||||
const credentials = this.allCredentials;
|
||||
const types = this.allCredentialTypes;
|
||||
|
||||
return types.reduce((accu: {[type: string]: ICredentialsResponse[]}, type: ICredentialType) => {
|
||||
accu[type.name] = credentials.filter((cred: ICredentialsResponse) => cred.type === type.name);
|
||||
return types.reduce(
|
||||
(accu: { [type: string]: ICredentialsResponse[] }, type: ICredentialType) => {
|
||||
accu[type.name] = credentials.filter(
|
||||
(cred: ICredentialsResponse) => cred.type === type.name,
|
||||
);
|
||||
|
||||
return accu;
|
||||
}, {});
|
||||
return accu;
|
||||
},
|
||||
{},
|
||||
);
|
||||
},
|
||||
getCredentialTypeByName() {
|
||||
return (type: string): ICredentialType => this.credentialTypes[type];
|
||||
@@ -57,7 +86,7 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
||||
},
|
||||
getCredentialsByType() {
|
||||
return (credentialType: string): ICredentialsResponse[] => {
|
||||
return (this.allCredentialsByType[credentialType] || []);
|
||||
return this.allCredentialsByType[credentialType] || [];
|
||||
};
|
||||
},
|
||||
getNodesWithAccess() {
|
||||
@@ -71,7 +100,7 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
||||
}
|
||||
|
||||
for (const credentialTypeDescription of nodeType.credentials) {
|
||||
if (credentialTypeDescription.name === credentialTypeName ) {
|
||||
if (credentialTypeDescription.name === credentialTypeName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -120,11 +149,14 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
||||
},
|
||||
actions: {
|
||||
setCredentialTypes(credentialTypes: ICredentialType[]): void {
|
||||
this.credentialTypes = credentialTypes.reduce((accu: ICredentialTypeMap, cred: ICredentialType) => {
|
||||
accu[cred.name] = cred;
|
||||
this.credentialTypes = credentialTypes.reduce(
|
||||
(accu: ICredentialTypeMap, cred: ICredentialType) => {
|
||||
accu[cred.name] = cred;
|
||||
|
||||
return accu;
|
||||
}, {});
|
||||
return accu;
|
||||
},
|
||||
{},
|
||||
);
|
||||
},
|
||||
setCredentials(credentials: ICredentialsResponse[]): void {
|
||||
this.credentials = credentials.reduce((accu: ICredentialMap, cred: ICredentialsResponse) => {
|
||||
@@ -143,7 +175,10 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
||||
},
|
||||
upsertCredential(credential: ICredentialsResponse): void {
|
||||
if (credential.id) {
|
||||
Vue.set(this.credentials, credential.id, { ...this.credentials[credential.id], ...credential });
|
||||
Vue.set(this.credentials, credential.id, {
|
||||
...this.credentials[credential.id],
|
||||
...credential,
|
||||
});
|
||||
}
|
||||
},
|
||||
enableOAuthCredential(credential: ICredentialsResponse): void {
|
||||
@@ -163,7 +198,11 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
||||
this.setCredentials(credentials);
|
||||
return credentials;
|
||||
},
|
||||
async getCredentialData({ id }: {id: string}): Promise<ICredentialsResponse | ICredentialsDecryptedResponse | undefined> {
|
||||
async getCredentialData({
|
||||
id,
|
||||
}: {
|
||||
id: string;
|
||||
}): Promise<ICredentialsResponse | ICredentialsDecryptedResponse | undefined> {
|
||||
const rootStore = useRootStore();
|
||||
return await getCredentialData(rootStore.getRestApiContext, id);
|
||||
},
|
||||
@@ -194,7 +233,10 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
||||
}
|
||||
return credential;
|
||||
},
|
||||
async updateCredential(params: {data: ICredentialsDecrypted, id: string}): Promise<ICredentialsResponse> {
|
||||
async updateCredential(params: {
|
||||
data: ICredentialsDecrypted;
|
||||
id: string;
|
||||
}): Promise<ICredentialsResponse> {
|
||||
const { id, data } = params;
|
||||
const rootStore = useRootStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
@@ -223,7 +265,7 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
||||
|
||||
return credential;
|
||||
},
|
||||
async deleteCredential({ id }: {id: string}) {
|
||||
async deleteCredential({ id }: { id: string }) {
|
||||
const rootStore = useRootStore();
|
||||
const deleted = await deleteCredential(rootStore.getRestApiContext, id);
|
||||
if (deleted) {
|
||||
@@ -249,7 +291,10 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
||||
if (!TYPES_WITH_DEFAULT_NAME.includes(credentialTypeName)) {
|
||||
const { displayName } = this.getCredentialTypeByName(credentialTypeName);
|
||||
newName = getAppNameFromCredType(displayName);
|
||||
newName = newName.length > 0 ? `${newName} ${DEFAULT_CREDENTIAL_POSTFIX}` : DEFAULT_CREDENTIAL_NAME;
|
||||
newName =
|
||||
newName.length > 0
|
||||
? `${newName} ${DEFAULT_CREDENTIAL_POSTFIX}`
|
||||
: DEFAULT_CREDENTIAL_NAME;
|
||||
}
|
||||
const rootStore = useRootStore();
|
||||
const res = await getCredentialsNewName(rootStore.getRestApiContext, newName);
|
||||
@@ -260,34 +305,31 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
||||
},
|
||||
|
||||
// Enterprise edition actions
|
||||
setCredentialOwnedBy(payload: { credentialId: string, ownedBy: Partial<IUser> }) {
|
||||
setCredentialOwnedBy(payload: { credentialId: string; ownedBy: Partial<IUser> }) {
|
||||
Vue.set(this.credentials[payload.credentialId], 'ownedBy', payload.ownedBy);
|
||||
},
|
||||
async setCredentialSharedWith(payload: { sharedWith: IUser[]; credentialId: string; }) {
|
||||
if(useSettingsStore().isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
|
||||
await setCredentialSharedWith(
|
||||
useRootStore().getRestApiContext,
|
||||
payload.credentialId,
|
||||
{
|
||||
shareWithIds: payload.sharedWith.map((sharee) => sharee.id),
|
||||
},
|
||||
);
|
||||
async setCredentialSharedWith(payload: { sharedWith: IUser[]; credentialId: string }) {
|
||||
if (useSettingsStore().isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
|
||||
await setCredentialSharedWith(useRootStore().getRestApiContext, payload.credentialId, {
|
||||
shareWithIds: payload.sharedWith.map((sharee) => sharee.id),
|
||||
});
|
||||
Vue.set(this.credentials[payload.credentialId], 'sharedWith', payload.sharedWith);
|
||||
}
|
||||
},
|
||||
addCredentialSharee(payload: { credentialId: string, sharee: Partial<IUser> }): void {
|
||||
addCredentialSharee(payload: { credentialId: string; sharee: Partial<IUser> }): void {
|
||||
Vue.set(
|
||||
this.credentials[payload.credentialId],
|
||||
'sharedWith',
|
||||
(this.credentials[payload.credentialId].sharedWith || []).concat([payload.sharee]),
|
||||
);
|
||||
},
|
||||
removeCredentialSharee(payload: { credentialId: string, sharee: Partial<IUser> }): void {
|
||||
removeCredentialSharee(payload: { credentialId: string; sharee: Partial<IUser> }): void {
|
||||
Vue.set(
|
||||
this.credentials[payload.credentialId],
|
||||
'sharedWith',
|
||||
(this.credentials[payload.credentialId].sharedWith || [])
|
||||
.filter((sharee) => sharee.id !== payload.sharee.id),
|
||||
(this.credentials[payload.credentialId].sharedWith || []).filter(
|
||||
(sharee) => sharee.id !== payload.sharee.id,
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { AddConnectionCommand, COMMANDS, RemoveConnectionCommand } from './../models/history';
|
||||
import { BulkCommand, Command, Undoable, MoveNodeCommand } from "@/models/history";
|
||||
import { STORES } from "@/constants";
|
||||
import { HistoryState } from "@/Interface";
|
||||
import { defineStore } from "pinia";
|
||||
import { BulkCommand, Command, Undoable, MoveNodeCommand } from '@/models/history';
|
||||
import { STORES } from '@/constants';
|
||||
import { HistoryState } from '@/Interface';
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
const STACK_LIMIT = 100;
|
||||
|
||||
@@ -24,7 +24,8 @@ export const useHistoryStore = defineStore(STORES.HISTORY, {
|
||||
pushCommandToUndo(undoable: Command, clearRedo = true): void {
|
||||
if (!this.bulkInProgress) {
|
||||
if (this.currentBulkAction) {
|
||||
const alreadyIn = this.currentBulkAction.commands.find(c => c.isEqualTo(undoable)) !== undefined;
|
||||
const alreadyIn =
|
||||
this.currentBulkAction.commands.find((c) => c.isEqualTo(undoable)) !== undefined;
|
||||
if (!alreadyIn) {
|
||||
this.currentBulkAction.commands.push(undoable);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,11 @@ import { useNodeTypesStore } from './nodeTypes';
|
||||
export const useRootStore = defineStore(STORES.ROOT, {
|
||||
state: (): RootState => ({
|
||||
// @ts-ignore
|
||||
baseUrl: import.meta.env.VUE_APP_URL_BASE_API ? import.meta.env.VUE_APP_URL_BASE_API : (window.BASE_PATH === '/%BASE_PATH%/' ? '/' : window.BASE_PATH),
|
||||
baseUrl: import.meta.env.VUE_APP_URL_BASE_API
|
||||
? import.meta.env.VUE_APP_URL_BASE_API
|
||||
: window.BASE_PATH === '/%BASE_PATH%/'
|
||||
? '/'
|
||||
: window.BASE_PATH,
|
||||
defaultLocale: 'en',
|
||||
endpointWebhook: 'webhook',
|
||||
endpointWebhookTest: 'webhook-test',
|
||||
@@ -59,7 +63,7 @@ export const useRootStore = defineStore(STORES.ROOT, {
|
||||
/**
|
||||
* Getter for node default names ending with a number: `'S3'`, `'Magento 2'`, etc.
|
||||
*/
|
||||
nativelyNumberSuffixedDefaults: (): string[] => {
|
||||
nativelyNumberSuffixedDefaults: (): string[] => {
|
||||
return useNodeTypesStore().allNodeTypes.reduce<string[]>((acc, cur) => {
|
||||
if (/\d$/.test(cur.defaults.name as string)) acc.push(cur.defaults.name as string);
|
||||
return acc;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { STORES } from "@/constants";
|
||||
import { INodeUi, IRunDataDisplayMode, NDVState, NodePanelType, XYPosition } from "@/Interface";
|
||||
import { IRunData } from "n8n-workflow";
|
||||
import { defineStore } from "pinia";
|
||||
import Vue from "vue";
|
||||
import { useWorkflowsStore } from "./workflows";
|
||||
import { STORES } from '@/constants';
|
||||
import { INodeUi, IRunDataDisplayMode, NDVState, NodePanelType, XYPosition } from '@/Interface';
|
||||
import { IRunData } from 'n8n-workflow';
|
||||
import { defineStore } from 'pinia';
|
||||
import Vue from 'vue';
|
||||
import { useWorkflowsStore } from './workflows';
|
||||
|
||||
export const useNDVStore = defineStore(STORES.NDV, {
|
||||
export const useNDVStore = defineStore(STORES.NDV, {
|
||||
state: (): NDVState => ({
|
||||
activeNodeName: null,
|
||||
mainPanelDimensions: {},
|
||||
sessionId: '',
|
||||
input: {
|
||||
displayMode: window.posthog?.isFeatureEnabled?.('schema-view') ? 'schema': 'table',
|
||||
displayMode: window.posthog?.isFeatureEnabled?.('schema-view') ? 'schema' : 'table',
|
||||
nodeName: undefined,
|
||||
run: undefined,
|
||||
branch: undefined,
|
||||
@@ -51,13 +51,19 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
||||
const executionData = workflowsStore.getWorkflowExecution;
|
||||
const inputNodeName: string | undefined = this.input.nodeName;
|
||||
const inputRunIndex: number = this.input.run ?? 0;
|
||||
const inputBranchIndex: number = this.input.branch?? 0;
|
||||
const inputBranchIndex: number = this.input.branch ?? 0;
|
||||
|
||||
if (!executionData || !inputNodeName || inputRunIndex === undefined || inputBranchIndex === undefined) {
|
||||
if (
|
||||
!executionData ||
|
||||
!inputNodeName ||
|
||||
inputRunIndex === undefined ||
|
||||
inputBranchIndex === undefined
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return executionData.data?.resultData?.runData?.[inputNodeName]?.[inputRunIndex]?.data?.main?.[inputBranchIndex];
|
||||
return executionData.data?.resultData?.runData?.[inputNodeName]?.[inputRunIndex]?.data
|
||||
?.main?.[inputBranchIndex];
|
||||
},
|
||||
getPanelDisplayMode() {
|
||||
return (panel: NodePanelType) => this[panel].displayMode;
|
||||
@@ -86,7 +92,7 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
||||
getMainPanelDimensions() {
|
||||
return (panelType: string) => {
|
||||
const defaults = { relativeRight: 1, relativeLeft: 1, relativeWidth: 1 };
|
||||
return {...defaults, ...this.mainPanelDimensions[panelType]};
|
||||
return { ...defaults, ...this.mainPanelDimensions[panelType] };
|
||||
};
|
||||
},
|
||||
draggableStickyPos(): XYPosition | null {
|
||||
@@ -109,26 +115,28 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
||||
setInputNodeName(name: string | undefined): void {
|
||||
Vue.set(this.input, 'nodeName', name);
|
||||
},
|
||||
setInputRunIndex(run?: string): void {
|
||||
setInputRunIndex(run?: string): void {
|
||||
Vue.set(this.input, 'run', run);
|
||||
},
|
||||
setMainPanelDimensions(params: { panelType:string, dimensions: { relativeLeft?: number, relativeRight?: number, relativeWidth?: number }}): void {
|
||||
Vue.set(
|
||||
this.mainPanelDimensions,
|
||||
params.panelType,
|
||||
{...this.mainPanelDimensions[params.panelType], ...params.dimensions },
|
||||
);
|
||||
setMainPanelDimensions(params: {
|
||||
panelType: string;
|
||||
dimensions: { relativeLeft?: number; relativeRight?: number; relativeWidth?: number };
|
||||
}): void {
|
||||
Vue.set(this.mainPanelDimensions, params.panelType, {
|
||||
...this.mainPanelDimensions[params.panelType],
|
||||
...params.dimensions,
|
||||
});
|
||||
},
|
||||
setNDVSessionId(): void {
|
||||
setNDVSessionId(): void {
|
||||
Vue.set(this, 'sessionId', `ndv-${Math.random().toString(36).slice(-8)}`);
|
||||
},
|
||||
resetNDVSessionId(): void {
|
||||
resetNDVSessionId(): void {
|
||||
Vue.set(this, 'sessionId', '');
|
||||
},
|
||||
setPanelDisplayMode(params: {pane: NodePanelType, mode: IRunDataDisplayMode}): void {
|
||||
setPanelDisplayMode(params: { pane: NodePanelType; mode: IRunDataDisplayMode }): void {
|
||||
Vue.set(this[params.pane], 'displayMode', params.mode);
|
||||
},
|
||||
setOutputPanelEditModeEnabled(isEnabled: boolean): void {
|
||||
setOutputPanelEditModeEnabled(isEnabled: boolean): void {
|
||||
Vue.set(this.output.editMode, 'enabled', isEnabled);
|
||||
},
|
||||
setOutputPanelEditModeValue(payload: string): void {
|
||||
@@ -137,7 +145,7 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
||||
setMappableNDVInputFocus(paramName: string): void {
|
||||
Vue.set(this, 'focusedMappableInput', paramName);
|
||||
},
|
||||
draggableStartDragging({type, data}: {type: string, data: string}): void {
|
||||
draggableStartDragging({ type, data }: { type: string; data: string }): void {
|
||||
this.draggable = {
|
||||
isDragging: true,
|
||||
type,
|
||||
@@ -161,8 +169,8 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
||||
setDraggableCanDrop(canDrop: boolean): void {
|
||||
Vue.set(this.draggable, 'canDrop', canDrop);
|
||||
},
|
||||
setMappingTelemetry(telemetry: {[key: string]: string | number | boolean}): void {
|
||||
this.mappingTelemetry = {...this.mappingTelemetry, ...telemetry};
|
||||
setMappingTelemetry(telemetry: { [key: string]: string | number | boolean }): void {
|
||||
this.mappingTelemetry = { ...this.mappingTelemetry, ...telemetry };
|
||||
},
|
||||
resetMappingTelemetry(): void {
|
||||
this.mappingTelemetry = {};
|
||||
@@ -170,10 +178,10 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
||||
setHoveringItem(item: null | NDVState['hoveringItem']): void {
|
||||
Vue.set(this, 'hoveringItem', item);
|
||||
},
|
||||
setNDVBranchIndex(e: {pane: 'input' | 'output', branchIndex: number}): void {
|
||||
setNDVBranchIndex(e: { pane: 'input' | 'output'; branchIndex: number }): void {
|
||||
Vue.set(this[e.pane], 'branch', e.branchIndex);
|
||||
},
|
||||
setNDVPanelDataIsEmpty(payload: {panel: 'input' | 'output', isEmpty: boolean}): void {
|
||||
setNDVPanelDataIsEmpty(payload: { panel: 'input' | 'output'; isEmpty: boolean }): void {
|
||||
Vue.set(this[payload.panel].data, 'isEmpty', payload.isEmpty);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,7 +1,23 @@
|
||||
import startCase from 'lodash.startCase';
|
||||
import { defineStore } from "pinia";
|
||||
import { INodePropertyCollection, INodePropertyOptions, IDataObject, INodeProperties, INodeTypeDescription, deepCopy, INodeParameters, INodeActionTypeDescription } from 'n8n-workflow';
|
||||
import { STORES, MANUAL_TRIGGER_NODE_TYPE, CORE_NODES_CATEGORY, CALENDLY_TRIGGER_NODE_TYPE, TRIGGER_NODE_FILTER, WEBHOOK_NODE_TYPE } from "@/constants";
|
||||
import { defineStore } from 'pinia';
|
||||
import {
|
||||
INodePropertyCollection,
|
||||
INodePropertyOptions,
|
||||
IDataObject,
|
||||
INodeProperties,
|
||||
INodeTypeDescription,
|
||||
deepCopy,
|
||||
INodeParameters,
|
||||
INodeActionTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
import {
|
||||
STORES,
|
||||
MANUAL_TRIGGER_NODE_TYPE,
|
||||
CORE_NODES_CATEGORY,
|
||||
CALENDLY_TRIGGER_NODE_TYPE,
|
||||
TRIGGER_NODE_FILTER,
|
||||
WEBHOOK_NODE_TYPE,
|
||||
} from '@/constants';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes';
|
||||
import { useWorkflowsStore } from './workflows';
|
||||
import { CUSTOM_API_CALL_KEY, ALL_NODE_FILTER } from '@/constants';
|
||||
@@ -12,28 +28,40 @@ import { Telemetry } from '@/plugins/telemetry';
|
||||
|
||||
const PLACEHOLDER_RECOMMENDED_ACTION_KEY = 'placeholder_recommended';
|
||||
|
||||
const customNodeActionsParsers: {[key: string]: (matchedProperty: INodeProperties, nodeTypeDescription: INodeTypeDescription) => INodeActionTypeDescription[] | undefined} = {
|
||||
const customNodeActionsParsers: {
|
||||
[key: string]: (
|
||||
matchedProperty: INodeProperties,
|
||||
nodeTypeDescription: INodeTypeDescription,
|
||||
) => INodeActionTypeDescription[] | undefined;
|
||||
} = {
|
||||
['n8n-nodes-base.hubspotTrigger']: (matchedProperty, nodeTypeDescription) => {
|
||||
const collection = matchedProperty?.options?.[0] as INodePropertyCollection;
|
||||
|
||||
return (collection?.values[0]?.options as INodePropertyOptions[])?.map((categoryItem): INodeActionTypeDescription => ({
|
||||
...getNodeTypeBase(nodeTypeDescription, i18n.baseText('nodeCreator.actionsCategory.recommended')),
|
||||
actionKey: categoryItem.value as string,
|
||||
displayName: i18n.baseText('nodeCreator.actionsCategory.onEvent', {
|
||||
interpolate: {event: startCase(categoryItem.name)},
|
||||
return (collection?.values[0]?.options as INodePropertyOptions[])?.map(
|
||||
(categoryItem): INodeActionTypeDescription => ({
|
||||
...getNodeTypeBase(
|
||||
nodeTypeDescription,
|
||||
i18n.baseText('nodeCreator.actionsCategory.recommended'),
|
||||
),
|
||||
actionKey: categoryItem.value as string,
|
||||
displayName: i18n.baseText('nodeCreator.actionsCategory.onEvent', {
|
||||
interpolate: { event: startCase(categoryItem.name) },
|
||||
}),
|
||||
description: categoryItem.description || '',
|
||||
displayOptions: matchedProperty.displayOptions,
|
||||
values: { eventsUi: { eventValues: [{ name: categoryItem.value }] } },
|
||||
}),
|
||||
description: categoryItem.description || '',
|
||||
displayOptions: matchedProperty.displayOptions,
|
||||
values: { eventsUi: { eventValues: [{ name: categoryItem.value }] } },
|
||||
}));
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
function filterSinglePlaceholderAction(actions: INodeActionTypeDescription[]) {
|
||||
return actions.filter((action: INodeActionTypeDescription, _: number, arr: INodeActionTypeDescription[]) => {
|
||||
const isPlaceholderTriggerAction = action.actionKey === PLACEHOLDER_RECOMMENDED_ACTION_KEY;
|
||||
return !isPlaceholderTriggerAction || (isPlaceholderTriggerAction && arr.length > 1);
|
||||
});
|
||||
return actions.filter(
|
||||
(action: INodeActionTypeDescription, _: number, arr: INodeActionTypeDescription[]) => {
|
||||
const isPlaceholderTriggerAction = action.actionKey === PLACEHOLDER_RECOMMENDED_ACTION_KEY;
|
||||
return !isPlaceholderTriggerAction || (isPlaceholderTriggerAction && arr.length > 1);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function getNodeTypeBase(nodeTypeDescription: INodeTypeDescription, category: string) {
|
||||
@@ -56,11 +84,14 @@ function getNodeTypeBase(nodeTypeDescription: INodeTypeDescription, category: st
|
||||
};
|
||||
}
|
||||
|
||||
function operationsCategory(nodeTypeDescription: INodeTypeDescription): INodeActionTypeDescription[] {
|
||||
function operationsCategory(
|
||||
nodeTypeDescription: INodeTypeDescription,
|
||||
): INodeActionTypeDescription[] {
|
||||
if (!!nodeTypeDescription.properties.find((property) => property.name === 'resource')) return [];
|
||||
|
||||
const matchedProperty = nodeTypeDescription.properties
|
||||
.find((property) =>property.name?.toLowerCase() === 'operation');
|
||||
const matchedProperty = nodeTypeDescription.properties.find(
|
||||
(property) => property.name?.toLowerCase() === 'operation',
|
||||
);
|
||||
|
||||
if (!matchedProperty || !matchedProperty.options) return [];
|
||||
|
||||
@@ -69,7 +100,10 @@ function operationsCategory(nodeTypeDescription: INodeTypeDescription): INodeAct
|
||||
);
|
||||
|
||||
const items = filteredOutItems.map((item: INodePropertyOptions) => ({
|
||||
...getNodeTypeBase(nodeTypeDescription, i18n.baseText('nodeCreator.actionsCategory.operations')),
|
||||
...getNodeTypeBase(
|
||||
nodeTypeDescription,
|
||||
i18n.baseText('nodeCreator.actionsCategory.operations'),
|
||||
),
|
||||
actionKey: item.value as string,
|
||||
displayName: item.action ?? startCase(item.name),
|
||||
description: item.description ?? '',
|
||||
@@ -85,7 +119,9 @@ function operationsCategory(nodeTypeDescription: INodeTypeDescription): INodeAct
|
||||
return items;
|
||||
}
|
||||
|
||||
function recommendedCategory(nodeTypeDescription: INodeTypeDescription): INodeActionTypeDescription[] {
|
||||
function recommendedCategory(
|
||||
nodeTypeDescription: INodeTypeDescription,
|
||||
): INodeActionTypeDescription[] {
|
||||
const matchingKeys = ['event', 'events', 'trigger on'];
|
||||
const isTrigger = nodeTypeDescription.displayName?.toLowerCase().includes('trigger');
|
||||
const matchedProperty = nodeTypeDescription.properties.find((property) =>
|
||||
@@ -97,29 +133,40 @@ function recommendedCategory(nodeTypeDescription: INodeTypeDescription): INodeAc
|
||||
// Inject placeholder action if no events are available
|
||||
// so user is able to add node to the canvas from the actions panel
|
||||
if (!matchedProperty || !matchedProperty.options) {
|
||||
return [{
|
||||
...getNodeTypeBase(nodeTypeDescription, i18n.baseText('nodeCreator.actionsCategory.recommended')),
|
||||
actionKey: PLACEHOLDER_RECOMMENDED_ACTION_KEY,
|
||||
displayName: i18n.baseText('nodeCreator.actionsCategory.onNewEvent', {
|
||||
interpolate: {event: nodeTypeDescription.displayName.replace('Trigger', '').trimEnd()},
|
||||
}),
|
||||
description: '',
|
||||
}];
|
||||
return [
|
||||
{
|
||||
...getNodeTypeBase(
|
||||
nodeTypeDescription,
|
||||
i18n.baseText('nodeCreator.actionsCategory.recommended'),
|
||||
),
|
||||
actionKey: PLACEHOLDER_RECOMMENDED_ACTION_KEY,
|
||||
displayName: i18n.baseText('nodeCreator.actionsCategory.onNewEvent', {
|
||||
interpolate: { event: nodeTypeDescription.displayName.replace('Trigger', '').trimEnd() },
|
||||
}),
|
||||
description: '',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const filteredOutItems = (matchedProperty.options as INodePropertyOptions[]).filter(
|
||||
(categoryItem: INodePropertyOptions) => !['*', '', ' '].includes(categoryItem.name),
|
||||
);
|
||||
|
||||
const customParsedItem = customNodeActionsParsers[nodeTypeDescription.name]?.(matchedProperty, nodeTypeDescription);
|
||||
const customParsedItem = customNodeActionsParsers[nodeTypeDescription.name]?.(
|
||||
matchedProperty,
|
||||
nodeTypeDescription,
|
||||
);
|
||||
|
||||
const items =
|
||||
customParsedItem ??
|
||||
filteredOutItems.map((categoryItem: INodePropertyOptions) => ({
|
||||
...getNodeTypeBase(nodeTypeDescription, i18n.baseText('nodeCreator.actionsCategory.recommended')),
|
||||
...getNodeTypeBase(
|
||||
nodeTypeDescription,
|
||||
i18n.baseText('nodeCreator.actionsCategory.recommended'),
|
||||
),
|
||||
actionKey: categoryItem.value as string,
|
||||
displayName: i18n.baseText('nodeCreator.actionsCategory.onEvent', {
|
||||
interpolate: {event: startCase(categoryItem.name)},
|
||||
interpolate: { event: startCase(categoryItem.name) },
|
||||
}),
|
||||
description: categoryItem.description || '',
|
||||
displayOptions: matchedProperty.displayOptions,
|
||||
@@ -132,12 +179,16 @@ function recommendedCategory(nodeTypeDescription: INodeTypeDescription): INodeAc
|
||||
return items;
|
||||
}
|
||||
|
||||
function resourceCategories(nodeTypeDescription: INodeTypeDescription): INodeActionTypeDescription[] {
|
||||
function resourceCategories(
|
||||
nodeTypeDescription: INodeTypeDescription,
|
||||
): INodeActionTypeDescription[] {
|
||||
const transformedNodes: INodeActionTypeDescription[] = [];
|
||||
const matchedProperties = nodeTypeDescription.properties.filter((property) =>property.displayName?.toLowerCase() === 'resource');
|
||||
const matchedProperties = nodeTypeDescription.properties.filter(
|
||||
(property) => property.displayName?.toLowerCase() === 'resource',
|
||||
);
|
||||
|
||||
matchedProperties.forEach((property) => {
|
||||
(property.options as INodePropertyOptions[] || [])
|
||||
((property.options as INodePropertyOptions[]) || [])
|
||||
.filter((option) => option.value !== CUSTOM_API_CALL_KEY)
|
||||
.forEach((resourceOption, i, options) => {
|
||||
const isSingleResource = options.length === 1;
|
||||
@@ -152,11 +203,10 @@ function resourceCategories(nodeTypeDescription: INodeTypeDescription): INodeAct
|
||||
|
||||
if (!operations?.options) return;
|
||||
|
||||
const items = (operations.options as INodePropertyOptions[] || []).map(
|
||||
const items = ((operations.options as INodePropertyOptions[]) || []).map(
|
||||
(operationOption) => {
|
||||
const displayName =
|
||||
operationOption.action ??
|
||||
`${resourceOption.name} ${startCase(operationOption.name)}`;
|
||||
operationOption.action ?? `${resourceOption.name} ${startCase(operationOption.name)}`;
|
||||
|
||||
// We need to manually populate displayOptions as they are not present in the node description
|
||||
// if the resource has only one option
|
||||
@@ -208,20 +258,22 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, {
|
||||
setFilter(search: string) {
|
||||
this.itemsFilter = search;
|
||||
},
|
||||
setAddedNodeActionParameters (action: IUpdateInformation, telemetry?: Telemetry, track = true) {
|
||||
setAddedNodeActionParameters(action: IUpdateInformation, telemetry?: Telemetry, track = true) {
|
||||
const { $onAction: onWorkflowStoreAction } = useWorkflowsStore();
|
||||
const storeWatcher = onWorkflowStoreAction(({ name, after, store: { setLastNodeParameters }, args }) => {
|
||||
if (name !== 'addNode' || args[0].type !== action.key) return;
|
||||
after(() => {
|
||||
setLastNodeParameters(action);
|
||||
if(track) this.trackActionSelected(action, telemetry);
|
||||
storeWatcher();
|
||||
});
|
||||
});
|
||||
const storeWatcher = onWorkflowStoreAction(
|
||||
({ name, after, store: { setLastNodeParameters }, args }) => {
|
||||
if (name !== 'addNode' || args[0].type !== action.key) return;
|
||||
after(() => {
|
||||
setLastNodeParameters(action);
|
||||
if (track) this.trackActionSelected(action, telemetry);
|
||||
storeWatcher();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return storeWatcher;
|
||||
},
|
||||
trackActionSelected (action: IUpdateInformation, telemetry?: Telemetry) {
|
||||
trackActionSelected(action: IUpdateInformation, telemetry?: Telemetry) {
|
||||
const { $externalHooks } = new externalHooks();
|
||||
|
||||
const payload = {
|
||||
@@ -240,7 +292,7 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, {
|
||||
const isCoreNode = node.codex?.categories?.includes(CORE_NODES_CATEGORY);
|
||||
// Core nodes shouldn't support actions
|
||||
node.actions = [];
|
||||
if(isCoreNode) return node;
|
||||
if (isCoreNode) return node;
|
||||
|
||||
node.actions.push(
|
||||
...recommendedCategory(node),
|
||||
@@ -253,54 +305,70 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, {
|
||||
return nodesWithActions;
|
||||
},
|
||||
mergedAppNodes(): INodeTypeDescription[] {
|
||||
const mergedNodes = this.visibleNodesWithActions.reduce((acc: Record<string, INodeTypeDescription>, node: INodeTypeDescription) => {
|
||||
const mergedNodes = this.visibleNodesWithActions.reduce(
|
||||
(acc: Record<string, INodeTypeDescription>, node: INodeTypeDescription) => {
|
||||
const clonedNode = deepCopy(node);
|
||||
const isCoreNode = node.codex?.categories?.includes(CORE_NODES_CATEGORY);
|
||||
const actions = node.actions || [];
|
||||
// Do not merge core nodes
|
||||
const normalizedName = isCoreNode
|
||||
? node.name
|
||||
: node.name.toLowerCase().replace('trigger', '');
|
||||
const existingNode = acc[normalizedName];
|
||||
|
||||
const clonedNode = deepCopy(node);
|
||||
const isCoreNode = node.codex?.categories?.includes(CORE_NODES_CATEGORY);
|
||||
const actions = node.actions || [];
|
||||
// Do not merge core nodes
|
||||
const normalizedName = isCoreNode ? node.name : node.name.toLowerCase().replace('trigger', '');
|
||||
const existingNode = acc[normalizedName];
|
||||
if (existingNode) existingNode.actions?.push(...actions);
|
||||
else acc[normalizedName] = clonedNode;
|
||||
|
||||
if(existingNode) existingNode.actions?.push(...actions);
|
||||
else acc[normalizedName] = clonedNode;
|
||||
if (!isCoreNode) {
|
||||
acc[normalizedName].displayName = node.displayName.replace('Trigger', '');
|
||||
}
|
||||
|
||||
if(!isCoreNode) acc[normalizedName].displayName = node.displayName.replace('Trigger', '');
|
||||
|
||||
acc[normalizedName].actions = filterSinglePlaceholderAction(acc[normalizedName].actions || []);
|
||||
return acc;
|
||||
}, {});
|
||||
acc[normalizedName].actions = filterSinglePlaceholderAction(
|
||||
acc[normalizedName].actions || [],
|
||||
);
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
return Object.values(mergedNodes);
|
||||
},
|
||||
getNodeTypesWithManualTrigger: () => (nodeType?: string): string[] => {
|
||||
if(!nodeType) return [];
|
||||
getNodeTypesWithManualTrigger:
|
||||
() =>
|
||||
(nodeType?: string): string[] => {
|
||||
if (!nodeType) return [];
|
||||
|
||||
const { workflowTriggerNodes } = useWorkflowsStore();
|
||||
const isTrigger = nodeType.toLocaleLowerCase().includes('trigger') || nodeType === WEBHOOK_NODE_TYPE;
|
||||
const workflowContainsTrigger = workflowTriggerNodes.length > 0;
|
||||
const isTriggerPanel = useNodeCreatorStore().selectedType === TRIGGER_NODE_FILTER;
|
||||
const { workflowTriggerNodes } = useWorkflowsStore();
|
||||
const isTrigger =
|
||||
nodeType.toLocaleLowerCase().includes('trigger') || nodeType === WEBHOOK_NODE_TYPE;
|
||||
const workflowContainsTrigger = workflowTriggerNodes.length > 0;
|
||||
const isTriggerPanel = useNodeCreatorStore().selectedType === TRIGGER_NODE_FILTER;
|
||||
|
||||
const nodeTypes = !isTrigger && !workflowContainsTrigger && isTriggerPanel
|
||||
? [MANUAL_TRIGGER_NODE_TYPE, nodeType]
|
||||
: [nodeType];
|
||||
const nodeTypes =
|
||||
!isTrigger && !workflowContainsTrigger && isTriggerPanel
|
||||
? [MANUAL_TRIGGER_NODE_TYPE, nodeType]
|
||||
: [nodeType];
|
||||
|
||||
return nodeTypes;
|
||||
},
|
||||
return nodeTypes;
|
||||
},
|
||||
|
||||
getActionData: () => (actionItem: INodeActionTypeDescription): IUpdateInformation => {
|
||||
const displayOptions = actionItem.displayOptions ;
|
||||
getActionData:
|
||||
() =>
|
||||
(actionItem: INodeActionTypeDescription): IUpdateInformation => {
|
||||
const displayOptions = actionItem.displayOptions;
|
||||
|
||||
const displayConditions = Object.keys(displayOptions?.show || {})
|
||||
.reduce((acc: IDataObject, showCondition: string) => {
|
||||
acc[showCondition] = displayOptions?.show?.[showCondition]?.[0];
|
||||
return acc;
|
||||
}, {});
|
||||
const displayConditions = Object.keys(displayOptions?.show || {}).reduce(
|
||||
(acc: IDataObject, showCondition: string) => {
|
||||
acc[showCondition] = displayOptions?.show?.[showCondition]?.[0];
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
return {
|
||||
name: actionItem.displayName,
|
||||
key: actionItem.name as string,
|
||||
value: { ...actionItem.values , ...displayConditions} as INodeParameters,
|
||||
};
|
||||
},
|
||||
return {
|
||||
name: actionItem.displayName,
|
||||
key: actionItem.name as string,
|
||||
value: { ...actionItem.values, ...displayConditions } as INodeParameters,
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,41 +1,66 @@
|
||||
import { getNodeParameterOptions, getNodesInformation, getNodeTranslationHeaders, getNodeTypes, getResourceLocatorResults } from "@/api/nodeTypes";
|
||||
import { DEFAULT_NODETYPE_VERSION, STORES } from "@/constants";
|
||||
import { ICategoriesWithNodes, INodeCreateElement, INodeTypesState, IResourceLocatorReqParams } from "@/Interface";
|
||||
import { addHeaders, addNodeTranslation } from "@/plugins/i18n";
|
||||
import { omit, getCategoriesWithNodes, getCategorizedList } from "@/utils";
|
||||
import { ILoadOptions, INodeCredentials, INodeListSearchResult, INodeParameters, INodePropertyOptions, INodeTypeDescription, INodeTypeNameVersion } from 'n8n-workflow';
|
||||
import { defineStore } from "pinia";
|
||||
import Vue from "vue";
|
||||
import { useCredentialsStore } from "./credentials";
|
||||
import { useRootStore } from "./n8nRootStore";
|
||||
import { useUsersStore } from "./users";
|
||||
import {
|
||||
getNodeParameterOptions,
|
||||
getNodesInformation,
|
||||
getNodeTranslationHeaders,
|
||||
getNodeTypes,
|
||||
getResourceLocatorResults,
|
||||
} from '@/api/nodeTypes';
|
||||
import { DEFAULT_NODETYPE_VERSION, STORES } from '@/constants';
|
||||
import {
|
||||
ICategoriesWithNodes,
|
||||
INodeCreateElement,
|
||||
INodeTypesState,
|
||||
IResourceLocatorReqParams,
|
||||
} from '@/Interface';
|
||||
import { addHeaders, addNodeTranslation } from '@/plugins/i18n';
|
||||
import { omit, getCategoriesWithNodes, getCategorizedList } from '@/utils';
|
||||
import {
|
||||
ILoadOptions,
|
||||
INodeCredentials,
|
||||
INodeListSearchResult,
|
||||
INodeParameters,
|
||||
INodePropertyOptions,
|
||||
INodeTypeDescription,
|
||||
INodeTypeNameVersion,
|
||||
} from 'n8n-workflow';
|
||||
import { defineStore } from 'pinia';
|
||||
import Vue from 'vue';
|
||||
import { useCredentialsStore } from './credentials';
|
||||
import { useRootStore } from './n8nRootStore';
|
||||
import { useUsersStore } from './users';
|
||||
import { useNodeCreatorStore } from './nodeCreator';
|
||||
function getNodeVersions(nodeType: INodeTypeDescription) {
|
||||
return Array.isArray(nodeType.version) ? nodeType.version : [nodeType.version];
|
||||
}
|
||||
|
||||
export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
||||
export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
||||
state: (): INodeTypesState => ({
|
||||
nodeTypes: {},
|
||||
}),
|
||||
getters: {
|
||||
allNodeTypes(): INodeTypeDescription[] {
|
||||
return Object.values(this.nodeTypes).reduce<INodeTypeDescription[]>((allNodeTypes, nodeType) => {
|
||||
const versionNumbers = Object.keys(nodeType).map(Number);
|
||||
const allNodeVersions = versionNumbers.map(version => nodeType[version]);
|
||||
return Object.values(this.nodeTypes).reduce<INodeTypeDescription[]>(
|
||||
(allNodeTypes, nodeType) => {
|
||||
const versionNumbers = Object.keys(nodeType).map(Number);
|
||||
const allNodeVersions = versionNumbers.map((version) => nodeType[version]);
|
||||
|
||||
return [...allNodeTypes, ...allNodeVersions];
|
||||
}, []);
|
||||
return [...allNodeTypes, ...allNodeVersions];
|
||||
},
|
||||
[],
|
||||
);
|
||||
},
|
||||
allLatestNodeTypes(): INodeTypeDescription[] {
|
||||
return Object.values(this.nodeTypes).reduce<INodeTypeDescription[]>((allLatestNodeTypes, nodeVersions) => {
|
||||
const versionNumbers = Object.keys(nodeVersions).map(Number);
|
||||
const latestNodeVersion = nodeVersions[Math.max(...versionNumbers)];
|
||||
return Object.values(this.nodeTypes).reduce<INodeTypeDescription[]>(
|
||||
(allLatestNodeTypes, nodeVersions) => {
|
||||
const versionNumbers = Object.keys(nodeVersions).map(Number);
|
||||
const latestNodeVersion = nodeVersions[Math.max(...versionNumbers)];
|
||||
|
||||
if (!latestNodeVersion) return allLatestNodeTypes;
|
||||
if (!latestNodeVersion) return allLatestNodeTypes;
|
||||
|
||||
return [...allLatestNodeTypes, latestNodeVersion];
|
||||
}, []);
|
||||
return [...allLatestNodeTypes, latestNodeVersion];
|
||||
},
|
||||
[],
|
||||
);
|
||||
},
|
||||
getNodeType() {
|
||||
return (nodeTypeName: string, version?: number): INodeTypeDescription | null => {
|
||||
@@ -68,27 +93,35 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
||||
},
|
||||
actions: {
|
||||
setNodeTypes(newNodeTypes: INodeTypeDescription[] = []): void {
|
||||
const nodeTypes = newNodeTypes.reduce<Record<string, Record<string, INodeTypeDescription>>>((acc, newNodeType) => {
|
||||
const newNodeVersions = getNodeVersions(newNodeType);
|
||||
const nodeTypes = newNodeTypes.reduce<Record<string, Record<string, INodeTypeDescription>>>(
|
||||
(acc, newNodeType) => {
|
||||
const newNodeVersions = getNodeVersions(newNodeType);
|
||||
|
||||
if (newNodeVersions.length === 0) {
|
||||
const singleVersion = { [DEFAULT_NODETYPE_VERSION]: newNodeType };
|
||||
if (newNodeVersions.length === 0) {
|
||||
const singleVersion = { [DEFAULT_NODETYPE_VERSION]: newNodeType };
|
||||
|
||||
acc[newNodeType.name] = singleVersion;
|
||||
return acc;
|
||||
}
|
||||
|
||||
for (const version of newNodeVersions) {
|
||||
// Node exists with the same name
|
||||
if (acc[newNodeType.name]) {
|
||||
acc[newNodeType.name][version] = Object.assign(acc[newNodeType.name][version] ?? {}, newNodeType);
|
||||
} else {
|
||||
acc[newNodeType.name] = Object.assign(acc[newNodeType.name] ?? {}, { [version]: newNodeType });
|
||||
acc[newNodeType.name] = singleVersion;
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, { ...this.nodeTypes });
|
||||
for (const version of newNodeVersions) {
|
||||
// Node exists with the same name
|
||||
if (acc[newNodeType.name]) {
|
||||
acc[newNodeType.name][version] = Object.assign(
|
||||
acc[newNodeType.name][version] ?? {},
|
||||
newNodeType,
|
||||
);
|
||||
} else {
|
||||
acc[newNodeType.name] = Object.assign(acc[newNodeType.name] ?? {}, {
|
||||
[version]: newNodeType,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ ...this.nodeTypes },
|
||||
);
|
||||
Vue.set(this, 'nodeTypes', nodeTypes);
|
||||
|
||||
// Trigger compute of mergedAppNodes getter so it's ready when user opens the node creator
|
||||
@@ -101,21 +134,21 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
||||
this.nodeTypes,
|
||||
);
|
||||
},
|
||||
async getNodesInformation(nodeInfos: INodeTypeNameVersion[], replace = true): Promise<INodeTypeDescription[]> {
|
||||
async getNodesInformation(
|
||||
nodeInfos: INodeTypeNameVersion[],
|
||||
replace = true,
|
||||
): Promise<INodeTypeDescription[]> {
|
||||
const rootStore = useRootStore();
|
||||
const nodesInformation = await getNodesInformation(rootStore.getRestApiContext, nodeInfos);
|
||||
|
||||
nodesInformation.forEach(nodeInformation => {
|
||||
nodesInformation.forEach((nodeInformation) => {
|
||||
if (nodeInformation.translation) {
|
||||
const nodeType = nodeInformation.name.replace('n8n-nodes-base.', '');
|
||||
|
||||
addNodeTranslation(
|
||||
{ [nodeType]: nodeInformation.translation },
|
||||
rootStore.defaultLocale,
|
||||
);
|
||||
addNodeTranslation({ [nodeType]: nodeInformation.translation }, rootStore.defaultLocale);
|
||||
}
|
||||
});
|
||||
if(replace) this.setNodeTypes(nodesInformation);
|
||||
if (replace) this.setNodeTypes(nodesInformation);
|
||||
|
||||
return nodesInformation;
|
||||
},
|
||||
@@ -139,16 +172,14 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
||||
addHeaders(headers, rootStore.defaultLocale);
|
||||
}
|
||||
},
|
||||
async getNodeParameterOptions(
|
||||
sendData: {
|
||||
nodeTypeAndVersion: INodeTypeNameVersion,
|
||||
path: string,
|
||||
methodName?: string,
|
||||
loadOptions?: ILoadOptions,
|
||||
currentNodeParameters: INodeParameters,
|
||||
credentials?: INodeCredentials,
|
||||
},
|
||||
): Promise<INodePropertyOptions[]> {
|
||||
async getNodeParameterOptions(sendData: {
|
||||
nodeTypeAndVersion: INodeTypeNameVersion;
|
||||
path: string;
|
||||
methodName?: string;
|
||||
loadOptions?: ILoadOptions;
|
||||
currentNodeParameters: INodeParameters;
|
||||
credentials?: INodeCredentials;
|
||||
}): Promise<INodePropertyOptions[]> {
|
||||
const rootStore = useRootStore();
|
||||
return getNodeParameterOptions(rootStore.getRestApiContext, sendData);
|
||||
},
|
||||
|
||||
@@ -1,16 +1,29 @@
|
||||
import { createApiKey, deleteApiKey, getApiKey } from "@/api/api-keys";
|
||||
import { getPromptsData, getSettings, submitContactInfo, submitValueSurvey } from "@/api/settings";
|
||||
import { testHealthEndpoint } from "@/api/templates";
|
||||
import { CONTACT_PROMPT_MODAL_KEY, EnterpriseEditionFeature, STORES, VALUE_SURVEY_MODAL_KEY } from "@/constants";
|
||||
import { ILogLevel, IN8nPromptResponse, IN8nPrompts, IN8nUISettings, IN8nValueSurveyData, ISettingsState, WorkflowCallerPolicyDefaultOption } from "@/Interface";
|
||||
import { store } from "@/store";
|
||||
import { ITelemetrySettings } from "n8n-workflow";
|
||||
import { defineStore } from "pinia";
|
||||
import Vue from "vue";
|
||||
import { useRootStore } from "./n8nRootStore";
|
||||
import { useUIStore } from "./ui";
|
||||
import { useUsersStore } from "./users";
|
||||
import { useVersionsStore } from "./versions";
|
||||
import { createApiKey, deleteApiKey, getApiKey } from '@/api/api-keys';
|
||||
import { getPromptsData, getSettings, submitContactInfo, submitValueSurvey } from '@/api/settings';
|
||||
import { testHealthEndpoint } from '@/api/templates';
|
||||
import {
|
||||
CONTACT_PROMPT_MODAL_KEY,
|
||||
EnterpriseEditionFeature,
|
||||
STORES,
|
||||
VALUE_SURVEY_MODAL_KEY,
|
||||
} from '@/constants';
|
||||
import {
|
||||
ILogLevel,
|
||||
IN8nPromptResponse,
|
||||
IN8nPrompts,
|
||||
IN8nUISettings,
|
||||
IN8nValueSurveyData,
|
||||
ISettingsState,
|
||||
WorkflowCallerPolicyDefaultOption,
|
||||
} from '@/Interface';
|
||||
import { store } from '@/store';
|
||||
import { ITelemetrySettings } from 'n8n-workflow';
|
||||
import { defineStore } from 'pinia';
|
||||
import Vue from 'vue';
|
||||
import { useRootStore } from './n8nRootStore';
|
||||
import { useUIStore } from './ui';
|
||||
import { useUsersStore } from './users';
|
||||
import { useVersionsStore } from './versions';
|
||||
|
||||
export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||
state: (): ISettingsState => ({
|
||||
@@ -34,7 +47,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||
}),
|
||||
getters: {
|
||||
isEnterpriseFeatureEnabled() {
|
||||
return (feature: EnterpriseEditionFeature) : boolean => this.settings.enterprise[feature];
|
||||
return (feature: EnterpriseEditionFeature): boolean => this.settings.enterprise[feature];
|
||||
},
|
||||
versionCli(): string {
|
||||
return this.settings.versionCli;
|
||||
@@ -51,58 +64,64 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||
publicApiPath(): string {
|
||||
return this.api.path;
|
||||
},
|
||||
showSetupPage() : boolean {
|
||||
showSetupPage(): boolean {
|
||||
return this.userManagement.showSetupOnFirstLoad === true;
|
||||
},
|
||||
isDesktopDeployment() : boolean {
|
||||
isDesktopDeployment(): boolean {
|
||||
if (!this.settings.deployment) {
|
||||
return false;
|
||||
}
|
||||
return this.settings.deployment?.type.startsWith('desktop_');
|
||||
},
|
||||
isCloudDeployment() : boolean {
|
||||
isCloudDeployment(): boolean {
|
||||
if (!this.settings.deployment) {
|
||||
return false;
|
||||
}
|
||||
return this.settings.deployment.type === 'cloud';
|
||||
},
|
||||
isSmtpSetup() : boolean {
|
||||
isSmtpSetup(): boolean {
|
||||
return this.userManagement.smtpSetup;
|
||||
},
|
||||
isPersonalizationSurveyEnabled() : boolean {
|
||||
return (this.settings.telemetry && this.settings.telemetry.enabled) && this.settings.personalizationSurveyEnabled;
|
||||
isPersonalizationSurveyEnabled(): boolean {
|
||||
return (
|
||||
this.settings.telemetry &&
|
||||
this.settings.telemetry.enabled &&
|
||||
this.settings.personalizationSurveyEnabled
|
||||
);
|
||||
},
|
||||
telemetry() : ITelemetrySettings {
|
||||
telemetry(): ITelemetrySettings {
|
||||
return this.settings.telemetry;
|
||||
},
|
||||
logLevel() : ILogLevel {
|
||||
logLevel(): ILogLevel {
|
||||
return this.settings.logLevel;
|
||||
},
|
||||
isTelemetryEnabled() : boolean {
|
||||
isTelemetryEnabled(): boolean {
|
||||
return this.settings.telemetry && this.settings.telemetry.enabled;
|
||||
},
|
||||
areTagsEnabled() : boolean {
|
||||
return this.settings.workflowTagsDisabled !== undefined ? !this.settings.workflowTagsDisabled : true;
|
||||
areTagsEnabled(): boolean {
|
||||
return this.settings.workflowTagsDisabled !== undefined
|
||||
? !this.settings.workflowTagsDisabled
|
||||
: true;
|
||||
},
|
||||
isHiringBannerEnabled() : boolean {
|
||||
isHiringBannerEnabled(): boolean {
|
||||
return this.settings.hiringBannerEnabled;
|
||||
},
|
||||
isTemplatesEnabled(): boolean {
|
||||
return Boolean(this.settings.templates && this.settings.templates.enabled);
|
||||
},
|
||||
isTemplatesEndpointReachable() : boolean {
|
||||
isTemplatesEndpointReachable(): boolean {
|
||||
return this.templatesEndpointHealthy;
|
||||
},
|
||||
templatesHost() : string {
|
||||
templatesHost(): string {
|
||||
return this.settings.templates.host;
|
||||
},
|
||||
isCommunityNodesFeatureEnabled() : boolean {
|
||||
isCommunityNodesFeatureEnabled(): boolean {
|
||||
return this.settings.communityNodesEnabled;
|
||||
},
|
||||
isNpmAvailable() : boolean {
|
||||
isNpmAvailable(): boolean {
|
||||
return this.settings.isNpmAvailable;
|
||||
},
|
||||
allowedModules() : { builtIn?: string[]; external?: string[] } {
|
||||
allowedModules(): { builtIn?: string[]; external?: string[] } {
|
||||
return this.settings.allowedModules;
|
||||
},
|
||||
isQueueModeEnabled(): boolean {
|
||||
@@ -117,8 +136,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||
},
|
||||
actions: {
|
||||
setSettings(settings: IN8nUISettings): void {
|
||||
|
||||
this.settings = settings;
|
||||
this.settings = settings;
|
||||
this.userManagement.enabled = settings.userManagement.enabled;
|
||||
this.userManagement.showSetupOnFirstLoad = !!settings.userManagement.showSetupOnFirstLoad;
|
||||
this.userManagement.smtpSetup = settings.userManagement.smtpSetup;
|
||||
@@ -133,7 +151,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||
|
||||
this.setSettings(settings);
|
||||
this.settings.communityNodesEnabled = settings.communityNodesEnabled;
|
||||
this.setAllowedModules(settings.allowedModules as { builtIn?: string, external?: string });
|
||||
this.setAllowedModules(settings.allowedModules as { builtIn?: string; external?: string });
|
||||
this.setSaveDataErrorExecution(settings.saveDataErrorExecution);
|
||||
this.setSaveDataSuccessExecution(settings.saveDataSuccessExecution);
|
||||
this.setSaveManualExecutions(settings.saveManualExecutions);
|
||||
@@ -155,12 +173,11 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||
},
|
||||
stopShowingSetupPage(): void {
|
||||
Vue.set(this.userManagement, 'showSetupOnFirstLoad', false);
|
||||
|
||||
},
|
||||
setPromptsData(promptsData: IN8nPrompts): void {
|
||||
Vue.set(this, 'promptsData', promptsData);
|
||||
},
|
||||
setAllowedModules(allowedModules: { builtIn?: string, external?: string }): void {
|
||||
setAllowedModules(allowedModules: { builtIn?: string; external?: string }): void {
|
||||
this.settings.allowedModules = {
|
||||
...(allowedModules.builtIn && { builtIn: allowedModules.builtIn.split(',') }),
|
||||
...(allowedModules.external && { external: allowedModules.external.split(',') }),
|
||||
@@ -173,11 +190,14 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||
try {
|
||||
const uiStore = useUIStore();
|
||||
const usersStore = useUsersStore();
|
||||
const promptsData: IN8nPrompts = await getPromptsData(this.settings.instanceId, usersStore.currentUserId || '');
|
||||
const promptsData: IN8nPrompts = await getPromptsData(
|
||||
this.settings.instanceId,
|
||||
usersStore.currentUserId || '',
|
||||
);
|
||||
|
||||
if (promptsData && promptsData.showContactPrompt) {
|
||||
uiStore.openModal(CONTACT_PROMPT_MODAL_KEY);
|
||||
} else if (promptsData && promptsData.showValueSurvey) {
|
||||
} else if (promptsData && promptsData.showValueSurvey) {
|
||||
uiStore.openModal(VALUE_SURVEY_MODAL_KEY);
|
||||
}
|
||||
|
||||
@@ -190,7 +210,11 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||
async submitContactInfo(email: string): Promise<IN8nPromptResponse | undefined> {
|
||||
try {
|
||||
const usersStore = useUsersStore();
|
||||
return await submitContactInfo(this.settings.instanceId, usersStore.currentUserId || '', email);
|
||||
return await submitContactInfo(
|
||||
this.settings.instanceId,
|
||||
usersStore.currentUserId || '',
|
||||
email,
|
||||
);
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
@@ -198,7 +222,11 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
||||
async submitValueSurvey(params: IN8nValueSurveyData): Promise<IN8nPromptResponse | undefined> {
|
||||
try {
|
||||
const usersStore = useUsersStore();
|
||||
return await submitValueSurvey(this.settings.instanceId, usersStore.currentUserId || '', params);
|
||||
return await submitValueSurvey(
|
||||
this.settings.instanceId,
|
||||
usersStore.currentUserId || '',
|
||||
params,
|
||||
);
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { createTag, deleteTag, getTags, updateTag } from "@/api/tags";
|
||||
import { STORES } from "@/constants";
|
||||
import { ITag, ITagsState } from "@/Interface";
|
||||
import { defineStore } from "pinia";
|
||||
import Vue from "vue";
|
||||
import { useRootStore } from "./n8nRootStore";
|
||||
import { useWorkflowsStore } from "./workflows";
|
||||
import { createTag, deleteTag, getTags, updateTag } from '@/api/tags';
|
||||
import { STORES } from '@/constants';
|
||||
import { ITag, ITagsState } from '@/Interface';
|
||||
import { defineStore } from 'pinia';
|
||||
import Vue from 'vue';
|
||||
import { useRootStore } from './n8nRootStore';
|
||||
import { useWorkflowsStore } from './workflows';
|
||||
|
||||
export const useTagsStore = defineStore(STORES.TAGS, {
|
||||
state: (): ITagsState => ({
|
||||
@@ -15,8 +15,7 @@ export const useTagsStore = defineStore(STORES.TAGS, {
|
||||
}),
|
||||
getters: {
|
||||
allTags(): ITag[] {
|
||||
return Object.values(this.tags)
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
return Object.values(this.tags).sort((a, b) => a.name.localeCompare(b.name));
|
||||
},
|
||||
isLoading(): boolean {
|
||||
return this.loading;
|
||||
@@ -30,12 +29,11 @@ export const useTagsStore = defineStore(STORES.TAGS, {
|
||||
},
|
||||
actions: {
|
||||
setAllTags(tags: ITag[]): void {
|
||||
this.tags = tags
|
||||
.reduce((accu: { [id: string]: ITag }, tag: ITag) => {
|
||||
accu[tag.id] = tag;
|
||||
this.tags = tags.reduce((accu: { [id: string]: ITag }, tag: ITag) => {
|
||||
accu[tag.id] = tag;
|
||||
|
||||
return accu;
|
||||
}, {});
|
||||
return accu;
|
||||
}, {});
|
||||
this.fetchedAll = true;
|
||||
},
|
||||
upsertTags(tags: ITag[]): void {
|
||||
@@ -48,8 +46,7 @@ export const useTagsStore = defineStore(STORES.TAGS, {
|
||||
...tag,
|
||||
};
|
||||
Vue.set(this.tags, tagId, newTag);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Vue.set(this.tags, tagId, tag);
|
||||
}
|
||||
});
|
||||
@@ -58,7 +55,7 @@ export const useTagsStore = defineStore(STORES.TAGS, {
|
||||
Vue.delete(this.tags, id);
|
||||
},
|
||||
|
||||
async fetchAll(params?: { force?: boolean, withUsageCount?: boolean }): Promise<ITag[]> {
|
||||
async fetchAll(params?: { force?: boolean; withUsageCount?: boolean }): Promise<ITag[]> {
|
||||
const { force = false, withUsageCount = false } = params || {};
|
||||
if (!force && this.fetchedAll && this.fetchedUsageCount === withUsageCount) {
|
||||
return Object.values(this.tags);
|
||||
@@ -79,7 +76,7 @@ export const useTagsStore = defineStore(STORES.TAGS, {
|
||||
|
||||
return tag;
|
||||
},
|
||||
async rename({ id, name }: { id: string, name: string }) {
|
||||
async rename({ id, name }: { id: string; name: string }) {
|
||||
const rootStore = useRootStore();
|
||||
const tag = await updateTag(rootStore.getRestApiContext, id, { name });
|
||||
this.upsertTags([tag]);
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { defineStore } from 'pinia';
|
||||
import { STORES } from '@/constants';
|
||||
import { ITemplatesCategory, ITemplatesCollection, ITemplatesCollectionFull, ITemplatesQuery, ITemplateState, ITemplatesWorkflow, ITemplatesWorkflowFull, IWorkflowTemplate } from "@/Interface";
|
||||
import Vue from "vue";
|
||||
import { useSettingsStore } from "./settings";
|
||||
import { getCategories, getCollectionById, getCollections, getTemplateById, getWorkflows, getWorkflowTemplate } from "@/api/templates";
|
||||
import {
|
||||
ITemplatesCategory,
|
||||
ITemplatesCollection,
|
||||
ITemplatesCollectionFull,
|
||||
ITemplatesQuery,
|
||||
ITemplateState,
|
||||
ITemplatesWorkflow,
|
||||
ITemplatesWorkflowFull,
|
||||
IWorkflowTemplate,
|
||||
} from '@/Interface';
|
||||
import Vue from 'vue';
|
||||
import { useSettingsStore } from './settings';
|
||||
import {
|
||||
getCategories,
|
||||
getCollectionById,
|
||||
getCollections,
|
||||
getTemplateById,
|
||||
getWorkflows,
|
||||
getWorkflowTemplate,
|
||||
} from '@/api/templates';
|
||||
|
||||
const TEMPLATES_PAGE_SIZE = 10;
|
||||
|
||||
@@ -12,256 +28,283 @@ function getSearchKey(query: ITemplatesQuery): string {
|
||||
}
|
||||
|
||||
export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
|
||||
state: (): ITemplateState => ({
|
||||
categories: {},
|
||||
collections: {},
|
||||
workflows: {},
|
||||
collectionSearches: {},
|
||||
workflowSearches: {},
|
||||
currentSessionId: '',
|
||||
previousSessionId: '',
|
||||
}),
|
||||
getters: {
|
||||
allCategories(): ITemplatesCategory[] {
|
||||
return Object.values(this.categories).sort((a: ITemplatesCategory, b: ITemplatesCategory) => a.name > b.name ? 1: -1);
|
||||
},
|
||||
getTemplateById() {
|
||||
return (id: string): null | ITemplatesWorkflow => this.workflows[id];
|
||||
},
|
||||
getCollectionById() {
|
||||
return (id: string): null | ITemplatesCollection => this.collections[id];
|
||||
},
|
||||
getCategoryById() {
|
||||
return (id: string): null | ITemplatesCategory => this.categories[id];
|
||||
},
|
||||
getSearchedCollections() {
|
||||
return (query: ITemplatesQuery) => {
|
||||
const searchKey = getSearchKey(query);
|
||||
const search = this.collectionSearches[searchKey];
|
||||
if (!search) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return search.collectionIds.map((collectionId: string) => this.collections[collectionId]);
|
||||
};
|
||||
},
|
||||
getSearchedWorkflows() {
|
||||
return (query: ITemplatesQuery) => {
|
||||
const searchKey = getSearchKey(query);
|
||||
const search = this.workflowSearches[searchKey];
|
||||
if (!search) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return search.workflowIds.map((workflowId: string) => this.workflows[workflowId]);
|
||||
};
|
||||
},
|
||||
getSearchedWorkflowsTotal() {
|
||||
return (query: ITemplatesQuery) => {
|
||||
const searchKey = getSearchKey(query);
|
||||
const search = this.workflowSearches[searchKey];
|
||||
|
||||
return search ? search.totalWorkflows : 0;
|
||||
};
|
||||
},
|
||||
isSearchLoadingMore() {
|
||||
return (query: ITemplatesQuery) => {
|
||||
const searchKey = getSearchKey(query);
|
||||
const search = this.workflowSearches[searchKey];
|
||||
|
||||
return Boolean(search && search.loadingMore);
|
||||
};
|
||||
},
|
||||
isSearchFinished() {
|
||||
return (query: ITemplatesQuery) => {
|
||||
const searchKey = getSearchKey(query);
|
||||
const search = this.workflowSearches[searchKey];
|
||||
|
||||
return Boolean(search && !search.loadingMore && search.totalWorkflows === search.workflowIds.length);
|
||||
};
|
||||
},
|
||||
state: (): ITemplateState => ({
|
||||
categories: {},
|
||||
collections: {},
|
||||
workflows: {},
|
||||
collectionSearches: {},
|
||||
workflowSearches: {},
|
||||
currentSessionId: '',
|
||||
previousSessionId: '',
|
||||
}),
|
||||
getters: {
|
||||
allCategories(): ITemplatesCategory[] {
|
||||
return Object.values(this.categories).sort((a: ITemplatesCategory, b: ITemplatesCategory) =>
|
||||
a.name > b.name ? 1 : -1,
|
||||
);
|
||||
},
|
||||
actions: {
|
||||
addCategories(categories: ITemplatesCategory[]): void {
|
||||
categories.forEach((category: ITemplatesCategory) => {
|
||||
Vue.set(this.categories, category.id, category);
|
||||
});
|
||||
},
|
||||
addCollections(collections: Array<ITemplatesCollection | ITemplatesCollectionFull>): void {
|
||||
collections.forEach((collection) => {
|
||||
const workflows = (collection.workflows || []).map((workflow) => ({id: workflow.id}));
|
||||
const cachedCollection = this.collections[collection.id] || {};
|
||||
Vue.set(this.collections, collection.id, {
|
||||
...cachedCollection,
|
||||
...collection,
|
||||
workflows,
|
||||
});
|
||||
});
|
||||
},
|
||||
addWorkflows(workflows: Array<ITemplatesWorkflow | ITemplatesWorkflowFull>): void {
|
||||
workflows.forEach((workflow: ITemplatesWorkflow) => {
|
||||
const cachedWorkflow = this.workflows[workflow.id] || {};
|
||||
Vue.set(this.workflows, workflow.id, {
|
||||
...cachedWorkflow,
|
||||
...workflow,
|
||||
});
|
||||
});
|
||||
},
|
||||
addCollectionSearch(data: {collections: ITemplatesCollection[], query: ITemplatesQuery}): void {
|
||||
const collectionIds = data.collections.map((collection) => collection.id);
|
||||
const searchKey = getSearchKey(data.query);
|
||||
Vue.set(this.collectionSearches, searchKey, {
|
||||
collectionIds,
|
||||
});
|
||||
},
|
||||
addWorkflowsSearch(data: {totalWorkflows: number; workflows: ITemplatesWorkflow[], query: ITemplatesQuery}): void {
|
||||
const workflowIds = data.workflows.map((workflow) => workflow.id);
|
||||
const searchKey = getSearchKey(data.query);
|
||||
const cachedResults = this.workflowSearches[searchKey];
|
||||
if (!cachedResults) {
|
||||
Vue.set(this.workflowSearches, searchKey, {
|
||||
workflowIds,
|
||||
totalWorkflows: data.totalWorkflows,
|
||||
});
|
||||
|
||||
return;
|
||||
getTemplateById() {
|
||||
return (id: string): null | ITemplatesWorkflow => this.workflows[id];
|
||||
},
|
||||
getCollectionById() {
|
||||
return (id: string): null | ITemplatesCollection => this.collections[id];
|
||||
},
|
||||
getCategoryById() {
|
||||
return (id: string): null | ITemplatesCategory => this.categories[id];
|
||||
},
|
||||
getSearchedCollections() {
|
||||
return (query: ITemplatesQuery) => {
|
||||
const searchKey = getSearchKey(query);
|
||||
const search = this.collectionSearches[searchKey];
|
||||
if (!search) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return search.collectionIds.map((collectionId: string) => this.collections[collectionId]);
|
||||
};
|
||||
},
|
||||
getSearchedWorkflows() {
|
||||
return (query: ITemplatesQuery) => {
|
||||
const searchKey = getSearchKey(query);
|
||||
const search = this.workflowSearches[searchKey];
|
||||
if (!search) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return search.workflowIds.map((workflowId: string) => this.workflows[workflowId]);
|
||||
};
|
||||
},
|
||||
getSearchedWorkflowsTotal() {
|
||||
return (query: ITemplatesQuery) => {
|
||||
const searchKey = getSearchKey(query);
|
||||
const search = this.workflowSearches[searchKey];
|
||||
|
||||
return search ? search.totalWorkflows : 0;
|
||||
};
|
||||
},
|
||||
isSearchLoadingMore() {
|
||||
return (query: ITemplatesQuery) => {
|
||||
const searchKey = getSearchKey(query);
|
||||
const search = this.workflowSearches[searchKey];
|
||||
|
||||
return Boolean(search && search.loadingMore);
|
||||
};
|
||||
},
|
||||
isSearchFinished() {
|
||||
return (query: ITemplatesQuery) => {
|
||||
const searchKey = getSearchKey(query);
|
||||
const search = this.workflowSearches[searchKey];
|
||||
|
||||
return Boolean(
|
||||
search && !search.loadingMore && search.totalWorkflows === search.workflowIds.length,
|
||||
);
|
||||
};
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
addCategories(categories: ITemplatesCategory[]): void {
|
||||
categories.forEach((category: ITemplatesCategory) => {
|
||||
Vue.set(this.categories, category.id, category);
|
||||
});
|
||||
},
|
||||
addCollections(collections: Array<ITemplatesCollection | ITemplatesCollectionFull>): void {
|
||||
collections.forEach((collection) => {
|
||||
const workflows = (collection.workflows || []).map((workflow) => ({ id: workflow.id }));
|
||||
const cachedCollection = this.collections[collection.id] || {};
|
||||
Vue.set(this.collections, collection.id, {
|
||||
...cachedCollection,
|
||||
...collection,
|
||||
workflows,
|
||||
});
|
||||
});
|
||||
},
|
||||
addWorkflows(workflows: Array<ITemplatesWorkflow | ITemplatesWorkflowFull>): void {
|
||||
workflows.forEach((workflow: ITemplatesWorkflow) => {
|
||||
const cachedWorkflow = this.workflows[workflow.id] || {};
|
||||
Vue.set(this.workflows, workflow.id, {
|
||||
...cachedWorkflow,
|
||||
...workflow,
|
||||
});
|
||||
});
|
||||
},
|
||||
addCollectionSearch(data: {
|
||||
collections: ITemplatesCollection[];
|
||||
query: ITemplatesQuery;
|
||||
}): void {
|
||||
const collectionIds = data.collections.map((collection) => collection.id);
|
||||
const searchKey = getSearchKey(data.query);
|
||||
Vue.set(this.collectionSearches, searchKey, {
|
||||
collectionIds,
|
||||
});
|
||||
},
|
||||
addWorkflowsSearch(data: {
|
||||
totalWorkflows: number;
|
||||
workflows: ITemplatesWorkflow[];
|
||||
query: ITemplatesQuery;
|
||||
}): void {
|
||||
const workflowIds = data.workflows.map((workflow) => workflow.id);
|
||||
const searchKey = getSearchKey(data.query);
|
||||
const cachedResults = this.workflowSearches[searchKey];
|
||||
if (!cachedResults) {
|
||||
Vue.set(this.workflowSearches, searchKey, {
|
||||
workflowIds: [...cachedResults.workflowIds, ...workflowIds],
|
||||
workflowIds,
|
||||
totalWorkflows: data.totalWorkflows,
|
||||
});
|
||||
},
|
||||
setWorkflowSearchLoading(query: ITemplatesQuery): void {
|
||||
const searchKey = getSearchKey(query);
|
||||
const cachedResults = this.workflowSearches[searchKey];
|
||||
if (!cachedResults) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vue.set(this.workflowSearches[searchKey], 'loadingMore', true);
|
||||
},
|
||||
setWorkflowSearchLoaded(query: ITemplatesQuery): void {
|
||||
const searchKey = getSearchKey(query);
|
||||
const cachedResults = this.workflowSearches[searchKey];
|
||||
if (!cachedResults) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Vue.set(this.workflowSearches[searchKey], 'loadingMore', false);
|
||||
},
|
||||
resetSessionId(): void {
|
||||
this.previousSessionId = this.currentSessionId;
|
||||
this.currentSessionId = '';
|
||||
},
|
||||
setSessionId(): void {
|
||||
if (!this.currentSessionId) {
|
||||
this.currentSessionId = `templates-${Date.now()}`;
|
||||
}
|
||||
},
|
||||
async fetchTemplateById(templateId: string): Promise<ITemplatesWorkflow | ITemplatesWorkflowFull> {
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
const versionCli: string = settingsStore.versionCli;
|
||||
const response = await getTemplateById(apiEndpoint, templateId, { 'n8n-version': versionCli });
|
||||
|
||||
const template: ITemplatesWorkflowFull = {
|
||||
...response.workflow,
|
||||
full: true,
|
||||
};
|
||||
this.addWorkflows([template]);
|
||||
|
||||
return template;
|
||||
},
|
||||
async fetchCollectionById(collectionId: string): Promise<ITemplatesCollection | null> {
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
const versionCli: string = settingsStore.versionCli;
|
||||
const response = await getCollectionById(apiEndpoint, collectionId, { 'n8n-version': versionCli });
|
||||
const collection: ITemplatesCollectionFull = {
|
||||
...response.collection,
|
||||
full: true,
|
||||
};
|
||||
|
||||
this.addCollections([collection]);
|
||||
this.addWorkflows(response.collection.workflows);
|
||||
return this.getCollectionById(collectionId);
|
||||
},
|
||||
async getCategories(): Promise<ITemplatesCategory[]> {
|
||||
const cachedCategories = this.allCategories;
|
||||
if (cachedCategories.length) {
|
||||
return cachedCategories;
|
||||
}
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
const versionCli: string = settingsStore.versionCli;
|
||||
const response = await getCategories(apiEndpoint, { 'n8n-version': versionCli });
|
||||
const categories = response.categories;
|
||||
|
||||
this.addCategories(categories);
|
||||
return categories;
|
||||
},
|
||||
async getCollections(query: ITemplatesQuery): Promise<ITemplatesCollection[]> {
|
||||
const cachedResults = this.getSearchedCollections(query);
|
||||
if (cachedResults) {
|
||||
return cachedResults;
|
||||
}
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
const versionCli: string = settingsStore.versionCli;
|
||||
const response = await getCollections(apiEndpoint, query, { 'n8n-version': versionCli });
|
||||
const collections = response.collections;
|
||||
|
||||
this.addCollections(collections);
|
||||
this.addCollectionSearch({query, collections});
|
||||
collections.forEach(collection => this.addWorkflows(collection.workflows as ITemplatesWorkflowFull[]));
|
||||
|
||||
return collections;
|
||||
},
|
||||
async getWorkflows(query: ITemplatesQuery): Promise<ITemplatesWorkflow[]> {
|
||||
const cachedResults = this.getSearchedWorkflows(query);
|
||||
if (cachedResults) {
|
||||
return cachedResults;
|
||||
}
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
const versionCli: string = settingsStore.versionCli;
|
||||
|
||||
const payload = await getWorkflows(apiEndpoint, {...query, skip: 0, limit: TEMPLATES_PAGE_SIZE}, { 'n8n-version': versionCli });
|
||||
|
||||
this.addWorkflows(payload.workflows);
|
||||
this.addWorkflowsSearch({...payload, query});
|
||||
return this.getSearchedWorkflows(query) || [];
|
||||
},
|
||||
async getMoreWorkflows(query: ITemplatesQuery): Promise<ITemplatesWorkflow[]> {
|
||||
if (this.isSearchLoadingMore(query) && !this.isSearchFinished(query)) {
|
||||
return [];
|
||||
}
|
||||
const cachedResults = this.getSearchedWorkflows(query) || [];
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
|
||||
this.setWorkflowSearchLoading(query);
|
||||
try {
|
||||
const payload = await getWorkflows(apiEndpoint, {...query, skip: cachedResults.length, limit: TEMPLATES_PAGE_SIZE});
|
||||
|
||||
this.setWorkflowSearchLoaded(query);
|
||||
this.addWorkflows(payload.workflows);
|
||||
this.addWorkflowsSearch({...payload, query});
|
||||
|
||||
return this.getSearchedWorkflows(query) || [];
|
||||
} catch (e) {
|
||||
this.setWorkflowSearchLoaded(query);
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
async getWorkflowTemplate(templateId: string): Promise<IWorkflowTemplate> {
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
const versionCli: string = settingsStore.versionCli;
|
||||
return await getWorkflowTemplate(apiEndpoint, templateId, { 'n8n-version': versionCli });
|
||||
},
|
||||
Vue.set(this.workflowSearches, searchKey, {
|
||||
workflowIds: [...cachedResults.workflowIds, ...workflowIds],
|
||||
totalWorkflows: data.totalWorkflows,
|
||||
});
|
||||
},
|
||||
setWorkflowSearchLoading(query: ITemplatesQuery): void {
|
||||
const searchKey = getSearchKey(query);
|
||||
const cachedResults = this.workflowSearches[searchKey];
|
||||
if (!cachedResults) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vue.set(this.workflowSearches[searchKey], 'loadingMore', true);
|
||||
},
|
||||
setWorkflowSearchLoaded(query: ITemplatesQuery): void {
|
||||
const searchKey = getSearchKey(query);
|
||||
const cachedResults = this.workflowSearches[searchKey];
|
||||
if (!cachedResults) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vue.set(this.workflowSearches[searchKey], 'loadingMore', false);
|
||||
},
|
||||
resetSessionId(): void {
|
||||
this.previousSessionId = this.currentSessionId;
|
||||
this.currentSessionId = '';
|
||||
},
|
||||
setSessionId(): void {
|
||||
if (!this.currentSessionId) {
|
||||
this.currentSessionId = `templates-${Date.now()}`;
|
||||
}
|
||||
},
|
||||
async fetchTemplateById(
|
||||
templateId: string,
|
||||
): Promise<ITemplatesWorkflow | ITemplatesWorkflowFull> {
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
const versionCli: string = settingsStore.versionCli;
|
||||
const response = await getTemplateById(apiEndpoint, templateId, {
|
||||
'n8n-version': versionCli,
|
||||
});
|
||||
|
||||
const template: ITemplatesWorkflowFull = {
|
||||
...response.workflow,
|
||||
full: true,
|
||||
};
|
||||
this.addWorkflows([template]);
|
||||
|
||||
return template;
|
||||
},
|
||||
async fetchCollectionById(collectionId: string): Promise<ITemplatesCollection | null> {
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
const versionCli: string = settingsStore.versionCli;
|
||||
const response = await getCollectionById(apiEndpoint, collectionId, {
|
||||
'n8n-version': versionCli,
|
||||
});
|
||||
const collection: ITemplatesCollectionFull = {
|
||||
...response.collection,
|
||||
full: true,
|
||||
};
|
||||
|
||||
this.addCollections([collection]);
|
||||
this.addWorkflows(response.collection.workflows);
|
||||
return this.getCollectionById(collectionId);
|
||||
},
|
||||
async getCategories(): Promise<ITemplatesCategory[]> {
|
||||
const cachedCategories = this.allCategories;
|
||||
if (cachedCategories.length) {
|
||||
return cachedCategories;
|
||||
}
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
const versionCli: string = settingsStore.versionCli;
|
||||
const response = await getCategories(apiEndpoint, { 'n8n-version': versionCli });
|
||||
const categories = response.categories;
|
||||
|
||||
this.addCategories(categories);
|
||||
return categories;
|
||||
},
|
||||
async getCollections(query: ITemplatesQuery): Promise<ITemplatesCollection[]> {
|
||||
const cachedResults = this.getSearchedCollections(query);
|
||||
if (cachedResults) {
|
||||
return cachedResults;
|
||||
}
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
const versionCli: string = settingsStore.versionCli;
|
||||
const response = await getCollections(apiEndpoint, query, { 'n8n-version': versionCli });
|
||||
const collections = response.collections;
|
||||
|
||||
this.addCollections(collections);
|
||||
this.addCollectionSearch({ query, collections });
|
||||
collections.forEach((collection) =>
|
||||
this.addWorkflows(collection.workflows as ITemplatesWorkflowFull[]),
|
||||
);
|
||||
|
||||
return collections;
|
||||
},
|
||||
async getWorkflows(query: ITemplatesQuery): Promise<ITemplatesWorkflow[]> {
|
||||
const cachedResults = this.getSearchedWorkflows(query);
|
||||
if (cachedResults) {
|
||||
return cachedResults;
|
||||
}
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
const versionCli: string = settingsStore.versionCli;
|
||||
|
||||
const payload = await getWorkflows(
|
||||
apiEndpoint,
|
||||
{ ...query, skip: 0, limit: TEMPLATES_PAGE_SIZE },
|
||||
{ 'n8n-version': versionCli },
|
||||
);
|
||||
|
||||
this.addWorkflows(payload.workflows);
|
||||
this.addWorkflowsSearch({ ...payload, query });
|
||||
return this.getSearchedWorkflows(query) || [];
|
||||
},
|
||||
async getMoreWorkflows(query: ITemplatesQuery): Promise<ITemplatesWorkflow[]> {
|
||||
if (this.isSearchLoadingMore(query) && !this.isSearchFinished(query)) {
|
||||
return [];
|
||||
}
|
||||
const cachedResults = this.getSearchedWorkflows(query) || [];
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
|
||||
this.setWorkflowSearchLoading(query);
|
||||
try {
|
||||
const payload = await getWorkflows(apiEndpoint, {
|
||||
...query,
|
||||
skip: cachedResults.length,
|
||||
limit: TEMPLATES_PAGE_SIZE,
|
||||
});
|
||||
|
||||
this.setWorkflowSearchLoaded(query);
|
||||
this.addWorkflows(payload.workflows);
|
||||
this.addWorkflowsSearch({ ...payload, query });
|
||||
|
||||
return this.getSearchedWorkflows(query) || [];
|
||||
} catch (e) {
|
||||
this.setWorkflowSearchLoaded(query);
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
async getWorkflowTemplate(templateId: string): Promise<IWorkflowTemplate> {
|
||||
const settingsStore = useSettingsStore();
|
||||
const apiEndpoint: string = settingsStore.templatesHost;
|
||||
const versionCli: string = settingsStore.versionCli;
|
||||
return await getWorkflowTemplate(apiEndpoint, templateId, { 'n8n-version': versionCli });
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
applyForOnboardingCall,
|
||||
fetchNextOnboardingPrompt,
|
||||
submitEmailOnSignup,
|
||||
} from "@/api/workflow-webhooks";
|
||||
} from '@/api/workflow-webhooks';
|
||||
import {
|
||||
ABOUT_MODAL_KEY,
|
||||
CHANGE_PASSWORD_MODAL_KEY,
|
||||
@@ -26,8 +26,9 @@ import {
|
||||
VERSIONS_MODAL_KEY,
|
||||
VIEWS,
|
||||
WORKFLOW_ACTIVE_MODAL_KEY,
|
||||
WORKFLOW_SETTINGS_MODAL_KEY, WORKFLOW_SHARE_MODAL_KEY,
|
||||
} from "@/constants";
|
||||
WORKFLOW_SETTINGS_MODAL_KEY,
|
||||
WORKFLOW_SHARE_MODAL_KEY,
|
||||
} from '@/constants';
|
||||
import {
|
||||
CurlToJSONResponse,
|
||||
IFakeDoorLocation,
|
||||
@@ -37,12 +38,12 @@ import {
|
||||
IUser,
|
||||
UIState,
|
||||
XYPosition,
|
||||
} from "@/Interface";
|
||||
import Vue from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import { useRootStore } from "./n8nRootStore";
|
||||
import { getCurlToJson } from "@/api/curlHelper";
|
||||
import { useWorkflowsStore } from "./workflows";
|
||||
} from '@/Interface';
|
||||
import Vue from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useRootStore } from './n8nRootStore';
|
||||
import { getCurlToJson } from '@/api/curlHelper';
|
||||
import { useWorkflowsStore } from './workflows';
|
||||
|
||||
export const useUIStore = defineStore(STORES.UI, {
|
||||
state: (): UIState => ({
|
||||
@@ -202,16 +203,16 @@ export const useUIStore = defineStore(STORES.UI, {
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getCurlCommand() : string|undefined {
|
||||
getCurlCommand(): string | undefined {
|
||||
return this.modals[IMPORT_CURL_MODAL_KEY].curlCommand;
|
||||
},
|
||||
getHttpNodeParameters() : string|undefined {
|
||||
getHttpNodeParameters(): string | undefined {
|
||||
return this.modals[IMPORT_CURL_MODAL_KEY].httpNodeParameters;
|
||||
},
|
||||
areExpressionsDisabled() : boolean {
|
||||
areExpressionsDisabled(): boolean {
|
||||
return this.currentView === VIEWS.DEMO;
|
||||
},
|
||||
isVersionsOpen() : boolean {
|
||||
isVersionsOpen(): boolean {
|
||||
return this.modals[VERSIONS_MODAL_KEY].open;
|
||||
},
|
||||
isModalOpen() {
|
||||
@@ -230,18 +231,24 @@ export const useUIStore = defineStore(STORES.UI, {
|
||||
return (name: string) => this.modals[name].data;
|
||||
},
|
||||
getFakeDoorByLocation() {
|
||||
return (location: IFakeDoorLocation) => this.fakeDoorFeatures.filter(fakeDoor => fakeDoor.uiLocations.includes(location));
|
||||
return (location: IFakeDoorLocation) =>
|
||||
this.fakeDoorFeatures.filter((fakeDoor) => fakeDoor.uiLocations.includes(location));
|
||||
},
|
||||
getFakeDoorById() {
|
||||
return (id: string) => this.fakeDoorFeatures.find(fakeDoor => fakeDoor.id.toString() === id);
|
||||
return (id: string) =>
|
||||
this.fakeDoorFeatures.find((fakeDoor) => fakeDoor.id.toString() === id);
|
||||
},
|
||||
isNodeView() : boolean {
|
||||
return [VIEWS.NEW_WORKFLOW.toString(), VIEWS.WORKFLOW.toString(), VIEWS.EXECUTION.toString()].includes(this.currentView);
|
||||
isNodeView(): boolean {
|
||||
return [
|
||||
VIEWS.NEW_WORKFLOW.toString(),
|
||||
VIEWS.WORKFLOW.toString(),
|
||||
VIEWS.EXECUTION.toString(),
|
||||
].includes(this.currentView);
|
||||
},
|
||||
isActionActive() {
|
||||
return (action: string) => this.activeActions.includes(action);
|
||||
},
|
||||
getSelectedNodes() : INodeUi[] {
|
||||
getSelectedNodes(): INodeUi[] {
|
||||
const seen = new Set();
|
||||
return this.selectedNodes.filter((node: INodeUi) => {
|
||||
// dedupe for instances when same node is selected in different ways
|
||||
@@ -265,20 +272,20 @@ export const useUIStore = defineStore(STORES.UI, {
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setMode(name: string, mode: string): void {
|
||||
setMode(name: string, mode: string): void {
|
||||
Vue.set(this.modals[name], 'mode', mode);
|
||||
},
|
||||
setActiveId(name: string, id: string): void {
|
||||
Vue.set(this.modals[name], 'activeId', id);
|
||||
},
|
||||
setModalData (payload: { name: string, data: Record<string, unknown> }) {
|
||||
setModalData(payload: { name: string; data: Record<string, unknown> }) {
|
||||
Vue.set(this.modals[payload.name], 'data', payload.data);
|
||||
},
|
||||
openModal(name: string): void {
|
||||
Vue.set(this.modals[name], 'open', true);
|
||||
this.modalStack = [name].concat(this.modalStack);
|
||||
},
|
||||
openModalWithData (payload: { name: string, data: Record<string, unknown> }): void {
|
||||
openModalWithData(payload: { name: string; data: Record<string, unknown> }): void {
|
||||
this.setModalData(payload);
|
||||
this.openModal(payload.name);
|
||||
},
|
||||
@@ -395,16 +402,16 @@ export const useUIStore = defineStore(STORES.UI, {
|
||||
const updated = this.sidebarMenuItems.concat(menuItems);
|
||||
Vue.set(this, 'sidebarMenuItems', updated);
|
||||
},
|
||||
setCurlCommand (payload: { name: string, command: string }): void {
|
||||
setCurlCommand(payload: { name: string; command: string }): void {
|
||||
Vue.set(this.modals[payload.name], 'curlCommand', payload.command);
|
||||
},
|
||||
setHttpNodeParameters (payload: { name: string, parameters: string }): void {
|
||||
setHttpNodeParameters(payload: { name: string; parameters: string }): void {
|
||||
Vue.set(this.modals[payload.name], 'httpNodeParameters', payload.parameters);
|
||||
},
|
||||
toggleSidebarMenuCollapse (): void {
|
||||
toggleSidebarMenuCollapse(): void {
|
||||
this.sidebarMenuCollapsed = !this.sidebarMenuCollapsed;
|
||||
},
|
||||
async getCurlToJson (curlCommand: string): Promise<CurlToJSONResponse> {
|
||||
async getCurlToJson(curlCommand: string): Promise<CurlToJSONResponse> {
|
||||
const rootStore = useRootStore();
|
||||
return await getCurlToJson(rootStore.getRestApiContext, curlCommand);
|
||||
},
|
||||
|
||||
@@ -1,14 +1,40 @@
|
||||
import { changePassword, deleteUser, getCurrentUser, getUsers, inviteUsers, login, loginCurrentUser, logout, reinvite, sendForgotPasswordEmail, setupOwner, signup, skipOwnerSetup, submitPersonalizationSurvey, updateCurrentUser, updateCurrentUserPassword, validatePasswordToken, validateSignupToken } from "@/api/users";
|
||||
import { PERSONALIZATION_MODAL_KEY, STORES } from "@/constants";
|
||||
import { IInviteResponse, IPersonalizationLatestVersion, IUser, IUserResponse, IUsersState } from "@/Interface";
|
||||
import { getPersonalizedNodeTypes, isAuthorized, PERMISSIONS, ROLE } from "@/utils";
|
||||
import { defineStore } from "pinia";
|
||||
import Vue from "vue";
|
||||
import { useRootStore } from "./n8nRootStore";
|
||||
import { useSettingsStore } from "./settings";
|
||||
import { useUIStore } from "./ui";
|
||||
import {
|
||||
changePassword,
|
||||
deleteUser,
|
||||
getCurrentUser,
|
||||
getUsers,
|
||||
inviteUsers,
|
||||
login,
|
||||
loginCurrentUser,
|
||||
logout,
|
||||
reinvite,
|
||||
sendForgotPasswordEmail,
|
||||
setupOwner,
|
||||
signup,
|
||||
skipOwnerSetup,
|
||||
submitPersonalizationSurvey,
|
||||
updateCurrentUser,
|
||||
updateCurrentUserPassword,
|
||||
validatePasswordToken,
|
||||
validateSignupToken,
|
||||
} from '@/api/users';
|
||||
import { PERSONALIZATION_MODAL_KEY, STORES } from '@/constants';
|
||||
import {
|
||||
IInviteResponse,
|
||||
IPersonalizationLatestVersion,
|
||||
IUser,
|
||||
IUserResponse,
|
||||
IUsersState,
|
||||
} from '@/Interface';
|
||||
import { getPersonalizedNodeTypes, isAuthorized, PERMISSIONS, ROLE } from '@/utils';
|
||||
import { defineStore } from 'pinia';
|
||||
import Vue from 'vue';
|
||||
import { useRootStore } from './n8nRootStore';
|
||||
import { useSettingsStore } from './settings';
|
||||
import { useUIStore } from './ui';
|
||||
|
||||
const isDefaultUser = (user: IUserResponse | null) => Boolean(user && user.isPending && user.globalRole && user.globalRole.name === ROLE.Owner);
|
||||
const isDefaultUser = (user: IUserResponse | null) =>
|
||||
Boolean(user && user.isPending && user.globalRole && user.globalRole.name === ROLE.Owner);
|
||||
const isPendingUser = (user: IUserResponse | null) => Boolean(user && user.isPending);
|
||||
|
||||
export const useUsersStore = defineStore(STORES.USERS, {
|
||||
@@ -72,7 +98,9 @@ export const useUsersStore = defineStore(STORES.USERS, {
|
||||
};
|
||||
const user: IUser = {
|
||||
...updatedUser,
|
||||
fullName: userResponse.firstName? `${updatedUser.firstName} ${updatedUser.lastName || ''}`: undefined,
|
||||
fullName: userResponse.firstName
|
||||
? `${updatedUser.firstName} ${updatedUser.lastName || ''}`
|
||||
: undefined,
|
||||
isDefaultUser: isDefaultUser(updatedUser),
|
||||
isPendingUser: isPendingUser(updatedUser),
|
||||
isOwner: Boolean(updatedUser.globalRole && updatedUser.globalRole.name === ROLE.Owner),
|
||||
@@ -107,7 +135,7 @@ export const useUsersStore = defineStore(STORES.USERS, {
|
||||
this.currentUserId = user.id;
|
||||
}
|
||||
},
|
||||
async loginWithCreds(params: {email: string, password: string}): Promise<void> {
|
||||
async loginWithCreds(params: { email: string; password: string }): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
const user = await login(rootStore.getRestApiContext, params);
|
||||
if (user) {
|
||||
@@ -120,7 +148,12 @@ export const useUsersStore = defineStore(STORES.USERS, {
|
||||
await logout(rootStore.getRestApiContext);
|
||||
this.currentUserId = null;
|
||||
},
|
||||
async createOwner(params: { firstName: string; lastName: string; email: string; password: string;}): Promise<void> {
|
||||
async createOwner(params: {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
password: string;
|
||||
}): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
const user = await setupOwner(rootStore.getRestApiContext, params);
|
||||
const settingsStore = useSettingsStore();
|
||||
@@ -130,11 +163,20 @@ export const useUsersStore = defineStore(STORES.USERS, {
|
||||
settingsStore.stopShowingSetupPage();
|
||||
}
|
||||
},
|
||||
async validateSignupToken(params: {inviteeId: string, inviterId: string}): Promise<{ inviter: { firstName: string, lastName: string } }> {
|
||||
async validateSignupToken(params: {
|
||||
inviteeId: string;
|
||||
inviterId: string;
|
||||
}): Promise<{ inviter: { firstName: string; lastName: string } }> {
|
||||
const rootStore = useRootStore();
|
||||
return await validateSignupToken(rootStore.getRestApiContext, params);
|
||||
},
|
||||
async signup(params: { inviteeId: string; inviterId: string; firstName: string; lastName: string; password: string;}): Promise<void> {
|
||||
async signup(params: {
|
||||
inviteeId: string;
|
||||
inviterId: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
password: string;
|
||||
}): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
const user = await signup(rootStore.getRestApiContext, params);
|
||||
if (user) {
|
||||
@@ -142,28 +184,46 @@ export const useUsersStore = defineStore(STORES.USERS, {
|
||||
this.currentUserId = user.id;
|
||||
}
|
||||
},
|
||||
async sendForgotPasswordEmail( params: {email: string}): Promise<void> {
|
||||
async sendForgotPasswordEmail(params: { email: string }): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
await sendForgotPasswordEmail(rootStore.getRestApiContext, params);
|
||||
},
|
||||
async validatePasswordToken(params: {token: string, userId: string}): Promise<void> {
|
||||
async validatePasswordToken(params: { token: string; userId: string }): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
await validatePasswordToken(rootStore.getRestApiContext, params);
|
||||
},
|
||||
async changePassword(params: {token: string, password: string, userId: string}): Promise<void> {
|
||||
async changePassword(params: {
|
||||
token: string;
|
||||
password: string;
|
||||
userId: string;
|
||||
}): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
await changePassword(rootStore.getRestApiContext, params);
|
||||
},
|
||||
async updateUser(params: {id: string, firstName: string, lastName: string, email: string}): Promise<void> {
|
||||
async updateUser(params: {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
}): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
const user = await updateCurrentUser(rootStore.getRestApiContext, params);
|
||||
this.addUsers([user]);
|
||||
},
|
||||
async updateCurrentUserPassword({password, currentPassword}: {password: string, currentPassword: string}): Promise<void> {
|
||||
async updateCurrentUserPassword({
|
||||
password,
|
||||
currentPassword,
|
||||
}: {
|
||||
password: string;
|
||||
currentPassword: string;
|
||||
}): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
await updateCurrentUserPassword(rootStore.getRestApiContext, {newPassword: password, currentPassword});
|
||||
await updateCurrentUserPassword(rootStore.getRestApiContext, {
|
||||
newPassword: password,
|
||||
currentPassword,
|
||||
});
|
||||
},
|
||||
async deleteUser(params: { id: string, transferId?: string}): Promise<void> {
|
||||
async deleteUser(params: { id: string; transferId?: string }): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
await deleteUser(rootStore.getRestApiContext, params);
|
||||
this.deleteUserById(params.id);
|
||||
@@ -173,13 +233,13 @@ export const useUsersStore = defineStore(STORES.USERS, {
|
||||
const users = await getUsers(rootStore.getRestApiContext);
|
||||
this.addUsers(users);
|
||||
},
|
||||
async inviteUsers(params: Array<{email: string}>): Promise<IInviteResponse[]> {
|
||||
async inviteUsers(params: Array<{ email: string }>): Promise<IInviteResponse[]> {
|
||||
const rootStore = useRootStore();
|
||||
const users = await inviteUsers(rootStore.getRestApiContext, params);
|
||||
this.addUsers(users.map(({user}) => ({ isPending: true, ...user })));
|
||||
this.addUsers(users.map(({ user }) => ({ isPending: true, ...user })));
|
||||
return users;
|
||||
},
|
||||
async reinviteUser(params: {id: string}): Promise<void> {
|
||||
async reinviteUser(params: { id: string }): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
await reinvite(rootStore.getRestApiContext, params);
|
||||
},
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { getNextVersions } from "@/api/versions";
|
||||
import { STORES } from "@/constants";
|
||||
import { IVersion, IVersionNotificationSettings, IVersionsState } from "@/Interface";
|
||||
import { defineStore } from "pinia";
|
||||
import { useRootStore } from "./n8nRootStore";
|
||||
import { getNextVersions } from '@/api/versions';
|
||||
import { STORES } from '@/constants';
|
||||
import { IVersion, IVersionNotificationSettings, IVersionsState } from '@/Interface';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useRootStore } from './n8nRootStore';
|
||||
|
||||
export const useVersionsStore = defineStore(STORES.VERSIONS, {
|
||||
state: (): IVersionsState => ({
|
||||
@@ -26,7 +26,7 @@ export const useVersionsStore = defineStore(STORES.VERSIONS, {
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setVersions({versions, currentVersion}: {versions: IVersion[], currentVersion: string}) {
|
||||
setVersions({ versions, currentVersion }: { versions: IVersion[]; currentVersion: string }) {
|
||||
this.nextVersions = versions.filter((version) => version.name !== currentVersion);
|
||||
this.currentVersion = versions.find((version) => version.name === currentVersion);
|
||||
},
|
||||
@@ -43,8 +43,7 @@ export const useVersionsStore = defineStore(STORES.VERSIONS, {
|
||||
const versions = await getNextVersions(endpoint, currentVersion, instanceId);
|
||||
this.setVersions({ versions, currentVersion });
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import { STORES } from "@/constants";
|
||||
import {IFakeDoor, INodeUi, IRootState, NestedRecord} from "@/Interface";
|
||||
import { IMenuItem } from "n8n-design-system";
|
||||
import { IWorkflowSettings } from "n8n-workflow";
|
||||
import { defineStore } from "pinia";
|
||||
import { useRootStore } from "./n8nRootStore";
|
||||
import { useNDVStore } from "./ndv";
|
||||
import { useSettingsStore } from "./settings";
|
||||
import { useUIStore } from "./ui";
|
||||
import { useUsersStore } from "./users";
|
||||
import { useWorkflowsStore } from "./workflows";
|
||||
import { STORES } from '@/constants';
|
||||
import { IFakeDoor, INodeUi, IRootState, NestedRecord } from '@/Interface';
|
||||
import { IMenuItem } from 'n8n-design-system';
|
||||
import { IWorkflowSettings } from 'n8n-workflow';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useRootStore } from './n8nRootStore';
|
||||
import { useNDVStore } from './ndv';
|
||||
import { useSettingsStore } from './settings';
|
||||
import { useUIStore } from './ui';
|
||||
import { useUsersStore } from './users';
|
||||
import { useWorkflowsStore } from './workflows';
|
||||
|
||||
export const useWebhooksStore = defineStore(STORES.WEBHOOKS, {
|
||||
getters: {
|
||||
globalRoleName(): string {
|
||||
return useUsersStore().globalRoleName;
|
||||
},
|
||||
getDynamicTranslations () {
|
||||
getDynamicTranslations() {
|
||||
return useUIStore().dynamicTranslations;
|
||||
},
|
||||
getFakeDoorFeatures () {
|
||||
getFakeDoorFeatures() {
|
||||
return useUIStore().fakeDoorFeatures;
|
||||
},
|
||||
isUserManagementEnabled () {
|
||||
isUserManagementEnabled() {
|
||||
return useSettingsStore().isUserManagementEnabled;
|
||||
},
|
||||
getFakeDoorItems(): IFakeDoor[] {
|
||||
@@ -48,9 +48,11 @@ export const useWebhooksStore = defineStore(STORES.WEBHOOKS, {
|
||||
activeExecutionId(): string {
|
||||
return useWorkflowsStore().activeExecutionId || '';
|
||||
},
|
||||
nodeByName: (state: IRootState) => (nodeName: string): INodeUi | null => {
|
||||
return useWorkflowsStore().getNodeByName(nodeName);
|
||||
},
|
||||
nodeByName:
|
||||
(state: IRootState) =>
|
||||
(nodeName: string): INodeUi | null => {
|
||||
return useWorkflowsStore().getNodeByName(nodeName);
|
||||
},
|
||||
allNodes(): INodeUi[] {
|
||||
return useWorkflowsStore().allNodes;
|
||||
},
|
||||
@@ -59,7 +61,7 @@ export const useWebhooksStore = defineStore(STORES.WEBHOOKS, {
|
||||
addSidebarMenuItems(menuItems: IMenuItem[]) {
|
||||
const uiStore = useUIStore();
|
||||
const updated = uiStore.sidebarMenuItems.concat(menuItems);
|
||||
uiStore.sidebarMenuItems = updated;
|
||||
uiStore.sidebarMenuItems = updated;
|
||||
},
|
||||
setFakeDoorFeatures(fakeDoors: IFakeDoor[]): void {
|
||||
useUIStore().fakeDoorFeatures = fakeDoors;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import Vue from 'vue';
|
||||
import {
|
||||
IUser,
|
||||
} from '../Interface';
|
||||
import {setWorkflowSharedWith} from "@/api/workflows.ee";
|
||||
import {EnterpriseEditionFeature, STORES} from "@/constants";
|
||||
import {useRootStore} from "@/stores/n8nRootStore";
|
||||
import {useSettingsStore} from "@/stores/settings";
|
||||
import {defineStore} from "pinia";
|
||||
import {useWorkflowsStore} from "@/stores/workflows";
|
||||
import {i18n} from "@/plugins/i18n";
|
||||
import { IUser } from '../Interface';
|
||||
import { setWorkflowSharedWith } from '@/api/workflows.ee';
|
||||
import { EnterpriseEditionFeature, STORES } from '@/constants';
|
||||
import { useRootStore } from '@/stores/n8nRootStore';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useWorkflowsStore } from '@/stores/workflows';
|
||||
import { i18n } from '@/plugins/i18n';
|
||||
|
||||
export const useWorkflowsEEStore = defineStore(STORES.WORKFLOWS_EE, {
|
||||
state() { return {}; },
|
||||
state() {
|
||||
return {};
|
||||
},
|
||||
getters: {
|
||||
getWorkflowOwnerName() {
|
||||
return (workflowId: string): string => {
|
||||
@@ -23,49 +23,54 @@ export const useWorkflowsEEStore = defineStore(STORES.WORKFLOWS_EE, {
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setWorkflowOwnedBy(payload: { workflowId: string, ownedBy: Partial<IUser> }): void {
|
||||
setWorkflowOwnedBy(payload: { workflowId: string; ownedBy: Partial<IUser> }): void {
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
|
||||
Vue.set(workflowsStore.workflowsById[payload.workflowId], 'ownedBy', payload.ownedBy);
|
||||
Vue.set(workflowsStore.workflow, 'ownedBy', payload.ownedBy);
|
||||
},
|
||||
setWorkflowSharedWith(payload: { workflowId: string, sharedWith: Array<Partial<IUser>> }): void {
|
||||
setWorkflowSharedWith(payload: {
|
||||
workflowId: string;
|
||||
sharedWith: Array<Partial<IUser>>;
|
||||
}): void {
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
|
||||
Vue.set(workflowsStore.workflowsById[payload.workflowId], 'sharedWith', payload.sharedWith);
|
||||
Vue.set(workflowsStore.workflow, 'sharedWith', payload.sharedWith);
|
||||
},
|
||||
addWorkflowSharee(payload: { workflowId: string, sharee: Partial<IUser> }): void {
|
||||
addWorkflowSharee(payload: { workflowId: string; sharee: Partial<IUser> }): void {
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
|
||||
Vue.set(
|
||||
workflowsStore.workflowsById[payload.workflowId],
|
||||
'sharedWith',
|
||||
(workflowsStore.workflowsById[payload.workflowId].sharedWith || []).concat([payload.sharee]),
|
||||
(workflowsStore.workflowsById[payload.workflowId].sharedWith || []).concat([
|
||||
payload.sharee,
|
||||
]),
|
||||
);
|
||||
},
|
||||
removeWorkflowSharee(payload: { workflowId: string, sharee: Partial<IUser> }): void {
|
||||
removeWorkflowSharee(payload: { workflowId: string; sharee: Partial<IUser> }): void {
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
|
||||
Vue.set(
|
||||
workflowsStore.workflowsById[payload.workflowId],
|
||||
'sharedWith',
|
||||
(workflowsStore.workflowsById[payload.workflowId].sharedWith || [])
|
||||
.filter((sharee) => sharee.id !== payload.sharee.id),
|
||||
(workflowsStore.workflowsById[payload.workflowId].sharedWith || []).filter(
|
||||
(sharee) => sharee.id !== payload.sharee.id,
|
||||
),
|
||||
);
|
||||
},
|
||||
async saveWorkflowSharedWith(payload: { sharedWith: Array<Partial<IUser>>; workflowId: string; }): Promise<void> {
|
||||
async saveWorkflowSharedWith(payload: {
|
||||
sharedWith: Array<Partial<IUser>>;
|
||||
workflowId: string;
|
||||
}): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
if (settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.WorkflowSharing)) {
|
||||
await setWorkflowSharedWith(
|
||||
rootStore.getRestApiContext,
|
||||
payload.workflowId,
|
||||
{
|
||||
shareWithIds: payload.sharedWith.map((sharee) => sharee.id as string),
|
||||
},
|
||||
);
|
||||
await setWorkflowSharedWith(rootStore.getRestApiContext, payload.workflowId, {
|
||||
shareWithIds: payload.sharedWith.map((sharee) => sharee.id as string),
|
||||
});
|
||||
|
||||
this.setWorkflowSharedWith(payload);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
MAX_WORKFLOW_NAME_LENGTH,
|
||||
PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
||||
STORES,
|
||||
} from "@/constants";
|
||||
} from '@/constants';
|
||||
import {
|
||||
IExecutionResponse,
|
||||
IExecutionsCurrentSummaryExtended,
|
||||
@@ -20,8 +20,8 @@ import {
|
||||
IWorkflowDb,
|
||||
IWorkflowsMap,
|
||||
WorkflowsState,
|
||||
} from "@/Interface";
|
||||
import { defineStore } from "pinia";
|
||||
} from '@/Interface';
|
||||
import { defineStore } from 'pinia';
|
||||
import {
|
||||
deepCopy,
|
||||
IConnection,
|
||||
@@ -38,24 +38,29 @@ import {
|
||||
ITaskData,
|
||||
IWorkflowSettings,
|
||||
} from 'n8n-workflow';
|
||||
import Vue from "vue";
|
||||
import Vue from 'vue';
|
||||
|
||||
import { useRootStore } from "./n8nRootStore";
|
||||
import { useRootStore } from './n8nRootStore';
|
||||
import {
|
||||
getActiveWorkflows,
|
||||
getCurrentExecutions,
|
||||
getFinishedExecutions,
|
||||
getNewWorkflow,
|
||||
getWorkflows,
|
||||
} from "@/api/workflows";
|
||||
import {useUIStore} from "./ui";
|
||||
import {dataPinningEventBus} from "@/event-bus/data-pinning-event-bus";
|
||||
import {isJsonKeyObject, getPairedItemsMapping, stringSizeInBytes, isObjectLiteral} from "@/utils";
|
||||
import {useNDVStore} from "./ndv";
|
||||
import {useNodeTypesStore} from "./nodeTypes";
|
||||
import {useWorkflowsEEStore} from "@/stores/workflows.ee";
|
||||
import {useUsersStore} from "@/stores/users";
|
||||
import {useSettingsStore} from "@/stores/settings";
|
||||
} from '@/api/workflows';
|
||||
import { useUIStore } from './ui';
|
||||
import { dataPinningEventBus } from '@/event-bus/data-pinning-event-bus';
|
||||
import {
|
||||
isJsonKeyObject,
|
||||
getPairedItemsMapping,
|
||||
stringSizeInBytes,
|
||||
isObjectLiteral,
|
||||
} from '@/utils';
|
||||
import { useNDVStore } from './ndv';
|
||||
import { useNodeTypesStore } from './nodeTypes';
|
||||
import { useWorkflowsEEStore } from '@/stores/workflows.ee';
|
||||
import { useUsersStore } from '@/stores/users';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
|
||||
const createEmptyWorkflow = (): IWorkflowDb => ({
|
||||
id: PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
||||
@@ -101,29 +106,31 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
workflowVersionId(): string | undefined {
|
||||
return this.workflow.versionId;
|
||||
},
|
||||
workflowSettings() : IWorkflowSettings {
|
||||
workflowSettings(): IWorkflowSettings {
|
||||
if (this.workflow.settings === undefined) {
|
||||
return {};
|
||||
}
|
||||
return this.workflow.settings;
|
||||
},
|
||||
workflowTags() : string[] {
|
||||
workflowTags(): string[] {
|
||||
return this.workflow.tags as string[];
|
||||
},
|
||||
allWorkflows() : IWorkflowDb[] {
|
||||
return Object.values(this.workflowsById)
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
allWorkflows(): IWorkflowDb[] {
|
||||
return Object.values(this.workflowsById).sort((a, b) => a.name.localeCompare(b.name));
|
||||
},
|
||||
isNewWorkflow() : boolean {
|
||||
isNewWorkflow(): boolean {
|
||||
return this.workflow.id === PLACEHOLDER_EMPTY_WORKFLOW_ID;
|
||||
},
|
||||
isWorkflowActive(): boolean {
|
||||
return this.workflow.active;
|
||||
},
|
||||
workflowTriggerNodes() : INodeUi[] {
|
||||
workflowTriggerNodes(): INodeUi[] {
|
||||
return this.workflow.nodes.filter((node: INodeUi) => {
|
||||
const nodeTypesStore = useNodeTypesStore();
|
||||
const nodeType = nodeTypesStore.getNodeType(node.type as string, node.typeVersion as number);
|
||||
const nodeType = nodeTypesStore.getNodeType(
|
||||
node.type as string,
|
||||
node.typeVersion as number,
|
||||
);
|
||||
return nodeType && nodeType.group.includes('trigger');
|
||||
});
|
||||
},
|
||||
@@ -131,7 +138,11 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
return !!this.workflow.nodes.find((node: INodeUi) => !!node.webhookId);
|
||||
},
|
||||
getWorkflowRunData(): IRunData | null {
|
||||
if (!this.workflowExecutionData || !this.workflowExecutionData.data || !this.workflowExecutionData.data.resultData) {
|
||||
if (
|
||||
!this.workflowExecutionData ||
|
||||
!this.workflowExecutionData.data ||
|
||||
!this.workflowExecutionData.data.resultData
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return this.workflowExecutionData.data.resultData.runData;
|
||||
@@ -154,10 +165,10 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
},
|
||||
|
||||
// Node getters
|
||||
allConnections() : IConnections {
|
||||
allConnections(): IConnections {
|
||||
return this.workflow.connections;
|
||||
},
|
||||
outgoingConnectionsByNodeName() {
|
||||
outgoingConnectionsByNodeName() {
|
||||
return (nodeName: string): INodeConnections => {
|
||||
if (this.workflow.connections.hasOwnProperty(nodeName)) {
|
||||
return this.workflow.connections[nodeName];
|
||||
@@ -165,10 +176,10 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
return {};
|
||||
};
|
||||
},
|
||||
allNodes() : INodeUi[] {
|
||||
allNodes(): INodeUi[] {
|
||||
return this.workflow.nodes;
|
||||
},
|
||||
nodesByName() : { [name: string]: INodeUi } {
|
||||
nodesByName(): { [name: string]: INodeUi } {
|
||||
return this.workflow.nodes.reduce((accu: { [name: string]: INodeUi }, node) => {
|
||||
accu[node.name] = node;
|
||||
return accu;
|
||||
@@ -178,7 +189,8 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
return (nodeName: string): INodeUi | null => this.nodesByName[nodeName] || null;
|
||||
},
|
||||
getNodeById() {
|
||||
return (nodeId: string): INodeUi | undefined => this.workflow.nodes.find((node: INodeUi) => node.id === nodeId);
|
||||
return (nodeId: string): INodeUi | undefined =>
|
||||
this.workflow.nodes.find((node: INodeUi) => node.id === nodeId);
|
||||
},
|
||||
nodesIssuesExist(): boolean {
|
||||
for (const node of this.workflow.nodes) {
|
||||
@@ -195,28 +207,31 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
pinDataSize(): number {
|
||||
const ndvStore = useNDVStore();
|
||||
const activeNode = ndvStore.activeNodeName;
|
||||
return this.workflow.nodes
|
||||
.reduce((acc, node) => {
|
||||
if (typeof node.pinData !== 'undefined' && node.name !== activeNode) {
|
||||
acc += stringSizeInBytes(node.pinData);
|
||||
}
|
||||
return this.workflow.nodes.reduce((acc, node) => {
|
||||
if (typeof node.pinData !== 'undefined' && node.name !== activeNode) {
|
||||
acc += stringSizeInBytes(node.pinData);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, 0);
|
||||
return acc;
|
||||
}, 0);
|
||||
},
|
||||
executedNode(): string | undefined {
|
||||
return this.workflowExecutionData ? this.workflowExecutionData.executedNode : undefined;
|
||||
},
|
||||
getParametersLastUpdate(): ((name: string) => number | undefined) {
|
||||
return (nodeName: string) => this.nodeMetadata[nodeName] && this.nodeMetadata[nodeName].parametersLastUpdatedAt;
|
||||
getParametersLastUpdate(): (name: string) => number | undefined {
|
||||
return (nodeName: string) =>
|
||||
this.nodeMetadata[nodeName] && this.nodeMetadata[nodeName].parametersLastUpdatedAt;
|
||||
},
|
||||
|
||||
// Executions getters
|
||||
getExecutionDataById(): (id: string) => IExecutionsSummary | undefined {
|
||||
return (id: string): IExecutionsSummary | undefined => this.currentWorkflowExecutions.find(execution => execution.id === id);
|
||||
return (id: string): IExecutionsSummary | undefined =>
|
||||
this.currentWorkflowExecutions.find((execution) => execution.id === id);
|
||||
},
|
||||
getAllLoadedFinishedExecutions(): IExecutionsSummary[] {
|
||||
return this.currentWorkflowExecutions.filter(ex => ex.finished === true || ex.stoppedAt !== undefined);
|
||||
return this.currentWorkflowExecutions.filter(
|
||||
(ex) => ex.finished === true || ex.stoppedAt !== undefined,
|
||||
);
|
||||
},
|
||||
getWorkflowExecution(): IExecutionResponse | null {
|
||||
return this.workflowExecutionData;
|
||||
@@ -226,7 +241,6 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
|
||||
// Workflow actions
|
||||
|
||||
async fetchAllWorkflows(): Promise<IWorkflowDb[]> {
|
||||
@@ -246,8 +260,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
try {
|
||||
const rootStore = useRootStore();
|
||||
workflowData = await getNewWorkflow(rootStore.getRestApiContext, name);
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
// in case of error, default to original name
|
||||
workflowData.name = name || DEFAULT_NEW_WORKFLOW_NAME;
|
||||
}
|
||||
@@ -268,26 +281,32 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
}
|
||||
},
|
||||
|
||||
setWorkflowId (id: string): void {
|
||||
setWorkflowId(id: string): void {
|
||||
this.workflow.id = id === 'new' ? PLACEHOLDER_EMPTY_WORKFLOW_ID : id;
|
||||
},
|
||||
|
||||
setUsedCredentials(data: IUsedCredential[]) {
|
||||
this.workflow.usedCredentials = data;
|
||||
this.usedCredentials = data.reduce<{ [name: string]: IUsedCredential }>((accu, credential) => {
|
||||
accu[credential.id!] = credential;
|
||||
return accu;
|
||||
}, {});
|
||||
this.usedCredentials = data.reduce<{ [name: string]: IUsedCredential }>(
|
||||
(accu, credential) => {
|
||||
accu[credential.id!] = credential;
|
||||
return accu;
|
||||
},
|
||||
{},
|
||||
);
|
||||
},
|
||||
|
||||
setWorkflowName(data: { newName: string, setStateDirty: boolean }): void {
|
||||
setWorkflowName(data: { newName: string; setStateDirty: boolean }): void {
|
||||
if (data.setStateDirty === true) {
|
||||
const uiStore = useUIStore();
|
||||
uiStore.stateIsDirty = true;
|
||||
}
|
||||
this.workflow.name = data.newName;
|
||||
|
||||
if (this.workflow.id !== PLACEHOLDER_EMPTY_WORKFLOW_ID && this.workflowsById[this.workflow.id]) {
|
||||
if (
|
||||
this.workflow.id !== PLACEHOLDER_EMPTY_WORKFLOW_ID &&
|
||||
this.workflowsById[this.workflow.id]
|
||||
) {
|
||||
this.workflowsById[this.workflow.id].name = data.newName;
|
||||
}
|
||||
},
|
||||
@@ -297,9 +316,14 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
},
|
||||
|
||||
// replace invalid credentials in workflow
|
||||
replaceInvalidWorkflowCredentials(data: {credentials: INodeCredentialsDetails, invalid: INodeCredentialsDetails, type: string}): void {
|
||||
this.workflow.nodes.forEach((node : INodeUi) => {
|
||||
const nodeCredentials: INodeCredentials | undefined = (node as unknown as INode).credentials;
|
||||
replaceInvalidWorkflowCredentials(data: {
|
||||
credentials: INodeCredentialsDetails;
|
||||
invalid: INodeCredentialsDetails;
|
||||
type: string;
|
||||
}): void {
|
||||
this.workflow.nodes.forEach((node: INodeUi) => {
|
||||
const nodeCredentials: INodeCredentials | undefined = (node as unknown as INode)
|
||||
.credentials;
|
||||
|
||||
if (!nodeCredentials || !nodeCredentials[data.type]) {
|
||||
return;
|
||||
@@ -307,7 +331,10 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
|
||||
const nodeCredentialDetails: INodeCredentialsDetails | string = nodeCredentials[data.type];
|
||||
|
||||
if (typeof nodeCredentialDetails === 'string' && nodeCredentialDetails === data.invalid.name) {
|
||||
if (
|
||||
typeof nodeCredentialDetails === 'string' &&
|
||||
nodeCredentialDetails === data.invalid.name
|
||||
) {
|
||||
(node.credentials as INodeCredentials)[data.type] = data.credentials;
|
||||
return;
|
||||
}
|
||||
@@ -325,7 +352,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
});
|
||||
},
|
||||
|
||||
setWorkflows(workflows: IWorkflowDb[]) : void {
|
||||
setWorkflows(workflows: IWorkflowDb[]): void {
|
||||
this.workflowsById = workflows.reduce<IWorkflowsMap>((acc, workflow: IWorkflowDb) => {
|
||||
if (workflow.id) {
|
||||
acc[workflow.id] = workflow;
|
||||
@@ -335,12 +362,12 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
}, {});
|
||||
},
|
||||
|
||||
deleteWorkflow(id: string) : void {
|
||||
deleteWorkflow(id: string): void {
|
||||
const { [id]: deletedWorkflow, ...workflows } = this.workflowsById;
|
||||
this.workflowsById = workflows;
|
||||
},
|
||||
|
||||
addWorkflow(workflow: IWorkflowDb) : void {
|
||||
addWorkflow(workflow: IWorkflowDb): void {
|
||||
Vue.set(this.workflowsById, workflow.id, {
|
||||
...this.workflowsById[workflow.id],
|
||||
...deepCopy(workflow),
|
||||
@@ -357,7 +384,6 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
if (this.workflowsById[workflowId]) {
|
||||
this.workflowsById[workflowId].active = true;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
setWorkflowInactive(workflowId: string): void {
|
||||
@@ -377,27 +403,27 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
return activeWorkflows;
|
||||
},
|
||||
|
||||
setActive(newActive: boolean) : void {
|
||||
setActive(newActive: boolean): void {
|
||||
this.workflow.active = newActive;
|
||||
},
|
||||
|
||||
async getDuplicateCurrentWorkflowName(currentWorkflowName: string): Promise<string> {
|
||||
if (currentWorkflowName && (currentWorkflowName.length + DUPLICATE_POSTFFIX.length) >= MAX_WORKFLOW_NAME_LENGTH) {
|
||||
if (
|
||||
currentWorkflowName &&
|
||||
currentWorkflowName.length + DUPLICATE_POSTFFIX.length >= MAX_WORKFLOW_NAME_LENGTH
|
||||
) {
|
||||
return currentWorkflowName;
|
||||
}
|
||||
|
||||
let newName = `${currentWorkflowName}${DUPLICATE_POSTFFIX}`;
|
||||
try {
|
||||
const rootStore = useRootStore();
|
||||
const newWorkflow = await getNewWorkflow(rootStore.getRestApiContext, newName );
|
||||
const newWorkflow = await getNewWorkflow(rootStore.getRestApiContext, newName);
|
||||
newName = newWorkflow.name;
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
} catch (e) {}
|
||||
return newName;
|
||||
},
|
||||
|
||||
|
||||
// Node actions
|
||||
setWorkflowExecutionData(workflowResultData: IExecutionResponse | null): void {
|
||||
this.workflowExecutionData = workflowResultData;
|
||||
@@ -453,7 +479,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
}
|
||||
},
|
||||
|
||||
pinData(payload: { node: INodeUi, data: INodeExecutionData[] }): void {
|
||||
pinData(payload: { node: INodeUi; data: INodeExecutionData[] }): void {
|
||||
if (!this.workflow.pinData) {
|
||||
Vue.set(this.workflow, 'pinData', {});
|
||||
}
|
||||
@@ -462,7 +488,9 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
payload.data = [payload.data];
|
||||
}
|
||||
|
||||
const storedPinData = payload.data.map(item => isJsonKeyObject(item) ? item : { json: item });
|
||||
const storedPinData = payload.data.map((item) =>
|
||||
isJsonKeyObject(item) ? item : { json: item },
|
||||
);
|
||||
|
||||
Vue.set(this.workflow.pinData!, payload.node.name, storedPinData);
|
||||
|
||||
@@ -486,7 +514,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
dataPinningEventBus.$emit('unpin-data', { [payload.node.name]: undefined });
|
||||
},
|
||||
|
||||
addConnection(data: { connection: IConnection[], setStateDirty: boolean }): void {
|
||||
addConnection(data: { connection: IConnection[]; setStateDirty: boolean }): void {
|
||||
if (data.connection.length !== 2) {
|
||||
// All connections need two entries
|
||||
// TODO: Check if there is an error or whatever that is supposed to be returned
|
||||
@@ -507,8 +535,15 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
if (!this.workflow.connections[sourceData.node].hasOwnProperty(sourceData.type)) {
|
||||
Vue.set(this.workflow.connections[sourceData.node], sourceData.type, []);
|
||||
}
|
||||
if (this.workflow.connections[sourceData.node][sourceData.type].length < (sourceData.index + 1)) {
|
||||
for (let i = this.workflow.connections[sourceData.node][sourceData.type].length; i <= sourceData.index; i++) {
|
||||
if (
|
||||
this.workflow.connections[sourceData.node][sourceData.type].length <
|
||||
sourceData.index + 1
|
||||
) {
|
||||
for (
|
||||
let i = this.workflow.connections[sourceData.node][sourceData.type].length;
|
||||
i <= sourceData.index;
|
||||
i++
|
||||
) {
|
||||
this.workflow.connections[sourceData.node][sourceData.type].push([]);
|
||||
}
|
||||
}
|
||||
@@ -517,10 +552,14 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
const checkProperties = ['index', 'node', 'type'];
|
||||
let propertyName: string;
|
||||
let connectionExists = false;
|
||||
connectionLoop:
|
||||
for (const existingConnection of this.workflow.connections[sourceData.node][sourceData.type][sourceData.index]) {
|
||||
connectionLoop: for (const existingConnection of this.workflow.connections[sourceData.node][
|
||||
sourceData.type
|
||||
][sourceData.index]) {
|
||||
for (propertyName of checkProperties) {
|
||||
if ((existingConnection as any)[propertyName] !== (destinationData as any)[propertyName]) { // tslint:disable-line:no-any
|
||||
if (
|
||||
// tslint:disable-next-line:no-any
|
||||
(existingConnection as any)[propertyName] !== (destinationData as any)[propertyName]
|
||||
) {
|
||||
continue connectionLoop;
|
||||
}
|
||||
}
|
||||
@@ -529,7 +568,9 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
}
|
||||
// Add the new connection if it does not exist already
|
||||
if (connectionExists === false) {
|
||||
this.workflow.connections[sourceData.node][sourceData.type][sourceData.index].push(destinationData);
|
||||
this.workflow.connections[sourceData.node][sourceData.type][sourceData.index].push(
|
||||
destinationData,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -543,15 +584,23 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
if (!this.workflow.connections[sourceData.node].hasOwnProperty(sourceData.type)) {
|
||||
return;
|
||||
}
|
||||
if (this.workflow.connections[sourceData.node][sourceData.type].length < (sourceData.index + 1)) {
|
||||
if (
|
||||
this.workflow.connections[sourceData.node][sourceData.type].length <
|
||||
sourceData.index + 1
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const uiStore = useUIStore();
|
||||
uiStore.stateIsDirty = true;
|
||||
|
||||
const connections = this.workflow.connections[sourceData.node][sourceData.type][sourceData.index];
|
||||
const connections =
|
||||
this.workflow.connections[sourceData.node][sourceData.type][sourceData.index];
|
||||
for (const index in connections) {
|
||||
if (connections[index].node === destinationData.node && connections[index].type === destinationData.type && connections[index].index === destinationData.index) {
|
||||
if (
|
||||
connections[index].node === destinationData.node &&
|
||||
connections[index].type === destinationData.type &&
|
||||
connections[index].index === destinationData.index
|
||||
) {
|
||||
// Found the connection to remove
|
||||
connections.splice(parseInt(index, 10), 1);
|
||||
}
|
||||
@@ -576,33 +625,50 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
|
||||
// Remove all destination connections
|
||||
const indexesToRemove = [];
|
||||
let sourceNode: string, type: string, sourceIndex: string, connectionIndex: string, connectionData: IConnection;
|
||||
let sourceNode: string,
|
||||
type: string,
|
||||
sourceIndex: string,
|
||||
connectionIndex: string,
|
||||
connectionData: IConnection;
|
||||
for (sourceNode of Object.keys(this.workflow.connections)) {
|
||||
for (type of Object.keys(this.workflow.connections[sourceNode])) {
|
||||
for (sourceIndex of Object.keys(this.workflow.connections[sourceNode][type])) {
|
||||
indexesToRemove.length = 0;
|
||||
for (connectionIndex of Object.keys(this.workflow.connections[sourceNode][type][parseInt(sourceIndex, 10)])) {
|
||||
connectionData = this.workflow.connections[sourceNode][type][parseInt(sourceIndex, 10)][parseInt(connectionIndex, 10)];
|
||||
for (connectionIndex of Object.keys(
|
||||
this.workflow.connections[sourceNode][type][parseInt(sourceIndex, 10)],
|
||||
)) {
|
||||
connectionData =
|
||||
this.workflow.connections[sourceNode][type][parseInt(sourceIndex, 10)][
|
||||
parseInt(connectionIndex, 10)
|
||||
];
|
||||
if (connectionData.node === node.name) {
|
||||
indexesToRemove.push(connectionIndex);
|
||||
}
|
||||
}
|
||||
|
||||
indexesToRemove.forEach((index) => {
|
||||
this.workflow.connections[sourceNode][type][parseInt(sourceIndex, 10)].splice(parseInt(index, 10), 1);
|
||||
this.workflow.connections[sourceNode][type][parseInt(sourceIndex, 10)].splice(
|
||||
parseInt(index, 10),
|
||||
1,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
renameNodeSelectedAndExecution(nameData: { old: string, new: string }): void {
|
||||
renameNodeSelectedAndExecution(nameData: { old: string; new: string }): void {
|
||||
const uiStore = useUIStore();
|
||||
uiStore.stateIsDirty = true;
|
||||
// If node has any WorkflowResultData rename also that one that the data
|
||||
// does still get displayed also after node got renamed
|
||||
if (this.workflowExecutionData !== null && this.workflowExecutionData.data && this.workflowExecutionData.data.resultData.runData.hasOwnProperty(nameData.old)) {
|
||||
this.workflowExecutionData.data.resultData.runData[nameData.new] = this.workflowExecutionData.data.resultData.runData[nameData.old];
|
||||
if (
|
||||
this.workflowExecutionData !== null &&
|
||||
this.workflowExecutionData.data &&
|
||||
this.workflowExecutionData.data.resultData.runData.hasOwnProperty(nameData.old)
|
||||
) {
|
||||
this.workflowExecutionData.data.resultData.runData[nameData.new] =
|
||||
this.workflowExecutionData.data.resultData.runData[nameData.old];
|
||||
delete this.workflowExecutionData.data.resultData.runData[nameData.old];
|
||||
}
|
||||
|
||||
@@ -630,7 +696,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
},
|
||||
|
||||
setNodeIssue(nodeIssueData: INodeIssueData): boolean {
|
||||
const node = this.workflow.nodes.find(node => {
|
||||
const node = this.workflow.nodes.find((node) => {
|
||||
return node.name === nodeIssueData.node;
|
||||
});
|
||||
if (!node) {
|
||||
@@ -681,7 +747,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
}
|
||||
},
|
||||
|
||||
removeAllNodes(data: { setStateDirty: boolean, removePinData: boolean }): void {
|
||||
removeAllNodes(data: { setStateDirty: boolean; removePinData: boolean }): void {
|
||||
if (data.setStateDirty === true) {
|
||||
const uiStore = useUIStore();
|
||||
uiStore.stateIsDirty = true;
|
||||
@@ -697,7 +763,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
|
||||
updateNodeProperties(updateInformation: INodeUpdatePropertiesInformation): void {
|
||||
// Find the node that should be updated
|
||||
const node = this.workflow.nodes.find(node => {
|
||||
const node = this.workflow.nodes.find((node) => {
|
||||
return node.name === updateInformation.name;
|
||||
});
|
||||
|
||||
@@ -712,12 +778,14 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
|
||||
setNodeValue(updateInformation: IUpdateInformation): void {
|
||||
// Find the node that should be updated
|
||||
const node = this.workflow.nodes.find(node => {
|
||||
const node = this.workflow.nodes.find((node) => {
|
||||
return node.name === updateInformation.name;
|
||||
});
|
||||
|
||||
if (node === undefined || node === null || !updateInformation.key) {
|
||||
throw new Error(`Node with the name "${updateInformation.name}" could not be found to set parameter.`);
|
||||
throw new Error(
|
||||
`Node with the name "${updateInformation.name}" could not be found to set parameter.`,
|
||||
);
|
||||
}
|
||||
|
||||
const uiStore = useUIStore();
|
||||
@@ -727,19 +795,22 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
|
||||
setNodeParameters(updateInformation: IUpdateInformation, append?: boolean): void {
|
||||
// Find the node that should be updated
|
||||
const node = this.workflow.nodes.find(node => {
|
||||
const node = this.workflow.nodes.find((node) => {
|
||||
return node.name === updateInformation.name;
|
||||
});
|
||||
|
||||
if (node === undefined || node === null) {
|
||||
throw new Error(`Node with the name "${updateInformation.name}" could not be found to set parameter.`);
|
||||
throw new Error(
|
||||
`Node with the name "${updateInformation.name}" could not be found to set parameter.`,
|
||||
);
|
||||
}
|
||||
|
||||
const uiStore = useUIStore();
|
||||
uiStore.stateIsDirty = true;
|
||||
const newParameters = !!append && isObjectLiteral(updateInformation.value)
|
||||
? {...node.parameters, ...updateInformation.value }
|
||||
: updateInformation.value;
|
||||
const newParameters =
|
||||
!!append && isObjectLiteral(updateInformation.value)
|
||||
? { ...node.parameters, ...updateInformation.value }
|
||||
: updateInformation.value;
|
||||
|
||||
Vue.set(node, 'parameters', newParameters);
|
||||
|
||||
@@ -750,9 +821,11 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
},
|
||||
|
||||
setLastNodeParameters(updateInformation: IUpdateInformation) {
|
||||
const latestNode = this.workflow.nodes.findLast((node) => node.type === updateInformation.key) as INodeUi;
|
||||
const latestNode = this.workflow.nodes.findLast(
|
||||
(node) => node.type === updateInformation.key,
|
||||
) as INodeUi;
|
||||
|
||||
if(latestNode) this.setNodeParameters({...updateInformation, name: latestNode.name}, true);
|
||||
if (latestNode) this.setNodeParameters({ ...updateInformation, name: latestNode.name }, true);
|
||||
},
|
||||
|
||||
addNodeExecutionData(pushData: IPushDataNodeExecuteAfter): void {
|
||||
@@ -774,7 +847,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
|
||||
pinDataByNodeName(nodeName: string): INodeExecutionData[] | undefined {
|
||||
if (!this.workflow.pinData || !this.workflow.pinData[nodeName]) return undefined;
|
||||
return this.workflow.pinData[nodeName].map(item => item.json) as INodeExecutionData[];
|
||||
return this.workflow.pinData[nodeName].map((item) => item.json) as INodeExecutionData[];
|
||||
},
|
||||
|
||||
activeNode(): INodeUi | null {
|
||||
@@ -785,9 +858,9 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
|
||||
// Executions actions
|
||||
|
||||
addActiveExecution(newActiveExecution: IExecutionsCurrentSummaryExtended) : void {
|
||||
addActiveExecution(newActiveExecution: IExecutionsCurrentSummaryExtended): void {
|
||||
// Check if the execution exists already
|
||||
const activeExecution = this.activeExecutions.find(execution => {
|
||||
const activeExecution = this.activeExecutions.find((execution) => {
|
||||
return execution.id === newActiveExecution.id;
|
||||
});
|
||||
|
||||
@@ -800,9 +873,9 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
}
|
||||
this.activeExecutions.unshift(newActiveExecution);
|
||||
},
|
||||
finishActiveExecution(finishedActiveExecution: IPushDataExecutionFinished) : void {
|
||||
finishActiveExecution(finishedActiveExecution: IPushDataExecutionFinished): void {
|
||||
// Find the execution to set to finished
|
||||
const activeExecution = this.activeExecutions.find(execution => {
|
||||
const activeExecution = this.activeExecutions.find((execution) => {
|
||||
return execution.id === finishedActiveExecution.executionId;
|
||||
});
|
||||
|
||||
@@ -819,11 +892,14 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
Vue.set(activeExecution, 'stoppedAt', finishedActiveExecution.data.stoppedAt);
|
||||
},
|
||||
|
||||
setActiveExecutions(newActiveExecutions: IExecutionsCurrentSummaryExtended[]) : void {
|
||||
setActiveExecutions(newActiveExecutions: IExecutionsCurrentSummaryExtended[]): void {
|
||||
Vue.set(this, 'activeExecutions', newActiveExecutions);
|
||||
},
|
||||
|
||||
async loadCurrentWorkflowExecutions (filter: { finished: boolean, status: string }): Promise<IExecutionsSummary[]> {
|
||||
async loadCurrentWorkflowExecutions(filter: {
|
||||
finished: boolean;
|
||||
status: string;
|
||||
}): Promise<IExecutionsSummary[]> {
|
||||
let activeExecutions = [];
|
||||
let finishedExecutions = [];
|
||||
const requestFilter: IDataObject = { workflowId: this.workflowId };
|
||||
@@ -833,24 +909,27 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
||||
}
|
||||
try {
|
||||
const rootStore = useRootStore();
|
||||
if (filter.status === ''|| !filter.finished) {
|
||||
if (filter.status === '' || !filter.finished) {
|
||||
activeExecutions = await getCurrentExecutions(rootStore.getRestApiContext, requestFilter);
|
||||
}
|
||||
if (filter.status === '' || filter.finished) {
|
||||
if (filter.status === 'waiting') {
|
||||
requestFilter.waitTill = true;
|
||||
} else if (filter.status !== '') {
|
||||
} else if (filter.status !== '') {
|
||||
requestFilter.finished = filter.status === 'success';
|
||||
}
|
||||
finishedExecutions = await getFinishedExecutions(rootStore.getRestApiContext, requestFilter);
|
||||
finishedExecutions = await getFinishedExecutions(
|
||||
rootStore.getRestApiContext,
|
||||
requestFilter,
|
||||
);
|
||||
}
|
||||
// context.commit('setTotalFinishedExecutionsCount', finishedExecutions.count);
|
||||
return [...activeExecutions, ...finishedExecutions.results || []];
|
||||
return [...activeExecutions, ...(finishedExecutions.results || [])];
|
||||
} catch (error) {
|
||||
throw(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
deleteExecution (execution: IExecutionsSummary): void {
|
||||
deleteExecution(execution: IExecutionsSummary): void {
|
||||
this.currentWorkflowExecutions.splice(this.currentWorkflowExecutions.indexOf(execution), 1);
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user