mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
feat: Refresh workflow name in workflows selector when updated (#14705)
This commit is contained in:
@@ -30,10 +30,8 @@ export async function getNewWorkflow(context: IRestApiContext, data?: IDataObjec
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getWorkflow(context: IRestApiContext, id: string, filter?: object) {
|
export async function getWorkflow(context: IRestApiContext, id: string) {
|
||||||
const sendData = filter ? { filter } : undefined;
|
return await makeRestApiRequest<IWorkflowDb>(context, 'GET', `/workflows/${id}`);
|
||||||
|
|
||||||
return await makeRestApiRequest<IWorkflowDb>(context, 'GET', `/workflows/${id}`, sendData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getWorkflows(context: IRestApiContext, filter?: object, options?: object) {
|
export async function getWorkflows(context: IRestApiContext, filter?: object, options?: object) {
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
|
import WorkflowSelectorParameterInput, {
|
||||||
|
type Props,
|
||||||
|
} from '@/components/WorkflowSelectorParameterInput/WorkflowSelectorParameterInput.vue';
|
||||||
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
|
import { cleanupAppModals, createAppModals, mockedStore } from '@/__tests__/utils';
|
||||||
|
import { useProjectsStore } from '@/stores/projects.store';
|
||||||
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
|
|
||||||
|
const { onDocumentVisible } = vi.hoisted(() => ({
|
||||||
|
onDocumentVisible: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const flushPromises = async () => await new Promise(setImmediate);
|
||||||
|
|
||||||
|
vi.mock('@/composables/useDocumentVisibility', () => ({
|
||||||
|
useDocumentVisibility: () => ({ onDocumentVisible }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const renderComponent = createComponentRenderer(WorkflowSelectorParameterInput, {
|
||||||
|
global: {
|
||||||
|
stubs: {
|
||||||
|
ResourceLocatorDropdown: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pinia: createTestingPinia({}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectsStore = mockedStore(useProjectsStore);
|
||||||
|
projectsStore.isTeamProjectFeatureEnabled = false;
|
||||||
|
|
||||||
|
const workflowsStore = mockedStore(useWorkflowsStore);
|
||||||
|
|
||||||
|
describe('WorkflowSelectorParameterInput', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
createAppModals();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanupAppModals();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update cached workflow when page is visible', async () => {
|
||||||
|
const props: Props = {
|
||||||
|
modelValue: {
|
||||||
|
__rl: true,
|
||||||
|
value: 'workflow-id',
|
||||||
|
mode: 'list',
|
||||||
|
},
|
||||||
|
path: '',
|
||||||
|
parameter: {
|
||||||
|
displayName: 'display-name',
|
||||||
|
type: 'workflowSelector',
|
||||||
|
name: 'name',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { emitted } = renderComponent({ props });
|
||||||
|
await flushPromises();
|
||||||
|
expect(emitted()['update:modelValue']?.[0]).toEqual([props.modelValue]);
|
||||||
|
expect(workflowsStore.fetchWorkflow).toHaveBeenCalledWith(props.modelValue.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update cached workflow when page is visible', async () => {
|
||||||
|
const props: Props = {
|
||||||
|
modelValue: {
|
||||||
|
__rl: true,
|
||||||
|
value: 'workflow-id',
|
||||||
|
mode: 'list',
|
||||||
|
},
|
||||||
|
path: '',
|
||||||
|
parameter: {
|
||||||
|
displayName: 'display-name',
|
||||||
|
type: 'workflowSelector',
|
||||||
|
name: 'name',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { emitted } = renderComponent({ props });
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
// on mount
|
||||||
|
expect(emitted()['update:modelValue']?.[0]).toEqual([props.modelValue]);
|
||||||
|
expect(workflowsStore.fetchWorkflow).toHaveBeenCalledWith(props.modelValue.value);
|
||||||
|
workflowsStore.fetchWorkflow.mockReset();
|
||||||
|
|
||||||
|
expect(onDocumentVisible).toHaveBeenCalled();
|
||||||
|
const onDocumentVisibleCallback = onDocumentVisible.mock.lastCall?.[0];
|
||||||
|
await onDocumentVisibleCallback();
|
||||||
|
|
||||||
|
expect(emitted()['update:modelValue']?.[1]).toEqual([props.modelValue]);
|
||||||
|
expect(workflowsStore.fetchWorkflow).toHaveBeenCalledWith(props.modelValue.value);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -23,8 +23,9 @@ import { useTelemetry } from '@/composables/useTelemetry';
|
|||||||
import { VIEWS } from '@/constants';
|
import { VIEWS } from '@/constants';
|
||||||
import { SAMPLE_SUBWORKFLOW_WORKFLOW } from '@/constants.workflows';
|
import { SAMPLE_SUBWORKFLOW_WORKFLOW } from '@/constants.workflows';
|
||||||
import type { IWorkflowDataCreate } from '@/Interface';
|
import type { IWorkflowDataCreate } from '@/Interface';
|
||||||
|
import { useDocumentVisibility } from '@/composables/useDocumentVisibility';
|
||||||
|
|
||||||
interface Props {
|
export interface Props {
|
||||||
modelValue: INodeParameterResourceLocator;
|
modelValue: INodeParameterResourceLocator;
|
||||||
eventBus?: EventBus;
|
eventBus?: EventBus;
|
||||||
inputSize?: 'small' | 'mini' | 'medium' | 'large' | 'xlarge';
|
inputSize?: 'small' | 'mini' | 'medium' | 'large' | 'xlarge';
|
||||||
@@ -82,6 +83,8 @@ const { hideDropdown, isDropdownVisible, showDropdown } = useWorkflowResourceLoc
|
|||||||
inputRef,
|
inputRef,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { onDocumentVisible } = useDocumentVisibility();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
hasMoreWorkflowsToLoad,
|
hasMoreWorkflowsToLoad,
|
||||||
isLoadingResources,
|
isLoadingResources,
|
||||||
@@ -146,14 +149,18 @@ function setWidth() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onInputChange(value: NodeParameterValue): void {
|
function onInputChange(workflowId: NodeParameterValue): void {
|
||||||
if (typeof value !== 'string') return;
|
if (typeof workflowId !== 'string') return;
|
||||||
|
|
||||||
const params: INodeParameterResourceLocator = { __rl: true, value, mode: selectedMode.value };
|
const params: INodeParameterResourceLocator = {
|
||||||
|
__rl: true,
|
||||||
|
value: workflowId,
|
||||||
|
mode: selectedMode.value,
|
||||||
|
};
|
||||||
if (isListMode.value) {
|
if (isListMode.value) {
|
||||||
const resource = workflowsStore.getWorkflowById(value);
|
const resource = workflowsStore.getWorkflowById(workflowId);
|
||||||
if (resource?.name) {
|
if (resource?.name) {
|
||||||
params.cachedResultName = getWorkflowName(value);
|
params.cachedResultName = getWorkflowName(workflowId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emit('update:modelValue', params);
|
emit('update:modelValue', params);
|
||||||
@@ -191,7 +198,27 @@ function openWorkflow() {
|
|||||||
window.open(getWorkflowUrl(props.modelValue.value?.toString() ?? ''), '_blank');
|
window.open(getWorkflowUrl(props.modelValue.value?.toString() ?? ''), '_blank');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function refreshCachedWorkflow() {
|
||||||
|
if (!props.modelValue || props.modelValue.mode !== 'list' || !props.modelValue.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowId = props.modelValue.value;
|
||||||
|
if (workflowId === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await workflowsStore.fetchWorkflow(`${workflowId}`);
|
||||||
|
onInputChange(workflowId);
|
||||||
|
} catch (e) {
|
||||||
|
// keep old cached value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDocumentVisible(refreshCachedWorkflow);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
void refreshCachedWorkflow();
|
||||||
window.addEventListener('resize', setWidth);
|
window.addEventListener('resize', setWidth);
|
||||||
setWidth();
|
setWidth();
|
||||||
void setWorkflowsResources();
|
void setWorkflowsResources();
|
||||||
|
|||||||
Reference in New Issue
Block a user