mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-02 07:01:30 -08:00
refactor(core): Port workflows config (no-changelog) (#10173)
This commit is contained in:
parent
9056242b3a
commit
b81f0bf9ea
20
packages/@n8n/config/src/configs/workflows.ts
Normal file
20
packages/@n8n/config/src/configs/workflows.ts
Normal 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';
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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', () => {
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue