refactor(core): Stronger typing for workflow settings (no-changelog) (#5754)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-03-24 13:11:48 +01:00 committed by GitHub
parent d33a1ac1e9
commit c9d9069c0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 77 additions and 101 deletions

View file

@ -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(

View file

@ -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;

View file

@ -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 !== '');

View file

@ -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,
); );
} }

View file

@ -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');

View file

@ -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'));
} }

View file

@ -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) {

View file

@ -245,30 +245,25 @@ 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',
'saveDataSuccessExecution',
'saveManualExecutions',
'saveExecutionProgress',
] as const;
for (const key of keysAllowingDefault) {
// Do not save the default value
if (workflowSettings[key] === 'DEFAULT') {
delete workflowSettings[key];
} }
if (workflow.settings.saveDataErrorExecution === 'DEFAULT') { }
if (workflowSettings.executionTimeout === config.get('executions.timeout')) {
// Do not save when default got set // Do not save when default got set
delete workflow.settings.saveDataErrorExecution; delete workflowSettings.executionTimeout;
}
if (workflow.settings.saveDataSuccessExecution === 'DEFAULT') {
// Do not save when default got set
delete workflow.settings.saveDataSuccessExecution;
}
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 (workflow.name) { if (workflow.name) {

View file

@ -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);

View file

@ -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';

View file

@ -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;