mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
fix(editor): Actually enforce the version and don't break for old values in local storage (#13025)
Co-authored-by: Csaba Tuncsik <csaba@n8n.io>
This commit is contained in:
parent
60a0f38f6a
commit
884a7e23f8
|
@ -142,6 +142,13 @@ describe('settings.store', () => {
|
||||||
userVersion: 1,
|
userVersion: 1,
|
||||||
result: 2,
|
result: 2,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'handle values that used to be allowed in local storage',
|
||||||
|
default: 1 as const,
|
||||||
|
enforce: false,
|
||||||
|
userVersion: 0,
|
||||||
|
result: 1,
|
||||||
|
},
|
||||||
])('%name', async ({ default: defaultVersion, userVersion, enforce, result }) => {
|
])('%name', async ({ default: defaultVersion, userVersion, enforce, result }) => {
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,12 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||||
? defaultVersion
|
? defaultVersion
|
||||||
: userVersion;
|
: userVersion;
|
||||||
|
|
||||||
|
// For backwards compatibility, e.g. if the user has 0 in their local
|
||||||
|
// storage, which used to be allowed, but not anymore.
|
||||||
|
if (![1, 2].includes(version)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return version;
|
return version;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,15 @@ import type { IPinData, ExecutionSummary, IConnection, INodeExecutionData } from
|
||||||
import { stringSizeInBytes } from '@/utils/typesUtils';
|
import { stringSizeInBytes } from '@/utils/typesUtils';
|
||||||
import { dataPinningEventBus } from '@/event-bus';
|
import { dataPinningEventBus } from '@/event-bus';
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
import type { PushPayload } from '@n8n/api-types';
|
import type { PushPayload, FrontendSettings } from '@n8n/api-types';
|
||||||
import { flushPromises } from '@vue/test-utils';
|
import { flushPromises } from '@vue/test-utils';
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
import { mock } from 'vitest-mock-extended';
|
import { mock } from 'vitest-mock-extended';
|
||||||
|
import { mockedStore, type MockedStore } from '@/__tests__/utils';
|
||||||
|
import * as apiUtils from '@/utils/apiUtils';
|
||||||
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
|
import { useLocalStorage } from '@vueuse/core';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
vi.mock('@/stores/ndv.store', () => ({
|
vi.mock('@/stores/ndv.store', () => ({
|
||||||
useNDVStore: vi.fn(() => ({
|
useNDVStore: vi.fn(() => ({
|
||||||
|
@ -45,14 +50,24 @@ vi.mock('@/composables/useTelemetry', () => ({
|
||||||
useTelemetry: () => ({ track }),
|
useTelemetry: () => ({ track }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
vi.mock('@vueuse/core', async (importOriginal) => {
|
||||||
|
const actual = await importOriginal<{}>();
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
useLocalStorage: vi.fn().mockReturnValue({ value: undefined }),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
describe('useWorkflowsStore', () => {
|
describe('useWorkflowsStore', () => {
|
||||||
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
|
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
|
||||||
let uiStore: ReturnType<typeof useUIStore>;
|
let uiStore: ReturnType<typeof useUIStore>;
|
||||||
|
let settingsStore: MockedStore<typeof useSettingsStore>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setActivePinia(createPinia());
|
setActivePinia(createPinia());
|
||||||
workflowsStore = useWorkflowsStore();
|
workflowsStore = useWorkflowsStore();
|
||||||
uiStore = useUIStore();
|
uiStore = useUIStore();
|
||||||
|
settingsStore = mockedStore(useSettingsStore);
|
||||||
track.mockReset();
|
track.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -662,6 +677,50 @@ describe('useWorkflowsStore', () => {
|
||||||
expect(workflowsStore.nodeMetadata[nodeName].parametersLastUpdatedAt).toBe(undefined);
|
expect(workflowsStore.nodeMetadata[nodeName].parametersLastUpdatedAt).toBe(undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
// enforce true cases - the version is always the defaultVersion
|
||||||
|
[-1, 1, true, 1], // enforce true, use default (1)
|
||||||
|
[0, 1, true, 1], // enforce true, use default (1)
|
||||||
|
[1, 1, true, 1], // enforce true, use default (1)
|
||||||
|
[2, 1, true, 1], // enforce true, use default (1)
|
||||||
|
[-1, 2, true, 2], // enforce true, use default (2)
|
||||||
|
[0, 2, true, 2], // enforce true, use default (2)
|
||||||
|
[1, 2, true, 2], // enforce true, use default (2)
|
||||||
|
[2, 2, true, 2], // enforce true, use default (2)
|
||||||
|
|
||||||
|
// enforce false cases - check userVersion behavior
|
||||||
|
[-1, 1, false, 1], // userVersion -1, use default (1)
|
||||||
|
[0, 1, false, 1], // userVersion 0, invalid, use default (1)
|
||||||
|
[1, 1, false, 1], // userVersion 1, valid, use userVersion (1)
|
||||||
|
[2, 1, false, 2], // userVersion 2, valid, use userVersion (2)
|
||||||
|
[-1, 2, false, 2], // userVersion -1, use default (2)
|
||||||
|
[0, 2, false, 1], // userVersion 0, invalid, use default (2)
|
||||||
|
[1, 2, false, 1], // userVersion 1, valid, use userVersion (1)
|
||||||
|
[2, 2, false, 2], // userVersion 2, valid, use userVersion (2)
|
||||||
|
] as Array<[number, 1 | 2, boolean, number]>)(
|
||||||
|
'when { userVersion:%s, defaultVersion:%s, enforced:%s } run workflow should use partial execution version %s',
|
||||||
|
async (userVersion, defaultVersion, enforce, expectedVersion) => {
|
||||||
|
vi.mocked(useLocalStorage).mockReturnValueOnce(ref(userVersion));
|
||||||
|
settingsStore.settings = {
|
||||||
|
partialExecution: { version: defaultVersion, enforce },
|
||||||
|
} as FrontendSettings;
|
||||||
|
|
||||||
|
const workflowData = { id: '1', nodes: [], connections: {} };
|
||||||
|
const makeRestApiRequestSpy = vi
|
||||||
|
.spyOn(apiUtils, 'makeRestApiRequest')
|
||||||
|
.mockImplementation(async () => ({}));
|
||||||
|
|
||||||
|
await workflowsStore.runWorkflow({ workflowData });
|
||||||
|
|
||||||
|
expect(makeRestApiRequestSpy).toHaveBeenCalledWith(
|
||||||
|
{ baseUrl: '/rest', pushRef: expect.any(String) },
|
||||||
|
'POST',
|
||||||
|
`/workflows/1/run?partialExecutionVersion=${expectedVersion}`,
|
||||||
|
{ workflowData },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function getMockEditFieldsNode() {
|
function getMockEditFieldsNode() {
|
||||||
|
|
|
@ -124,8 +124,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
||||||
const nodeHelpers = useNodeHelpers();
|
const nodeHelpers = useNodeHelpers();
|
||||||
const usersStore = useUsersStore();
|
const usersStore = useUsersStore();
|
||||||
|
|
||||||
const version = settingsStore.partialExecutionVersion;
|
const version = computed(() => settingsStore.partialExecutionVersion);
|
||||||
|
|
||||||
const workflow = ref<IWorkflowDb>(createEmptyWorkflow());
|
const workflow = ref<IWorkflowDb>(createEmptyWorkflow());
|
||||||
const usedCredentials = ref<Record<string, IUsedCredential>>({});
|
const usedCredentials = ref<Record<string, IUsedCredential>>({});
|
||||||
|
|
||||||
|
@ -1470,7 +1469,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
||||||
return await makeRestApiRequest(
|
return await makeRestApiRequest(
|
||||||
rootStore.restApiContext,
|
rootStore.restApiContext,
|
||||||
'POST',
|
'POST',
|
||||||
`/workflows/${startRunData.workflowData.id}/run?partialExecutionVersion=${version}`,
|
`/workflows/${startRunData.workflowData.id}/run?partialExecutionVersion=${version.value}`,
|
||||||
startRunData as unknown as IDataObject,
|
startRunData as unknown as IDataObject,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
Loading…
Reference in a new issue