mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix: Sync partial execution version of FE and BE, also allow enforcing a specific version (#12840)
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
This commit is contained in:
parent
a65a9e631b
commit
a15504329b
|
@ -44,6 +44,7 @@ export { CredentialsGetOneRequestQuery } from './credentials/credentials-get-one
|
|||
export { CredentialsGetManyRequestQuery } from './credentials/credentials-get-many-request.dto';
|
||||
|
||||
export { ImportWorkflowFromUrlDto } from './workflows/import-workflow-from-url.dto';
|
||||
export { ManualRunQueryDto } from './workflows/manual-run-query.dto';
|
||||
|
||||
export { CreateOrUpdateTagRequestDto } from './tag/create-or-update-tag-request.dto';
|
||||
export { RetrieveTagQueryDto } from './tag/retrieve-tag-query.dto';
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import { ManualRunQueryDto } from '../manual-run-query.dto';
|
||||
|
||||
describe('ManualRunQueryDto', () => {
|
||||
describe('Valid requests', () => {
|
||||
test.each([
|
||||
{ name: 'version number 1', partialExecutionVersion: '1' },
|
||||
{ name: 'version number 2', partialExecutionVersion: '2' },
|
||||
{ name: 'missing version' },
|
||||
])('should validate $name', ({ partialExecutionVersion }) => {
|
||||
const result = ManualRunQueryDto.safeParse({ partialExecutionVersion });
|
||||
|
||||
if (!result.success) {
|
||||
return fail('expected validation to succeed');
|
||||
}
|
||||
expect(result.success).toBe(true);
|
||||
expect(typeof result.data.partialExecutionVersion).toBe('number');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invalid requests', () => {
|
||||
test.each([
|
||||
{
|
||||
name: 'invalid version 0',
|
||||
partialExecutionVersion: '0',
|
||||
expectedErrorPath: ['partialExecutionVersion'],
|
||||
},
|
||||
{
|
||||
name: 'invalid type (boolean)',
|
||||
partialExecutionVersion: true,
|
||||
expectedErrorPath: ['partialExecutionVersion'],
|
||||
},
|
||||
{
|
||||
name: 'invalid type (number)',
|
||||
partialExecutionVersion: 1,
|
||||
expectedErrorPath: ['partialExecutionVersion'],
|
||||
},
|
||||
])('should fail validation for $name', ({ partialExecutionVersion, expectedErrorPath }) => {
|
||||
const result = ManualRunQueryDto.safeParse({ partialExecutionVersion });
|
||||
|
||||
if (result.success) {
|
||||
return fail('expected validation to fail');
|
||||
}
|
||||
|
||||
expect(result.error.issues[0].path).toEqual(expectedErrorPath);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
import { z } from 'zod';
|
||||
import { Z } from 'zod-class';
|
||||
|
||||
export class ManualRunQueryDto extends Z.class({
|
||||
partialExecutionVersion: z
|
||||
.enum(['1', '2'])
|
||||
.default('1')
|
||||
.transform((version) => Number.parseInt(version) as 1 | 2),
|
||||
}) {}
|
|
@ -178,4 +178,8 @@ export interface FrontendSettings {
|
|||
};
|
||||
betaFeatures: FrontendBetaFeatures[];
|
||||
easyAIWorkflowOnboarded: boolean;
|
||||
partialExecution: {
|
||||
version: 1 | 2;
|
||||
enforce: boolean;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { Config, Env } from '../decorators';
|
||||
|
||||
@Config
|
||||
export class PartialExecutionsConfig {
|
||||
/** Partial execution logic version to use by default. */
|
||||
@Env('N8N_PARTIAL_EXECUTION_VERSION_DEFAULT')
|
||||
version: 1 | 2 = 1;
|
||||
|
||||
/** Set this to true to enforce using the default version. Users cannot use the other version then by setting a local storage key. */
|
||||
@Env('N8N_PARTIAL_EXECUTION_ENFORCE_VERSION')
|
||||
enforce: boolean = false;
|
||||
}
|
|
@ -14,6 +14,7 @@ import { LicenseConfig } from './configs/license.config';
|
|||
import { LoggingConfig } from './configs/logging.config';
|
||||
import { MultiMainSetupConfig } from './configs/multi-main-setup.config';
|
||||
import { NodesConfig } from './configs/nodes.config';
|
||||
import { PartialExecutionsConfig } from './configs/partial-executions.config';
|
||||
import { PublicApiConfig } from './configs/public-api.config';
|
||||
import { TaskRunnersConfig } from './configs/runners.config';
|
||||
import { ScalingModeConfig } from './configs/scaling-mode.config';
|
||||
|
@ -134,4 +135,7 @@ export class GlobalConfig {
|
|||
|
||||
@Nested
|
||||
tags: TagsConfig;
|
||||
|
||||
@Nested
|
||||
partialExecutions: PartialExecutionsConfig;
|
||||
}
|
||||
|
|
|
@ -302,6 +302,10 @@ describe('GlobalConfig', () => {
|
|||
tags: {
|
||||
disabled: false,
|
||||
},
|
||||
partialExecutions: {
|
||||
version: 1,
|
||||
enforce: false,
|
||||
},
|
||||
};
|
||||
|
||||
it('should use all default values when no env variables are defined', () => {
|
||||
|
|
|
@ -370,13 +370,4 @@ export const schema = {
|
|||
env: 'N8N_PROXY_HOPS',
|
||||
doc: 'Number of reverse-proxies n8n is running behind',
|
||||
},
|
||||
|
||||
featureFlags: {
|
||||
partialExecutionVersionDefault: {
|
||||
format: String,
|
||||
default: '0',
|
||||
env: 'PARTIAL_EXECUTION_VERSION_DEFAULT',
|
||||
doc: 'Set this to 1 to enable the new partial execution logic by default.',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -141,7 +141,7 @@ export class TestRunnerService {
|
|||
pinData,
|
||||
workflowData: { ...workflow, pinData },
|
||||
userId: metadata.userId,
|
||||
partialExecutionVersion: '1',
|
||||
partialExecutionVersion: 2,
|
||||
};
|
||||
|
||||
// Trigger the workflow under test with mocked data
|
||||
|
|
|
@ -105,7 +105,7 @@ export class ManualExecutionService {
|
|||
// Execute only the nodes between start and destination nodes
|
||||
const workflowExecute = new WorkflowExecute(additionalData, data.executionMode);
|
||||
|
||||
if (data.partialExecutionVersion === '1') {
|
||||
if (data.partialExecutionVersion === 2) {
|
||||
return workflowExecute.runPartialWorkflow2(
|
||||
workflow,
|
||||
data.runData,
|
||||
|
|
|
@ -234,6 +234,7 @@ export class FrontendService {
|
|||
},
|
||||
betaFeatures: this.frontendConfig.betaFeatures,
|
||||
easyAIWorkflowOnboarded: false,
|
||||
partialExecution: this.globalConfig.partialExecutions,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ export class WorkflowExecutionService {
|
|||
}: WorkflowRequest.ManualRunPayload,
|
||||
user: User,
|
||||
pushRef?: string,
|
||||
partialExecutionVersion?: string,
|
||||
partialExecutionVersion: 1 | 2 = 1,
|
||||
) {
|
||||
const pinData = workflowData.pinData;
|
||||
const pinnedTrigger = this.selectPinnedActivatorStarter(
|
||||
|
@ -142,7 +142,7 @@ export class WorkflowExecutionService {
|
|||
startNodes,
|
||||
workflowData,
|
||||
userId: user.id,
|
||||
partialExecutionVersion: partialExecutionVersion ?? '0',
|
||||
partialExecutionVersion,
|
||||
dirtyNodeNames,
|
||||
triggerToStartFrom,
|
||||
};
|
||||
|
|
|
@ -55,12 +55,7 @@ export declare namespace WorkflowRequest {
|
|||
|
||||
type NewName = AuthenticatedRequest<{}, {}, {}, { name?: string }>;
|
||||
|
||||
type ManualRun = AuthenticatedRequest<
|
||||
{ workflowId: string },
|
||||
{},
|
||||
ManualRunPayload,
|
||||
{ partialExecutionVersion?: string }
|
||||
>;
|
||||
type ManualRun = AuthenticatedRequest<{ workflowId: string }, {}, ManualRunPayload, {}>;
|
||||
|
||||
type Share = AuthenticatedRequest<{ workflowId: string }, {}, { shareWithIds: string[] }>;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ImportWorkflowFromUrlDto } from '@n8n/api-types';
|
||||
import { ImportWorkflowFromUrlDto, ManualRunQueryDto } from '@n8n/api-types';
|
||||
import { GlobalConfig } from '@n8n/config';
|
||||
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
|
||||
import { In, type FindOptionsRelations } from '@n8n/typeorm';
|
||||
|
@ -9,7 +9,6 @@ import { ApplicationError } from 'n8n-workflow';
|
|||
import { v4 as uuid } from 'uuid';
|
||||
import { z } from 'zod';
|
||||
|
||||
import config from '@/config';
|
||||
import type { Project } from '@/databases/entities/project';
|
||||
import { SharedWorkflow } from '@/databases/entities/shared-workflow';
|
||||
import { WorkflowEntity } from '@/databases/entities/workflow-entity';
|
||||
|
@ -367,7 +366,11 @@ export class WorkflowsController {
|
|||
|
||||
@Post('/:workflowId/run')
|
||||
@ProjectScope('workflow:execute')
|
||||
async runManually(req: WorkflowRequest.ManualRun) {
|
||||
async runManually(
|
||||
req: WorkflowRequest.ManualRun,
|
||||
_res: unknown,
|
||||
@Query query: ManualRunQueryDto,
|
||||
) {
|
||||
if (!req.body.workflowData.id) {
|
||||
throw new ApplicationError('You cannot execute a workflow without an ID', {
|
||||
level: 'warning',
|
||||
|
@ -395,9 +398,7 @@ export class WorkflowsController {
|
|||
req.body,
|
||||
req.user,
|
||||
req.headers['push-ref'],
|
||||
req.query.partialExecutionVersion === '-1'
|
||||
? config.getEnv('featureFlags.partialExecutionVersionDefault')
|
||||
: req.query.partialExecutionVersion,
|
||||
query.partialExecutionVersion,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -137,4 +137,8 @@ export const defaultSettings: FrontendSettings = {
|
|||
},
|
||||
betaFeatures: [],
|
||||
easyAIWorkflowOnboarded: false,
|
||||
partialExecution: {
|
||||
version: 1,
|
||||
enforce: false,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -19,9 +19,8 @@ import { useUIStore } from '@/stores/ui.store';
|
|||
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||
import { useToast } from './useToast';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
import { ref } from 'vue';
|
||||
import { captor, mock } from 'vitest-mock-extended';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
|
||||
vi.mock('@/stores/workflows.store', () => ({
|
||||
useWorkflowsStore: vi.fn().mockReturnValue({
|
||||
|
@ -41,16 +40,6 @@ vi.mock('@/stores/workflows.store', () => ({
|
|||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@vueuse/core', async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||
const originalModule = await vi.importActual<typeof import('@vueuse/core')>('@vueuse/core');
|
||||
|
||||
return {
|
||||
...originalModule, // Keep all original exports
|
||||
useLocalStorage: vi.fn().mockReturnValue({ value: undefined }), // Mock useLocalStorage
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@/composables/useTelemetry', () => ({
|
||||
useTelemetry: vi.fn().mockReturnValue({ track: vi.fn() }),
|
||||
}));
|
||||
|
@ -106,6 +95,7 @@ describe('useRunWorkflow({ router })', () => {
|
|||
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
|
||||
let router: ReturnType<typeof useRouter>;
|
||||
let workflowHelpers: ReturnType<typeof useWorkflowHelpers>;
|
||||
let settingsStore: ReturnType<typeof useSettingsStore>;
|
||||
|
||||
beforeAll(() => {
|
||||
const pinia = createTestingPinia({ stubActions: false });
|
||||
|
@ -115,6 +105,7 @@ describe('useRunWorkflow({ router })', () => {
|
|||
rootStore = useRootStore();
|
||||
uiStore = useUIStore();
|
||||
workflowsStore = useWorkflowsStore();
|
||||
settingsStore = useSettingsStore();
|
||||
|
||||
router = useRouter();
|
||||
workflowHelpers = useWorkflowHelpers({ router });
|
||||
|
@ -322,8 +313,8 @@ describe('useRunWorkflow({ router })', () => {
|
|||
expect(result).toEqual(mockExecutionResponse);
|
||||
});
|
||||
|
||||
it('should send dirty nodes for partial executions', async () => {
|
||||
vi.mocked(useLocalStorage).mockReturnValueOnce(ref(1));
|
||||
it('should send dirty nodes for partial executions v2', async () => {
|
||||
vi.mocked(settingsStore).partialExecutionVersion = 2;
|
||||
const composable = useRunWorkflow({ router });
|
||||
const parentName = 'When clicking';
|
||||
const executeName = 'Code';
|
||||
|
@ -404,7 +395,7 @@ describe('useRunWorkflow({ router })', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('does not use the original run data if `PartialExecution.version` is set to 0', async () => {
|
||||
it('does not use the original run data if `partialExecutionVersion` is set to 1', async () => {
|
||||
// ARRANGE
|
||||
const mockExecutionResponse = { executionId: '123' };
|
||||
const mockRunData = { nodeName: [] };
|
||||
|
@ -413,7 +404,7 @@ describe('useRunWorkflow({ router })', () => {
|
|||
const workflow = mock<Workflow>({ name: 'Test Workflow' });
|
||||
workflow.getParentNodes.mockReturnValue([]);
|
||||
|
||||
vi.mocked(useLocalStorage).mockReturnValueOnce(ref(0));
|
||||
vi.mocked(settingsStore).partialExecutionVersion = 1;
|
||||
vi.mocked(rootStore).pushConnectionActive = true;
|
||||
vi.mocked(workflowsStore).runWorkflow.mockResolvedValue(mockExecutionResponse);
|
||||
vi.mocked(workflowsStore).nodesIssuesExist = false;
|
||||
|
@ -435,7 +426,7 @@ describe('useRunWorkflow({ router })', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('retains the original run data if `PartialExecution.version` is set to 1', async () => {
|
||||
it('retains the original run data if `partialExecutionVersion` is set to 2', async () => {
|
||||
// ARRANGE
|
||||
const mockExecutionResponse = { executionId: '123' };
|
||||
const mockRunData = { nodeName: [] };
|
||||
|
@ -444,7 +435,7 @@ describe('useRunWorkflow({ router })', () => {
|
|||
const workflow = mock<Workflow>({ name: 'Test Workflow' });
|
||||
workflow.getParentNodes.mockReturnValue([]);
|
||||
|
||||
vi.mocked(useLocalStorage).mockReturnValueOnce(ref(1));
|
||||
vi.mocked(settingsStore).partialExecutionVersion = 2;
|
||||
vi.mocked(rootStore).pushConnectionActive = true;
|
||||
vi.mocked(workflowsStore).runWorkflow.mockResolvedValue(mockExecutionResponse);
|
||||
vi.mocked(workflowsStore).nodesIssuesExist = false;
|
||||
|
@ -464,7 +455,7 @@ describe('useRunWorkflow({ router })', () => {
|
|||
expect(dataCaptor.value).toMatchObject({ data: { resultData: { runData: mockRunData } } });
|
||||
});
|
||||
|
||||
it("does not send run data if it's not a partial execution even if `PartialExecution.version` is set to 1", async () => {
|
||||
it("does not send run data if it's not a partial execution even if `partialExecutionVersion` is set to 2", async () => {
|
||||
// ARRANGE
|
||||
const mockExecutionResponse = { executionId: '123' };
|
||||
const mockRunData = { nodeName: [] };
|
||||
|
@ -473,7 +464,7 @@ describe('useRunWorkflow({ router })', () => {
|
|||
const workflow = mock<Workflow>({ name: 'Test Workflow' });
|
||||
workflow.getParentNodes.mockReturnValue([]);
|
||||
|
||||
vi.mocked(useLocalStorage).mockReturnValueOnce(ref(1));
|
||||
vi.mocked(settingsStore).partialExecutionVersion = 2;
|
||||
vi.mocked(rootStore).pushConnectionActive = true;
|
||||
vi.mocked(workflowsStore).runWorkflow.mockResolvedValue(mockExecutionResponse);
|
||||
vi.mocked(workflowsStore).nodesIssuesExist = false;
|
||||
|
|
|
@ -35,7 +35,7 @@ import { isEmpty } from '@/utils/typesUtils';
|
|||
import { useI18n } from '@/composables/useI18n';
|
||||
import { get } from 'lodash-es';
|
||||
import { useExecutionsStore } from '@/stores/executions.store';
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
|
||||
const getDirtyNodeNames = (
|
||||
runData: IRunData,
|
||||
|
@ -260,18 +260,18 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
|
|||
return undefined;
|
||||
}
|
||||
|
||||
// -1 means the backend chooses the default
|
||||
// 0 is the old flow
|
||||
// 1 is the new flow
|
||||
const partialExecutionVersion = useLocalStorage('PartialExecution.version', -1);
|
||||
// partial executions must have a destination node
|
||||
const isPartialExecution = options.destinationNode !== undefined;
|
||||
const settingsStore = useSettingsStore();
|
||||
const version = settingsStore.partialExecutionVersion;
|
||||
const startRunData: IStartRunData = {
|
||||
workflowData,
|
||||
// With the new partial execution version the backend decides what run
|
||||
// data to use and what to ignore.
|
||||
runData: !isPartialExecution
|
||||
? // if it's a full execution we don't want to send any run data
|
||||
undefined
|
||||
: partialExecutionVersion.value === 1
|
||||
: version === 2
|
||||
? // With the new partial execution version the backend decides
|
||||
//what run data to use and what to ignore.
|
||||
(runData ?? undefined)
|
||||
|
|
|
@ -2,6 +2,8 @@ import type { FrontendSettings } from '@n8n/api-types';
|
|||
import { createPinia, setActivePinia } from 'pinia';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { useSettingsStore } from './settings.store';
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const { getSettings } = vi.hoisted(() => ({
|
||||
getSettings: vi.fn(),
|
||||
|
@ -54,6 +56,16 @@ vi.mock('@/stores/versions.store', () => ({
|
|||
})),
|
||||
}));
|
||||
|
||||
vi.mock('@vueuse/core', async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||
const originalModule = await vi.importActual<typeof import('@vueuse/core')>('@vueuse/core');
|
||||
|
||||
return {
|
||||
...originalModule,
|
||||
useLocalStorage: vi.fn().mockReturnValue({ value: undefined }),
|
||||
};
|
||||
});
|
||||
|
||||
const mockSettings = mock<FrontendSettings>({
|
||||
authCookie: { secure: true },
|
||||
});
|
||||
|
@ -99,4 +111,47 @@ describe('settings.store', () => {
|
|||
expect(sessionStarted).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('partialExecutionVersion', () => {
|
||||
it.each([
|
||||
{
|
||||
name: 'pick the default',
|
||||
default: 1 as const,
|
||||
enforce: false,
|
||||
userVersion: -1,
|
||||
result: 1,
|
||||
},
|
||||
{
|
||||
name: "pick the user' choice",
|
||||
default: 1 as const,
|
||||
enforce: false,
|
||||
userVersion: 2,
|
||||
result: 2,
|
||||
},
|
||||
{
|
||||
name: 'enforce the default',
|
||||
default: 1 as const,
|
||||
enforce: true,
|
||||
userVersion: 2,
|
||||
result: 1,
|
||||
},
|
||||
{
|
||||
name: 'enforce the default',
|
||||
default: 2 as const,
|
||||
enforce: true,
|
||||
userVersion: 1,
|
||||
result: 2,
|
||||
},
|
||||
])('%name', async ({ default: defaultVersion, userVersion, enforce, result }) => {
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
settingsStore.settings.partialExecution = {
|
||||
version: defaultVersion,
|
||||
enforce,
|
||||
};
|
||||
vi.mocked(useLocalStorage).mockReturnValueOnce(ref(userVersion));
|
||||
|
||||
expect(settingsStore.partialExecutionVersion).toBe(result);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,6 +19,7 @@ import { useVersionsStore } from './versions.store';
|
|||
import { makeRestApiRequest } from '@/utils/apiUtils';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { i18n } from '@/plugins/i18n';
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
|
||||
export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||
const initialized = ref(false);
|
||||
|
@ -98,6 +99,22 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
|||
|
||||
const isCloudDeployment = computed(() => settings.value.deployment?.type === 'cloud');
|
||||
|
||||
const partialExecutionVersion = computed(() => {
|
||||
const defaultVersion = settings.value.partialExecution?.version ?? 1;
|
||||
const enforceVersion = settings.value.partialExecution?.enforce ?? false;
|
||||
// -1 means we pick the defaultVersion
|
||||
// 1 is the old flow
|
||||
// 2 is the new flow
|
||||
const userVersion = useLocalStorage('PartialExecution.version', -1).value;
|
||||
const version = enforceVersion
|
||||
? defaultVersion
|
||||
: userVersion === -1
|
||||
? defaultVersion
|
||||
: userVersion;
|
||||
|
||||
return version;
|
||||
});
|
||||
|
||||
const isAiCreditsEnabled = computed(() => settings.value.aiCredits?.enabled);
|
||||
|
||||
const aiCreditsQuota = computed(() => settings.value.aiCredits?.credits);
|
||||
|
@ -430,5 +447,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
|||
getSettings,
|
||||
setSettings,
|
||||
initialize,
|
||||
partialExecutionVersion,
|
||||
};
|
||||
});
|
||||
|
|
|
@ -79,7 +79,6 @@ import { computed, ref } from 'vue';
|
|||
import { useProjectsStore } from '@/stores/projects.store';
|
||||
import type { ProjectSharingData } from '@/types/projects.types';
|
||||
import type { PushPayload } from '@n8n/api-types';
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { TelemetryHelpers } from 'n8n-workflow';
|
||||
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||
|
@ -125,10 +124,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
|||
const nodeHelpers = useNodeHelpers();
|
||||
const usersStore = useUsersStore();
|
||||
|
||||
// -1 means the backend chooses the default
|
||||
// 0 is the old flow
|
||||
// 1 is the new flow
|
||||
const partialExecutionVersion = useLocalStorage('PartialExecution.version', -1);
|
||||
const version = settingsStore.partialExecutionVersion;
|
||||
|
||||
const workflow = ref<IWorkflowDb>(createEmptyWorkflow());
|
||||
const usedCredentials = ref<Record<string, IUsedCredential>>({});
|
||||
|
@ -1474,7 +1470,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
|||
return await makeRestApiRequest(
|
||||
rootStore.restApiContext,
|
||||
'POST',
|
||||
`/workflows/${startRunData.workflowData.id}/run?partialExecutionVersion=${partialExecutionVersion.value}`,
|
||||
`/workflows/${startRunData.workflowData.id}/run?partialExecutionVersion=${version}`,
|
||||
startRunData as unknown as IDataObject,
|
||||
);
|
||||
} catch (error) {
|
||||
|
|
|
@ -2293,12 +2293,10 @@ export interface IWorkflowExecutionDataProcess {
|
|||
/**
|
||||
* Defines which version of the partial execution flow is used.
|
||||
* Possible values are:
|
||||
* 0 - use the old flow
|
||||
* 1 - use the new flow
|
||||
* -1 - the backend chooses which flow based on the environment variable
|
||||
* PARTIAL_EXECUTION_VERSION_DEFAULT
|
||||
* 1 - use the old flow
|
||||
* 2 - use the new flow
|
||||
*/
|
||||
partialExecutionVersion?: string;
|
||||
partialExecutionVersion?: 1 | 2;
|
||||
dirtyNodeNames?: string[];
|
||||
triggerToStartFrom?: {
|
||||
name: string;
|
||||
|
|
Loading…
Reference in a new issue