refactor(core): Port workflows config (no-changelog) (#10173)

This commit is contained in:
Iván Ovejero 2024-07-24 14:38:29 +02:00 committed by GitHub
parent 9056242b3a
commit b81f0bf9ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 48 additions and 28 deletions

View file

@ -0,0 +1,20 @@
import { Config, Env } from '../decorators';
@Config
export class WorkflowsConfig {
/** Default name for workflow */
@Env('WORKFLOWS_DEFAULT_NAME')
readonly defaultName: string = 'My workflow';
/** Show onboarding flow in new workflow */
@Env('N8N_ONBOARDING_FLOW_DISABLED')
readonly onboardingFlowDisabled: boolean = false;
/** Default option for which workflows may call the current workflow */
@Env('N8N_WORKFLOW_CALLER_POLICY_DEFAULT_OPTION')
readonly callerPolicyDefaultOption:
| 'any'
| 'none'
| 'workflowsFromAList'
| 'workflowsFromSameOwner' = 'workflowsFromSameOwner';
}

View file

@ -9,6 +9,7 @@ import { TemplatesConfig } from './configs/templates';
import { EventBusConfig } from './configs/event-bus'; import { EventBusConfig } from './configs/event-bus';
import { NodesConfig } from './configs/nodes'; import { NodesConfig } from './configs/nodes';
import { ExternalStorageConfig } from './configs/external-storage'; import { ExternalStorageConfig } from './configs/external-storage';
import { WorkflowsConfig } from './configs/workflows';
@Config @Config
class UserManagementConfig { class UserManagementConfig {
@ -47,4 +48,7 @@ export class GlobalConfig {
@Nested @Nested
readonly externalStorage: ExternalStorageConfig; readonly externalStorage: ExternalStorageConfig;
@Nested
readonly workflows: WorkflowsConfig;
} }

View file

@ -135,6 +135,11 @@ describe('GlobalConfig', () => {
}, },
}, },
}, },
workflows: {
defaultName: 'My workflow',
onboardingFlowDisabled: false,
callerPolicyDefaultOption: 'workflowsFromSameOwner',
},
}; };
it('should use all default values when no env variables are defined', () => { it('should use all default values when no env variables are defined', () => {

View file

@ -12,27 +12,6 @@ convict.addFormat({
}); });
export const schema = { export const schema = {
workflows: {
defaultName: {
doc: 'Default name for workflow',
format: String,
default: 'My workflow',
env: 'WORKFLOWS_DEFAULT_NAME',
},
onboardingFlowDisabled: {
doc: 'Show onboarding flow in new workflow',
format: Boolean,
default: false,
env: 'N8N_ONBOARDING_FLOW_DISABLED',
},
callerPolicyDefaultOption: {
doc: 'Default option for which workflows may call the current workflow',
format: ['any', 'none', 'workflowsFromAList', 'workflowsFromSameOwner'] as const,
default: 'workflowsFromSameOwner',
env: 'N8N_WORKFLOW_CALLER_POLICY_DEFAULT_OPTION',
},
},
executions: { executions: {
// TODO: remove this and all usage of `executions.process` when we're sure that nobody has this in their config file anymore. // TODO: remove this and all usage of `executions.process` when we're sure that nobody has this in their config file anymore.
process: { process: {

View file

@ -99,7 +99,7 @@ export class FrontendService {
saveExecutionProgress: config.getEnv('executions.saveExecutionProgress'), saveExecutionProgress: config.getEnv('executions.saveExecutionProgress'),
executionTimeout: config.getEnv('executions.timeout'), executionTimeout: config.getEnv('executions.timeout'),
maxExecutionTimeout: config.getEnv('executions.maxTimeout'), maxExecutionTimeout: config.getEnv('executions.maxTimeout'),
workflowCallerPolicyDefaultOption: config.getEnv('workflows.callerPolicyDefaultOption'), workflowCallerPolicyDefaultOption: this.globalConfig.workflows.callerPolicyDefaultOption,
timezone: config.getEnv('generic.timezone'), timezone: config.getEnv('generic.timezone'),
urlBaseWebhook: this.urlService.getWebhookBaseUrl(), urlBaseWebhook: this.urlService.getWebhookBaseUrl(),
urlBaseEditor: instanceBaseUrl, urlBaseEditor: instanceBaseUrl,

View file

@ -10,6 +10,7 @@ import { SubworkflowPolicyChecker } from '../subworkflow-policy-checker.service'
import type { WorkflowEntity } from '@/databases/entities/WorkflowEntity'; import type { WorkflowEntity } from '@/databases/entities/WorkflowEntity';
import type { License } from '@/License'; import type { License } from '@/License';
import type { GlobalConfig } from '@n8n/config';
const toTargetCallErrorMsg = (subworkflowId: string) => const toTargetCallErrorMsg = (subworkflowId: string) =>
`Target workflow ID ${subworkflowId} may not be called`; `Target workflow ID ${subworkflowId} may not be called`;
@ -19,7 +20,10 @@ const memberPersonalProject = mock<Project>();
describe('SubworkflowPolicyChecker', () => { describe('SubworkflowPolicyChecker', () => {
const license = mock<License>(); const license = mock<License>();
const checker = new SubworkflowPolicyChecker(mock(), license, ownershipService); const globalConfig = mock<GlobalConfig>({
workflows: { callerPolicyDefaultOption: 'workflowsFromSameOwner' },
});
const checker = new SubworkflowPolicyChecker(mock(), license, ownershipService, globalConfig);
beforeEach(() => { beforeEach(() => {
license.isSharingEnabled.mockReturnValue(true); license.isSharingEnabled.mockReturnValue(true);
@ -31,7 +35,12 @@ describe('SubworkflowPolicyChecker', () => {
describe('no caller policy', () => { describe('no caller policy', () => {
test('should fall back to N8N_WORKFLOW_CALLER_POLICY_DEFAULT_OPTION', async () => { test('should fall back to N8N_WORKFLOW_CALLER_POLICY_DEFAULT_OPTION', async () => {
config.set('workflows.callerPolicyDefaultOption', 'none'); const checker = new SubworkflowPolicyChecker(
mock(),
license,
ownershipService,
mock<GlobalConfig>({ workflows: { callerPolicyDefaultOption: 'none' } }),
);
const parentWorkflow = mock<WorkflowEntity>(); const parentWorkflow = mock<WorkflowEntity>();
const subworkflow = mock<Workflow>(); // no caller policy const subworkflow = mock<Workflow>(); // no caller policy

View file

@ -1,6 +1,6 @@
import { Service } from 'typedi'; import { Service } from 'typedi';
import { WorkflowOperationError } from 'n8n-workflow'; import { WorkflowOperationError } from 'n8n-workflow';
import config from '@/config'; import { GlobalConfig } from '@n8n/config';
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { License } from '@/License'; import { License } from '@/License';
import { OwnershipService } from '@/services/ownership.service'; import { OwnershipService } from '@/services/ownership.service';
@ -12,6 +12,7 @@ export class SubworkflowPolicyChecker {
private readonly logger: Logger, private readonly logger: Logger,
private readonly license: License, private readonly license: License,
private readonly ownershipService: OwnershipService, private readonly ownershipService: OwnershipService,
private readonly globalConfig: GlobalConfig,
) {} ) {}
async check(subworkflow: Workflow, parentWorkflowId: string, node?: INode) { async check(subworkflow: Workflow, parentWorkflowId: string, node?: INode) {
@ -28,7 +29,7 @@ export class SubworkflowPolicyChecker {
} }
let policy = let policy =
subworkflow.settings?.callerPolicy ?? config.getEnv('workflows.callerPolicyDefaultOption'); subworkflow.settings?.callerPolicy ?? this.globalConfig.workflows.callerPolicyDefaultOption;
const isSharingEnabled = this.license.isSharingEnabled(); const isSharingEnabled = this.license.isSharingEnabled();

View file

@ -43,6 +43,7 @@ import type { Project } from '@/databases/entities/Project';
import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository'; import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository';
import { z } from 'zod'; import { z } from 'zod';
import { EventService } from '@/eventbus/event.service'; import { EventService } from '@/eventbus/event.service';
import { GlobalConfig } from '@n8n/config';
@RestController('/workflows') @RestController('/workflows')
export class WorkflowsController { export class WorkflowsController {
@ -67,6 +68,7 @@ export class WorkflowsController {
private readonly projectService: ProjectService, private readonly projectService: ProjectService,
private readonly projectRelationRepository: ProjectRelationRepository, private readonly projectRelationRepository: ProjectRelationRepository,
private readonly eventService: EventService, private readonly eventService: EventService,
private readonly globalConfig: GlobalConfig,
) {} ) {}
@Post('/') @Post('/')
@ -204,12 +206,12 @@ export class WorkflowsController {
@Get('/new') @Get('/new')
async getNewName(req: WorkflowRequest.NewName) { async getNewName(req: WorkflowRequest.NewName) {
const requestedName = req.query.name ?? config.getEnv('workflows.defaultName'); const requestedName = req.query.name ?? this.globalConfig.workflows.defaultName;
const name = await this.namingService.getUniqueWorkflowName(requestedName); const name = await this.namingService.getUniqueWorkflowName(requestedName);
const onboardingFlowEnabled = const onboardingFlowEnabled =
!config.getEnv('workflows.onboardingFlowDisabled') && !this.globalConfig.workflows.onboardingFlowDisabled &&
!req.user.settings?.isOnboarded && !req.user.settings?.isOnboarded &&
(await this.userOnboardingService.isBelowThreshold(req.user)); (await this.userOnboardingService.isBelowThreshold(req.user));