mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(editor): Provide default ExecuteWorkflow node names based on the selected workflow (#18953)
This commit is contained in:
@@ -92,6 +92,7 @@ const {
|
|||||||
searchFilter,
|
searchFilter,
|
||||||
onSearchFilter,
|
onSearchFilter,
|
||||||
getWorkflowName,
|
getWorkflowName,
|
||||||
|
renameDefaultNodeName,
|
||||||
populateNextWorkflowsPage,
|
populateNextWorkflowsPage,
|
||||||
setWorkflowsResources,
|
setWorkflowsResources,
|
||||||
reloadWorkflows,
|
reloadWorkflows,
|
||||||
@@ -174,6 +175,10 @@ function onListItemSelected(value: NodeParameterValue) {
|
|||||||
telemetry.track('User chose sub-workflow', {});
|
telemetry.track('User chose sub-workflow', {});
|
||||||
onInputChange(value);
|
onInputChange(value);
|
||||||
hideDropdown();
|
hideDropdown();
|
||||||
|
// we rename defaults here to allow selecting the same workflow to
|
||||||
|
// update the name, as we don't eagerly update a changed workflow name
|
||||||
|
// but rather only react on changed id elsewhere
|
||||||
|
renameDefaultNodeName(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onInputFocus(): void {
|
function onInputFocus(): void {
|
||||||
@@ -239,6 +244,19 @@ watch(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val, old) => {
|
||||||
|
// We update the name only if the actual ID changed
|
||||||
|
// Because eagerly renaming the node when the target sub-workflow
|
||||||
|
// changed name means the workflow becomes unsaved and changed just by
|
||||||
|
// opening the ExecuteWorkflow node referencing the renamed workflow
|
||||||
|
if (old.value !== val.value) {
|
||||||
|
renameDefaultNodeName(val.value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
onClickOutside(dropdown, () => {
|
onClickOutside(dropdown, () => {
|
||||||
isDropdownVisible.value = false;
|
isDropdownVisible.value = false;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
|
import { useWorkflowResourcesLocator } from './useWorkflowResourcesLocator';
|
||||||
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
|
import { type MockedStore, mockedStore } from '@/__tests__/utils';
|
||||||
|
import type { IWorkflowDb } from '@/Interface';
|
||||||
|
import type { Router } from 'vue-router';
|
||||||
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
|
|
||||||
|
const useCanvasOperations = vi.hoisted(() => vi.fn());
|
||||||
|
|
||||||
|
vi.mock('@/composables/useCanvasOperations', () => ({
|
||||||
|
useCanvasOperations,
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('useWorkflowResourcesLocator', () => {
|
||||||
|
let workflowsStoreMock: MockedStore<typeof useWorkflowsStore>;
|
||||||
|
let ndvStoreMock: MockedStore<typeof useNDVStore>;
|
||||||
|
|
||||||
|
const renameNodeMock = vi.fn();
|
||||||
|
const routerMock = { resolve: vi.fn() } as unknown as Router;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
|
||||||
|
createTestingPinia();
|
||||||
|
workflowsStoreMock = mockedStore(useWorkflowsStore);
|
||||||
|
ndvStoreMock = mockedStore(useNDVStore);
|
||||||
|
|
||||||
|
useCanvasOperations.mockReturnValue({ renameNode: renameNodeMock });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('renameDefaultNodeName', () => {
|
||||||
|
it.each([
|
||||||
|
{
|
||||||
|
activeNodeName: 'Execute Workflow',
|
||||||
|
workflowId: 'workflow-id',
|
||||||
|
mockedWorkflow: { name: 'Test Workflow' },
|
||||||
|
expectedRename: "Call 'Test Workflow'",
|
||||||
|
expectedCalledWith: 'Execute Workflow',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
activeNodeName: 'Call n8n Workflow Tool',
|
||||||
|
workflowId: 'workflow-id',
|
||||||
|
mockedWorkflow: { name: 'Test Workflow' },
|
||||||
|
expectedRename: "Call 'Test Workflow'",
|
||||||
|
expectedCalledWith: 'Call n8n Workflow Tool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
activeNodeName: "Call 'Old Workflow'",
|
||||||
|
workflowId: 'workflow-id',
|
||||||
|
mockedWorkflow: { name: 'New Workflow' },
|
||||||
|
expectedRename: "Call 'New Workflow'",
|
||||||
|
expectedCalledWith: "Call 'Old Workflow'",
|
||||||
|
},
|
||||||
|
])(
|
||||||
|
'should rename the node correctly for activeNodeName: $activeNodeName',
|
||||||
|
({ activeNodeName, workflowId, mockedWorkflow, expectedRename, expectedCalledWith }) => {
|
||||||
|
const { renameDefaultNodeName } = useWorkflowResourcesLocator(routerMock);
|
||||||
|
|
||||||
|
ndvStoreMock.activeNodeName = activeNodeName;
|
||||||
|
workflowsStoreMock.getWorkflowById.mockReturnValue(
|
||||||
|
mockedWorkflow as unknown as IWorkflowDb,
|
||||||
|
);
|
||||||
|
|
||||||
|
renameDefaultNodeName(workflowId);
|
||||||
|
|
||||||
|
expect(workflowsStoreMock.getWorkflowById).toHaveBeenCalledWith(workflowId);
|
||||||
|
expect(renameNodeMock).toHaveBeenCalledWith(expectedCalledWith, expectedRename);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should not rename the node for invalid workflowId', () => {
|
||||||
|
const { renameDefaultNodeName } = useWorkflowResourcesLocator(routerMock);
|
||||||
|
const workflowId = 123;
|
||||||
|
|
||||||
|
renameDefaultNodeName(workflowId);
|
||||||
|
|
||||||
|
expect(renameNodeMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not rename the node for workflowId: workflow-id with null mockedWorkflow', () => {
|
||||||
|
const { renameDefaultNodeName } = useWorkflowResourcesLocator(routerMock);
|
||||||
|
const workflowId = 'workflow-id';
|
||||||
|
const activeNodeName = 'Execute Workflow';
|
||||||
|
|
||||||
|
ndvStoreMock.activeNodeName = activeNodeName;
|
||||||
|
workflowsStoreMock.getWorkflowById.mockReturnValue(null as unknown as IWorkflowDb);
|
||||||
|
|
||||||
|
renameDefaultNodeName(workflowId);
|
||||||
|
|
||||||
|
expect(workflowsStoreMock.getWorkflowById).toHaveBeenCalledWith(workflowId);
|
||||||
|
expect(renameNodeMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not rename the node for workflowId: workflow-id with activeNodeName: Some Other Node', () => {
|
||||||
|
const { renameDefaultNodeName } = useWorkflowResourcesLocator(routerMock);
|
||||||
|
const workflowId = 'workflow-id';
|
||||||
|
const activeNodeName = 'Some Other Node';
|
||||||
|
const mockedWorkflow = { name: 'Test Workflow' };
|
||||||
|
|
||||||
|
ndvStoreMock.activeNodeName = activeNodeName;
|
||||||
|
workflowsStoreMock.getWorkflowById.mockReturnValue(mockedWorkflow as unknown as IWorkflowDb);
|
||||||
|
|
||||||
|
renameDefaultNodeName(workflowId);
|
||||||
|
|
||||||
|
expect(workflowsStoreMock.getWorkflowById).not.toHaveBeenCalled();
|
||||||
|
expect(renameNodeMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -5,9 +5,15 @@ import type { Router } from 'vue-router';
|
|||||||
import { VIEWS } from '@/constants';
|
import { VIEWS } from '@/constants';
|
||||||
|
|
||||||
import type { IWorkflowDb } from '@/Interface';
|
import type { IWorkflowDb } from '@/Interface';
|
||||||
|
import type { NodeParameterValue } from 'n8n-workflow';
|
||||||
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
|
import { useCanvasOperations } from '@/composables/useCanvasOperations';
|
||||||
|
|
||||||
export function useWorkflowResourcesLocator(router: Router) {
|
export function useWorkflowResourcesLocator(router: Router) {
|
||||||
const workflowsStore = useWorkflowsStore();
|
const workflowsStore = useWorkflowsStore();
|
||||||
|
const ndvStore = useNDVStore();
|
||||||
|
const { renameNode } = useCanvasOperations();
|
||||||
|
|
||||||
const workflowsResources = ref<Array<{ name: string; value: string; url: string }>>([]);
|
const workflowsResources = ref<Array<{ name: string; value: string; url: string }>>([]);
|
||||||
const isLoadingResources = ref(true);
|
const isLoadingResources = ref(true);
|
||||||
const searchFilter = ref('');
|
const searchFilter = ref('');
|
||||||
@@ -77,10 +83,34 @@ export function useWorkflowResourcesLocator(router: Router) {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWorkflowBaseName(id: string): string | null {
|
||||||
|
const workflow = workflowsStore.getWorkflowById(id);
|
||||||
|
if (workflow) {
|
||||||
|
return workflow.name;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function onSearchFilter(filter: string) {
|
function onSearchFilter(filter: string) {
|
||||||
searchFilter.value = filter;
|
searchFilter.value = filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renameDefaultNodeName(workflowId: NodeParameterValue) {
|
||||||
|
if (typeof workflowId !== 'string') return;
|
||||||
|
|
||||||
|
const nodeName = ndvStore.activeNodeName;
|
||||||
|
if (
|
||||||
|
nodeName === 'Execute Workflow' ||
|
||||||
|
nodeName === 'Call n8n Workflow Tool' ||
|
||||||
|
(nodeName?.startsWith("Call '") && nodeName?.endsWith("'"))
|
||||||
|
) {
|
||||||
|
const baseName = getWorkflowBaseName(workflowId);
|
||||||
|
if (baseName !== null) {
|
||||||
|
void renameNode(nodeName, `Call '${baseName}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
workflowsResources,
|
workflowsResources,
|
||||||
isLoadingResources,
|
isLoadingResources,
|
||||||
@@ -91,6 +121,7 @@ export function useWorkflowResourcesLocator(router: Router) {
|
|||||||
getWorkflowUrl,
|
getWorkflowUrl,
|
||||||
onSearchFilter,
|
onSearchFilter,
|
||||||
getWorkflowName,
|
getWorkflowName,
|
||||||
|
renameDefaultNodeName,
|
||||||
populateNextWorkflowsPage,
|
populateNextWorkflowsPage,
|
||||||
setWorkflowsResources,
|
setWorkflowsResources,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user