mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57:29 -08:00
refactor(core): Stronger typing for workflow settings (no-changelog) (#5754)
This commit is contained in:
parent
d33a1ac1e9
commit
c9d9069c0e
|
@ -393,8 +393,7 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expressionResolveValues) {
|
if (expressionResolveValues) {
|
||||||
const timezone =
|
const timezone = expressionResolveValues.workflow.settings.timezone ?? defaultTimezone;
|
||||||
(expressionResolveValues.workflow.settings.timezone as string) || defaultTimezone;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
decryptedData = expressionResolveValues.workflow.expression.getParameterValue(
|
decryptedData = expressionResolveValues.workflow.expression.getParameterValue(
|
||||||
|
|
|
@ -23,6 +23,7 @@ import type {
|
||||||
ExecutionStatus,
|
ExecutionStatus,
|
||||||
IExecutionsSummary,
|
IExecutionsSummary,
|
||||||
FeatureFlags,
|
FeatureFlags,
|
||||||
|
WorkflowSettings,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import type { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
import type { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
||||||
|
@ -455,16 +456,12 @@ export interface IVersionNotificationSettings {
|
||||||
export interface IN8nUISettings {
|
export interface IN8nUISettings {
|
||||||
endpointWebhook: string;
|
endpointWebhook: string;
|
||||||
endpointWebhookTest: string;
|
endpointWebhookTest: string;
|
||||||
saveDataErrorExecution: 'all' | 'none';
|
saveDataErrorExecution: WorkflowSettings.SaveDataExecution;
|
||||||
saveDataSuccessExecution: 'all' | 'none';
|
saveDataSuccessExecution: WorkflowSettings.SaveDataExecution;
|
||||||
saveManualExecutions: boolean;
|
saveManualExecutions: boolean;
|
||||||
executionTimeout: number;
|
executionTimeout: number;
|
||||||
maxExecutionTimeout: number;
|
maxExecutionTimeout: number;
|
||||||
workflowCallerPolicyDefaultOption:
|
workflowCallerPolicyDefaultOption: WorkflowSettings.CallerPolicy;
|
||||||
| 'any'
|
|
||||||
| 'none'
|
|
||||||
| 'workflowsFromAList'
|
|
||||||
| 'workflowsFromSameOwner';
|
|
||||||
oauthCallbackUrls: {
|
oauthCallbackUrls: {
|
||||||
oauth1: string;
|
oauth1: string;
|
||||||
oauth2: string;
|
oauth2: string;
|
||||||
|
|
|
@ -116,7 +116,7 @@ export class PermissionChecker {
|
||||||
if (parentWorkflowId === undefined) {
|
if (parentWorkflowId === undefined) {
|
||||||
throw errorToThrow;
|
throw errorToThrow;
|
||||||
}
|
}
|
||||||
const allowedCallerIds = (subworkflow.settings.callerIds as string | undefined)
|
const allowedCallerIds = subworkflow.settings.callerIds
|
||||||
?.split(',')
|
?.split(',')
|
||||||
.map((id) => id.trim())
|
.map((id) => id.trim())
|
||||||
.filter((id) => id !== '');
|
.filter((id) => id !== '');
|
||||||
|
|
|
@ -136,17 +136,11 @@ export function executeErrorWorkflow(
|
||||||
|
|
||||||
// Run the error workflow
|
// Run the error workflow
|
||||||
// To avoid an infinite loop do not run the error workflow again if the error-workflow itself failed and it is its own error-workflow.
|
// To avoid an infinite loop do not run the error workflow again if the error-workflow itself failed and it is its own error-workflow.
|
||||||
if (
|
const { errorWorkflow } = workflowData.settings ?? {};
|
||||||
workflowData.settings?.errorWorkflow &&
|
if (errorWorkflow && !(mode === 'error' && workflowId && errorWorkflow === workflowId)) {
|
||||||
!(
|
|
||||||
mode === 'error' &&
|
|
||||||
workflowId &&
|
|
||||||
workflowData.settings.errorWorkflow.toString() === workflowId
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Logger.verbose('Start external error workflow', {
|
Logger.verbose('Start external error workflow', {
|
||||||
executionId,
|
executionId,
|
||||||
errorWorkflowId: workflowData.settings.errorWorkflow.toString(),
|
errorWorkflowId: errorWorkflow,
|
||||||
workflowId,
|
workflowId,
|
||||||
});
|
});
|
||||||
// If a specific error workflow is set run only that one
|
// If a specific error workflow is set run only that one
|
||||||
|
@ -160,11 +154,7 @@ export function executeErrorWorkflow(
|
||||||
}
|
}
|
||||||
getWorkflowOwner(workflowId)
|
getWorkflowOwner(workflowId)
|
||||||
.then((user) => {
|
.then((user) => {
|
||||||
void WorkflowHelpers.executeErrorWorkflow(
|
void WorkflowHelpers.executeErrorWorkflow(errorWorkflow, workflowErrorData, user);
|
||||||
workflowData.settings!.errorWorkflow as string,
|
|
||||||
workflowErrorData,
|
|
||||||
user,
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.catch((error: Error) => {
|
.catch((error: Error) => {
|
||||||
ErrorReporter.error(error);
|
ErrorReporter.error(error);
|
||||||
|
@ -172,7 +162,7 @@ export function executeErrorWorkflow(
|
||||||
`Could not execute ErrorWorkflow for execution ID ${this.executionId} because of error querying the workflow owner`,
|
`Could not execute ErrorWorkflow for execution ID ${this.executionId} because of error querying the workflow owner`,
|
||||||
{
|
{
|
||||||
executionId,
|
executionId,
|
||||||
errorWorkflowId: workflowData.settings!.errorWorkflow!.toString(),
|
errorWorkflowId: errorWorkflow,
|
||||||
workflowId,
|
workflowId,
|
||||||
error,
|
error,
|
||||||
workflowErrorData,
|
workflowErrorData,
|
||||||
|
@ -421,21 +411,21 @@ export function hookFunctionsPreExecute(parentProcessMode?: string): IWorkflowEx
|
||||||
],
|
],
|
||||||
nodeExecuteAfter: [
|
nodeExecuteAfter: [
|
||||||
async function (
|
async function (
|
||||||
|
this: WorkflowHooks,
|
||||||
nodeName: string,
|
nodeName: string,
|
||||||
data: ITaskData,
|
data: ITaskData,
|
||||||
executionData: IRunExecutionData,
|
executionData: IRunExecutionData,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (this.workflowData.settings !== undefined) {
|
const saveExecutionProgress = config.getEnv('executions.saveExecutionProgress');
|
||||||
if (this.workflowData.settings.saveExecutionProgress === false) {
|
const workflowSettings = this.workflowData.settings;
|
||||||
|
if (workflowSettings !== undefined) {
|
||||||
|
if (workflowSettings.saveExecutionProgress === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (workflowSettings.saveExecutionProgress !== true && !saveExecutionProgress) {
|
||||||
this.workflowData.settings.saveExecutionProgress !== true &&
|
|
||||||
!config.getEnv('executions.saveExecutionProgress')
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (!config.getEnv('executions.saveExecutionProgress')) {
|
} else if (!saveExecutionProgress) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,13 +553,11 @@ function hookFunctionsSave(parentProcessMode?: string): IWorkflowExecuteHooks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const workflowSettings = this.workflowData.settings;
|
||||||
let saveManualExecutions = config.getEnv('executions.saveDataManualExecutions');
|
let saveManualExecutions = config.getEnv('executions.saveDataManualExecutions');
|
||||||
if (
|
if (workflowSettings?.saveManualExecutions !== undefined) {
|
||||||
this.workflowData.settings !== undefined &&
|
|
||||||
this.workflowData.settings.saveManualExecutions !== undefined
|
|
||||||
) {
|
|
||||||
// Apply to workflow override
|
// Apply to workflow override
|
||||||
saveManualExecutions = this.workflowData.settings.saveManualExecutions as boolean;
|
saveManualExecutions = workflowSettings.saveManualExecutions as boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isManualMode && !saveManualExecutions && !fullRunData.waitTill) {
|
if (isManualMode && !saveManualExecutions && !fullRunData.waitTill) {
|
||||||
|
@ -1024,16 +1012,14 @@ async function executeWorkflow(
|
||||||
additionalDataIntegrated.executeWorkflow = additionalData.executeWorkflow;
|
additionalDataIntegrated.executeWorkflow = additionalData.executeWorkflow;
|
||||||
|
|
||||||
let subworkflowTimeout = additionalData.executionTimeoutTimestamp;
|
let subworkflowTimeout = additionalData.executionTimeoutTimestamp;
|
||||||
if (
|
const workflowSettings = workflowData.settings;
|
||||||
workflowData.settings?.executionTimeout !== undefined &&
|
if (workflowSettings?.executionTimeout !== undefined && workflowSettings.executionTimeout > 0) {
|
||||||
workflowData.settings.executionTimeout > 0
|
|
||||||
) {
|
|
||||||
// We might have received a max timeout timestamp from the parent workflow
|
// We might have received a max timeout timestamp from the parent workflow
|
||||||
// If we did, then we get the minimum time between the two timeouts
|
// If we did, then we get the minimum time between the two timeouts
|
||||||
// If no timeout was given from the parent, then we use our timeout.
|
// If no timeout was given from the parent, then we use our timeout.
|
||||||
subworkflowTimeout = Math.min(
|
subworkflowTimeout = Math.min(
|
||||||
additionalData.executionTimeoutTimestamp || Number.MAX_SAFE_INTEGER,
|
additionalData.executionTimeoutTimestamp || Number.MAX_SAFE_INTEGER,
|
||||||
Date.now() + (workflowData.settings.executionTimeout as number) * 1000,
|
Date.now() + workflowSettings.executionTimeout * 1000,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import type {
|
||||||
IRun,
|
IRun,
|
||||||
WorkflowExecuteMode,
|
WorkflowExecuteMode,
|
||||||
WorkflowHooks,
|
WorkflowHooks,
|
||||||
|
WorkflowSettings,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import {
|
import {
|
||||||
ErrorReporterProxy as ErrorReporter,
|
ErrorReporterProxy as ErrorReporter,
|
||||||
|
@ -248,11 +249,9 @@ export class WorkflowRunner {
|
||||||
// Changes were made by adding the `workflowTimeout` to the `additionalData`
|
// Changes were made by adding the `workflowTimeout` to the `additionalData`
|
||||||
// So that the timeout will also work for executions with nested workflows.
|
// So that the timeout will also work for executions with nested workflows.
|
||||||
let executionTimeout: NodeJS.Timeout;
|
let executionTimeout: NodeJS.Timeout;
|
||||||
let workflowTimeout = config.getEnv('executions.timeout'); // initialize with default
|
|
||||||
if (data.workflowData.settings && data.workflowData.settings.executionTimeout) {
|
|
||||||
workflowTimeout = data.workflowData.settings.executionTimeout as number; // preference on workflow setting
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const workflowSettings = data.workflowData.settings ?? {};
|
||||||
|
let workflowTimeout = workflowSettings.executionTimeout ?? config.getEnv('executions.timeout'); // initialize with default
|
||||||
if (workflowTimeout > 0) {
|
if (workflowTimeout > 0) {
|
||||||
workflowTimeout = Math.min(workflowTimeout, config.getEnv('executions.maxTimeout'));
|
workflowTimeout = Math.min(workflowTimeout, config.getEnv('executions.maxTimeout'));
|
||||||
}
|
}
|
||||||
|
@ -265,7 +264,7 @@ export class WorkflowRunner {
|
||||||
active: data.workflowData.active,
|
active: data.workflowData.active,
|
||||||
nodeTypes,
|
nodeTypes,
|
||||||
staticData: data.workflowData.staticData,
|
staticData: data.workflowData.staticData,
|
||||||
settings: data.workflowData.settings,
|
settings: workflowSettings,
|
||||||
});
|
});
|
||||||
const additionalData = await WorkflowExecuteAdditionalData.getBase(
|
const additionalData = await WorkflowExecuteAdditionalData.getBase(
|
||||||
data.userId,
|
data.userId,
|
||||||
|
@ -590,16 +589,12 @@ export class WorkflowRunner {
|
||||||
try {
|
try {
|
||||||
// Check if this execution data has to be removed from database
|
// Check if this execution data has to be removed from database
|
||||||
// based on workflow settings.
|
// based on workflow settings.
|
||||||
let saveDataErrorExecution = config.getEnv('executions.saveDataOnError') as string;
|
const workflowSettings = data.workflowData.settings ?? {};
|
||||||
let saveDataSuccessExecution = config.getEnv('executions.saveDataOnSuccess') as string;
|
const saveDataErrorExecution =
|
||||||
if (data.workflowData.settings !== undefined) {
|
workflowSettings.saveDataErrorExecution ?? config.getEnv('executions.saveDataOnError');
|
||||||
saveDataErrorExecution =
|
const saveDataSuccessExecution =
|
||||||
(data.workflowData.settings.saveDataErrorExecution as string) ||
|
workflowSettings.saveDataSuccessExecution ??
|
||||||
saveDataErrorExecution;
|
config.getEnv('executions.saveDataOnSuccess');
|
||||||
saveDataSuccessExecution =
|
|
||||||
(data.workflowData.settings.saveDataSuccessExecution as string) ||
|
|
||||||
saveDataSuccessExecution;
|
|
||||||
}
|
|
||||||
|
|
||||||
const workflowDidSucceed = !runData.data.resultData.error;
|
const workflowDidSucceed = !runData.data.resultData.error;
|
||||||
if (
|
if (
|
||||||
|
@ -666,10 +661,9 @@ export class WorkflowRunner {
|
||||||
|
|
||||||
// Start timeout for the execution
|
// Start timeout for the execution
|
||||||
let executionTimeout: NodeJS.Timeout;
|
let executionTimeout: NodeJS.Timeout;
|
||||||
let workflowTimeout = config.getEnv('executions.timeout'); // initialize with default
|
|
||||||
if (data.workflowData.settings && data.workflowData.settings.executionTimeout) {
|
const workflowSettings = data.workflowData.settings ?? {};
|
||||||
workflowTimeout = data.workflowData.settings.executionTimeout as number; // preference on workflow setting
|
let workflowTimeout = workflowSettings.executionTimeout ?? config.getEnv('executions.timeout'); // initialize with default
|
||||||
}
|
|
||||||
|
|
||||||
const processTimeoutFunction = (timeout: number) => {
|
const processTimeoutFunction = (timeout: number) => {
|
||||||
this.activeExecutions.stopExecution(executionId, 'timeout');
|
this.activeExecutions.stopExecution(executionId, 'timeout');
|
||||||
|
|
|
@ -130,13 +130,10 @@ class WorkflowRunnerProcess {
|
||||||
const license = Container.get(License);
|
const license = Container.get(License);
|
||||||
await license.init(instanceId);
|
await license.init(instanceId);
|
||||||
|
|
||||||
// Start timeout for the execution
|
const workflowSettings = this.data.workflowData.settings ?? {};
|
||||||
let workflowTimeout = config.getEnv('executions.timeout'); // initialize with default
|
|
||||||
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
|
||||||
if (this.data.workflowData.settings && this.data.workflowData.settings.executionTimeout) {
|
|
||||||
workflowTimeout = this.data.workflowData.settings.executionTimeout as number; // preference on workflow setting
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Start timeout for the execution
|
||||||
|
let workflowTimeout = workflowSettings.executionTimeout ?? config.getEnv('executions.timeout'); // initialize with default
|
||||||
if (workflowTimeout > 0) {
|
if (workflowTimeout > 0) {
|
||||||
workflowTimeout = Math.min(workflowTimeout, config.getEnv('executions.maxTimeout'));
|
workflowTimeout = Math.min(workflowTimeout, config.getEnv('executions.maxTimeout'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,14 +127,9 @@ export class Worker extends BaseCommand {
|
||||||
staticData = workflowData.staticData;
|
staticData = workflowData.staticData;
|
||||||
}
|
}
|
||||||
|
|
||||||
let workflowTimeout = config.getEnv('executions.timeout'); // initialize with default
|
const workflowSettings = currentExecutionDb.workflowData.settings ?? {};
|
||||||
if (
|
|
||||||
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
let workflowTimeout = workflowSettings.executionTimeout ?? config.getEnv('executions.timeout'); // initialize with default
|
||||||
currentExecutionDb.workflowData.settings &&
|
|
||||||
currentExecutionDb.workflowData.settings.executionTimeout
|
|
||||||
) {
|
|
||||||
workflowTimeout = currentExecutionDb.workflowData.settings.executionTimeout as number; // preference on workflow setting
|
|
||||||
}
|
|
||||||
|
|
||||||
let executionTimeoutTimestamp: number | undefined;
|
let executionTimeoutTimestamp: number | undefined;
|
||||||
if (workflowTimeout > 0) {
|
if (workflowTimeout > 0) {
|
||||||
|
|
|
@ -245,32 +245,27 @@ export class WorkflowsService {
|
||||||
await Container.get(ActiveWorkflowRunner).remove(workflowId);
|
await Container.get(ActiveWorkflowRunner).remove(workflowId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (workflow.settings) {
|
const workflowSettings = workflow.settings ?? {};
|
||||||
if (workflow.settings.timezone === 'DEFAULT') {
|
|
||||||
// Do not save the default timezone
|
const keysAllowingDefault = [
|
||||||
delete workflow.settings.timezone;
|
'timezone',
|
||||||
}
|
'saveDataErrorExecution',
|
||||||
if (workflow.settings.saveDataErrorExecution === 'DEFAULT') {
|
'saveDataSuccessExecution',
|
||||||
// Do not save when default got set
|
'saveManualExecutions',
|
||||||
delete workflow.settings.saveDataErrorExecution;
|
'saveExecutionProgress',
|
||||||
}
|
] as const;
|
||||||
if (workflow.settings.saveDataSuccessExecution === 'DEFAULT') {
|
for (const key of keysAllowingDefault) {
|
||||||
// Do not save when default got set
|
// Do not save the default value
|
||||||
delete workflow.settings.saveDataSuccessExecution;
|
if (workflowSettings[key] === 'DEFAULT') {
|
||||||
}
|
delete workflowSettings[key];
|
||||||
if (workflow.settings.saveManualExecutions === 'DEFAULT') {
|
|
||||||
// Do not save when default got set
|
|
||||||
delete workflow.settings.saveManualExecutions;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
parseInt(workflow.settings.executionTimeout as string, 10) ===
|
|
||||||
config.get('executions.timeout')
|
|
||||||
) {
|
|
||||||
// Do not save when default got set
|
|
||||||
delete workflow.settings.executionTimeout;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (workflowSettings.executionTimeout === config.get('executions.timeout')) {
|
||||||
|
// Do not save when default got set
|
||||||
|
delete workflowSettings.executionTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
if (workflow.name) {
|
if (workflow.name) {
|
||||||
workflow.updatedAt = new Date(); // required due to atomic update
|
workflow.updatedAt = new Date(); // required due to atomic update
|
||||||
await validateEntity(workflow);
|
await validateEntity(workflow);
|
||||||
|
|
|
@ -66,7 +66,7 @@ export class Expression {
|
||||||
if (value instanceof Date) {
|
if (value instanceof Date) {
|
||||||
// We don't want to use JSON.stringify for dates since it disregards workflow timezone
|
// We don't want to use JSON.stringify for dates since it disregards workflow timezone
|
||||||
result = DateTime.fromJSDate(value, {
|
result = DateTime.fromJSDate(value, {
|
||||||
zone: this.workflow.settings.timezone?.toString() ?? 'default',
|
zone: this.workflow.settings?.timezone ?? 'default',
|
||||||
}).toISO();
|
}).toISO();
|
||||||
} else {
|
} else {
|
||||||
result = JSON.stringify(value);
|
result = JSON.stringify(value);
|
||||||
|
|
|
@ -1709,8 +1709,21 @@ export interface IWorkflowHooksOptionalParameters {
|
||||||
sessionId?: string;
|
sessionId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export namespace WorkflowSettings {
|
||||||
|
export type CallerPolicy = 'any' | 'none' | 'workflowsFromAList' | 'workflowsFromSameOwner';
|
||||||
|
export type SaveDataExecution = 'DEFAULT' | 'all' | 'none';
|
||||||
|
}
|
||||||
|
|
||||||
export interface IWorkflowSettings {
|
export interface IWorkflowSettings {
|
||||||
[key: string]: IDataObject | string | number | boolean | undefined;
|
timezone?: 'DEFAULT' | string;
|
||||||
|
errorWorkflow?: string;
|
||||||
|
callerIds?: string;
|
||||||
|
callerPolicy?: WorkflowSettings.CallerPolicy;
|
||||||
|
saveDataErrorExecution?: WorkflowSettings.SaveDataExecution;
|
||||||
|
saveDataSuccessExecution?: WorkflowSettings.SaveDataExecution;
|
||||||
|
saveManualExecutions?: 'DEFAULT' | boolean;
|
||||||
|
saveExecutionProgress?: 'DEFAULT' | boolean;
|
||||||
|
executionTimeout?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LogTypes = 'debug' | 'verbose' | 'info' | 'warn' | 'error';
|
export type LogTypes = 'debug' | 'verbose' | 'info' | 'warn' | 'error';
|
||||||
|
|
|
@ -111,7 +111,7 @@ export class WorkflowDataProxy {
|
||||||
this.siblingParameters = siblingParameters;
|
this.siblingParameters = siblingParameters;
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
this.defaultTimezone = defaultTimezone;
|
this.defaultTimezone = defaultTimezone;
|
||||||
this.timezone = (this.workflow.settings.timezone as string) || this.defaultTimezone;
|
this.timezone = workflow.settings?.timezone ?? defaultTimezone;
|
||||||
this.selfData = selfData;
|
this.selfData = selfData;
|
||||||
this.additionalKeys = additionalKeys;
|
this.additionalKeys = additionalKeys;
|
||||||
this.executeData = executeData;
|
this.executeData = executeData;
|
||||||
|
|
Loading…
Reference in a new issue