refactor(core): Make controller constructors consistent (no-changelog) (#7015)

This commit is contained in:
Iván Ovejero 2023-08-25 13:23:22 +02:00 committed by GitHub
parent 07d3633a05
commit 87cf1d9c1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 145 additions and 270 deletions

View file

@ -168,11 +168,14 @@ import { isExternalSecretsEnabled } from './ExternalSecrets/externalSecretsHelpe
import { isSourceControlLicensed } from '@/environments/sourceControl/sourceControlHelper.ee';
import { SourceControlService } from '@/environments/sourceControl/sourceControl.service.ee';
import { SourceControlController } from '@/environments/sourceControl/sourceControl.controller.ee';
import { ExecutionRepository } from '@db/repositories';
import { ExecutionRepository, SettingsRepository } from '@db/repositories';
import type { ExecutionEntity } from '@db/entities/ExecutionEntity';
import { TOTPService } from './Mfa/totp.service';
import { MfaService } from './Mfa/mfa.service';
import { handleMfaDisable, isMfaFeatureEnabled } from './Mfa/helpers';
import { JwtService } from './services/jwt.service';
import { RoleService } from './services/role.service';
import { UserService } from './services/user.service';
const exec = promisify(callbackExec);
@ -498,35 +501,50 @@ export class Server extends AbstractServer {
const logger = LoggerProxy;
const internalHooks = Container.get(InternalHooks);
const mailer = Container.get(UserManagementMailer);
const userService = Container.get(UserService);
const jwtService = Container.get(JwtService);
const postHog = this.postHog;
const mfaService = new MfaService(repositories.User, new TOTPService(), encryptionKey);
const controllers: object[] = [
new EventBusController(),
new AuthController({ config, internalHooks, logger, postHog, mfaService }),
new OwnerController({ config, internalHooks, repositories, logger, postHog }),
new MeController({ externalHooks, internalHooks, logger }),
new NodeTypesController({ config, nodeTypes }),
new PasswordResetController({
new AuthController(config, logger, internalHooks, mfaService, userService, postHog),
new OwnerController(
config,
logger,
internalHooks,
Container.get(SettingsRepository),
userService,
postHog,
),
new MeController(logger, externalHooks, internalHooks, userService),
new NodeTypesController(config, nodeTypes),
new PasswordResetController(
config,
logger,
externalHooks,
internalHooks,
mailer,
logger,
userService,
jwtService,
mfaService,
}),
),
Container.get(TagsController),
new TranslationController(config, this.credentialTypes),
new UsersController({
new UsersController(
config,
mailer,
logger,
externalHooks,
internalHooks,
repositories,
repositories.SharedCredentials,
repositories.SharedWorkflow,
activeWorkflowRunner,
logger,
mailer,
jwtService,
Container.get(RoleService),
userService,
postHog,
}),
),
Container.get(SamlController),
Container.get(SourceControlController),
Container.get(WorkflowStatisticsController),

View file

@ -12,13 +12,14 @@ import { sanitizeUser, withFeatureFlags } from '@/UserManagement/UserManagementH
import { issueCookie, resolveJwt } from '@/auth/jwt';
import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from '@/constants';
import { Request, Response } from 'express';
import type { ILogger } from 'n8n-workflow';
import { ILogger } from 'n8n-workflow';
import type { User } from '@db/entities/User';
import { LoginRequest, UserRequest } from '@/requests';
import type { Config } from '@/config';
import type { PublicUser, IInternalHooksClass, CurrentUser } from '@/Interfaces';
import { Config } from '@/config';
import { IInternalHooksClass } from '@/Interfaces';
import type { PublicUser, CurrentUser } from '@/Interfaces';
import { handleEmailLogin, handleLdapLogin } from '@/auth';
import type { PostHogClient } from '@/posthog';
import { PostHogClient } from '@/posthog';
import {
getCurrentAuthenticationMethod,
isLdapCurrentAuthenticationMethod,
@ -27,42 +28,18 @@ import {
import { InternalHooks } from '../InternalHooks';
import { License } from '@/License';
import { UserService } from '@/services/user.service';
import type { MfaService } from '@/Mfa/mfa.service';
import { MfaService } from '@/Mfa/mfa.service';
@RestController()
export class AuthController {
private readonly config: Config;
private readonly logger: ILogger;
private readonly internalHooks: IInternalHooksClass;
private readonly userService: UserService;
private readonly postHog?: PostHogClient;
private readonly mfaService: MfaService;
constructor({
config,
logger,
internalHooks,
postHog,
mfaService,
}: {
config: Config;
logger: ILogger;
internalHooks: IInternalHooksClass;
postHog?: PostHogClient;
mfaService: MfaService;
}) {
this.config = config;
this.logger = logger;
this.internalHooks = internalHooks;
this.postHog = postHog;
this.userService = Container.get(UserService);
this.mfaService = mfaService;
}
constructor(
private readonly config: Config,
private readonly logger: ILogger,
private readonly internalHooks: IInternalHooksClass,
private readonly mfaService: MfaService,
private readonly userService: UserService,
private readonly postHog?: PostHogClient,
) {}
/**
* Log in a user.

View file

@ -12,44 +12,28 @@ import { validateEntity } from '@/GenericHelpers';
import { issueCookie } from '@/auth/jwt';
import type { User } from '@db/entities/User';
import { Response } from 'express';
import type { ILogger } from 'n8n-workflow';
import { ILogger } from 'n8n-workflow';
import {
AuthenticatedRequest,
MeRequest,
UserSettingsUpdatePayload,
UserUpdatePayload,
} from '@/requests';
import type { PublicUser, IExternalHooksClass, IInternalHooksClass } from '@/Interfaces';
import { IExternalHooksClass, IInternalHooksClass } from '@/Interfaces';
import type { PublicUser } from '@/Interfaces';
import { randomBytes } from 'crypto';
import { isSamlLicensedAndEnabled } from '../sso/saml/samlHelpers';
import { UserService } from '@/services/user.service';
import Container from 'typedi';
@Authorized()
@RestController('/me')
export class MeController {
private readonly logger: ILogger;
private readonly externalHooks: IExternalHooksClass;
private readonly internalHooks: IInternalHooksClass;
private readonly userService: UserService;
constructor({
logger,
externalHooks,
internalHooks,
}: {
logger: ILogger;
externalHooks: IExternalHooksClass;
internalHooks: IInternalHooksClass;
}) {
this.logger = logger;
this.externalHooks = externalHooks;
this.internalHooks = internalHooks;
this.userService = Container.get(UserService);
}
constructor(
private readonly logger: ILogger,
private readonly externalHooks: IExternalHooksClass,
private readonly internalHooks: IInternalHooksClass,
private readonly userService: UserService,
) {}
/**
* Update the logged-in user's properties, except password.

View file

@ -4,20 +4,16 @@ import { Request } from 'express';
import type { INodeTypeDescription, INodeTypeNameVersion } from 'n8n-workflow';
import { Authorized, Post, RestController } from '@/decorators';
import { getNodeTranslationPath } from '@/TranslationHelpers';
import type { Config } from '@/config';
import type { NodeTypes } from '@/NodeTypes';
import { Config } from '@/config';
import { NodeTypes } from '@/NodeTypes';
@Authorized()
@RestController('/node-types')
export class NodeTypesController {
private readonly config: Config;
private readonly nodeTypes: NodeTypes;
constructor({ config, nodeTypes }: { config: Config; nodeTypes: NodeTypes }) {
this.config = config;
this.nodeTypes = nodeTypes;
}
constructor(
private readonly config: Config,
private readonly nodeTypes: NodeTypes,
) {}
@Post('/')
async getNodeInfo(req: Request) {

View file

@ -10,50 +10,25 @@ import {
} from '@/UserManagement/UserManagementHelper';
import { issueCookie } from '@/auth/jwt';
import { Response } from 'express';
import type { ILogger } from 'n8n-workflow';
import type { Config } from '@/config';
import { ILogger } from 'n8n-workflow';
import { Config } from '@/config';
import { OwnerRequest } from '@/requests';
import type { IDatabaseCollections, IInternalHooksClass } from '@/Interfaces';
import type { SettingsRepository } from '@db/repositories';
import { IInternalHooksClass } from '@/Interfaces';
import { SettingsRepository } from '@db/repositories';
import { PostHogClient } from '@/posthog';
import { UserService } from '@/services/user.service';
import Container from 'typedi';
import type { PostHogClient } from '@/posthog';
@Authorized(['global', 'owner'])
@RestController('/owner')
export class OwnerController {
private readonly config: Config;
private readonly logger: ILogger;
private readonly internalHooks: IInternalHooksClass;
private readonly userService: UserService;
private readonly settingsRepository: SettingsRepository;
private readonly postHog?: PostHogClient;
constructor({
config,
logger,
internalHooks,
repositories,
postHog,
}: {
config: Config;
logger: ILogger;
internalHooks: IInternalHooksClass;
repositories: Pick<IDatabaseCollections, 'Settings'>;
postHog?: PostHogClient;
}) {
this.config = config;
this.logger = logger;
this.internalHooks = internalHooks;
this.userService = Container.get(UserService);
this.settingsRepository = repositories.Settings;
this.postHog = postHog;
}
constructor(
private readonly config: Config,
private readonly logger: ILogger,
private readonly internalHooks: IInternalHooksClass,
private readonly settingsRepository: SettingsRepository,
private readonly userService: UserService,
private readonly postHog?: PostHogClient,
) {}
/**
* Promote a shell into the owner of the n8n instance,

View file

@ -13,13 +13,13 @@ import {
hashPassword,
validatePassword,
} from '@/UserManagement/UserManagementHelper';
import type { UserManagementMailer } from '@/UserManagement/email';
import { UserManagementMailer } from '@/UserManagement/email';
import { Response } from 'express';
import type { ILogger } from 'n8n-workflow';
import type { Config } from '@/config';
import { ILogger } from 'n8n-workflow';
import { Config } from '@/config';
import { PasswordResetRequest } from '@/requests';
import type { IExternalHooksClass, IInternalHooksClass } from '@/Interfaces';
import { IExternalHooksClass, IInternalHooksClass } from '@/Interfaces';
import { issueCookie } from '@/auth/jwt';
import { isLdapEnabled } from '@/Ldap/helpers';
import { isSamlCurrentAuthenticationMethod } from '@/sso/ssoHelpers';
@ -30,50 +30,20 @@ import { RESPONSE_ERROR_MESSAGES } from '@/constants';
import { TokenExpiredError } from 'jsonwebtoken';
import type { JwtPayload } from '@/services/jwt.service';
import { JwtService } from '@/services/jwt.service';
import type { MfaService } from '@/Mfa/mfa.service';
import { MfaService } from '@/Mfa/mfa.service';
@RestController()
export class PasswordResetController {
private readonly config: Config;
private readonly logger: ILogger;
private readonly externalHooks: IExternalHooksClass;
private readonly internalHooks: IInternalHooksClass;
private readonly mailer: UserManagementMailer;
private readonly jwtService: JwtService;
private readonly userService: UserService;
private readonly mfaService: MfaService;
constructor({
config,
logger,
externalHooks,
internalHooks,
mailer,
mfaService,
}: {
config: Config;
logger: ILogger;
externalHooks: IExternalHooksClass;
internalHooks: IInternalHooksClass;
mailer: UserManagementMailer;
mfaService: MfaService;
}) {
this.config = config;
this.logger = logger;
this.externalHooks = externalHooks;
this.internalHooks = internalHooks;
this.mailer = mailer;
this.jwtService = Container.get(JwtService);
this.userService = Container.get(UserService);
this.mfaService = mfaService;
}
constructor(
private readonly config: Config,
private readonly logger: ILogger,
private readonly externalHooks: IExternalHooksClass,
private readonly internalHooks: IInternalHooksClass,
private readonly mailer: UserManagementMailer,
private readonly userService: UserService,
private readonly jwtService: JwtService,
private readonly mfaService: MfaService,
) {}
/**
* Send a password reset email.

View file

@ -1,7 +1,6 @@
import validator from 'validator';
import { In } from 'typeorm';
import type { ILogger } from 'n8n-workflow';
import { ErrorReporterProxy as ErrorReporter } from 'n8n-workflow';
import { ILogger, ErrorReporterProxy as ErrorReporter } from 'n8n-workflow';
import { User } from '@db/entities/User';
import { SharedCredentials } from '@db/entities/SharedCredentials';
import { SharedWorkflow } from '@db/entities/SharedWorkflow';
@ -24,21 +23,16 @@ import {
UnauthorizedError,
} from '@/ResponseHelper';
import { Response } from 'express';
import type { Config } from '@/config';
import { Config } from '@/config';
import { UserRequest, UserSettingsUpdatePayload } from '@/requests';
import type { UserManagementMailer } from '@/UserManagement/email';
import type {
PublicUser,
IDatabaseCollections,
IExternalHooksClass,
IInternalHooksClass,
ITelemetryUserDeletionData,
} from '@/Interfaces';
import type { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import { UserManagementMailer } from '@/UserManagement/email';
import { IExternalHooksClass, IInternalHooksClass } from '@/Interfaces';
import type { PublicUser, ITelemetryUserDeletionData } from '@/Interfaces';
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import { AuthIdentity } from '@db/entities/AuthIdentity';
import type { PostHogClient } from '@/posthog';
import { PostHogClient } from '@/posthog';
import { isSamlLicensedAndEnabled } from '../sso/saml/samlHelpers';
import type { SharedCredentialsRepository, SharedWorkflowRepository } from '@db/repositories';
import { SharedCredentialsRepository, SharedWorkflowRepository } from '@db/repositories';
import { plainToInstance } from 'class-transformer';
import { License } from '@/License';
import { Container } from 'typedi';
@ -50,62 +44,20 @@ import { UserService } from '@/services/user.service';
@Authorized(['global', 'owner'])
@RestController('/users')
export class UsersController {
private config: Config;
private logger: ILogger;
private externalHooks: IExternalHooksClass;
private internalHooks: IInternalHooksClass;
private sharedCredentialsRepository: SharedCredentialsRepository;
private sharedWorkflowRepository: SharedWorkflowRepository;
private activeWorkflowRunner: ActiveWorkflowRunner;
private mailer: UserManagementMailer;
private jwtService: JwtService;
private postHog?: PostHogClient;
private roleService: RoleService;
private userService: UserService;
constructor({
config,
logger,
externalHooks,
internalHooks,
repositories,
activeWorkflowRunner,
mailer,
postHog,
}: {
config: Config;
logger: ILogger;
externalHooks: IExternalHooksClass;
internalHooks: IInternalHooksClass;
repositories: Pick<IDatabaseCollections, 'SharedCredentials' | 'SharedWorkflow'>;
activeWorkflowRunner: ActiveWorkflowRunner;
mailer: UserManagementMailer;
postHog?: PostHogClient;
}) {
this.config = config;
this.logger = logger;
this.externalHooks = externalHooks;
this.internalHooks = internalHooks;
this.sharedCredentialsRepository = repositories.SharedCredentials;
this.sharedWorkflowRepository = repositories.SharedWorkflow;
this.activeWorkflowRunner = activeWorkflowRunner;
this.mailer = mailer;
this.jwtService = Container.get(JwtService);
this.postHog = postHog;
this.roleService = Container.get(RoleService);
this.userService = Container.get(UserService);
}
constructor(
private readonly config: Config,
private readonly logger: ILogger,
private readonly externalHooks: IExternalHooksClass,
private readonly internalHooks: IInternalHooksClass,
private readonly sharedCredentialsRepository: SharedCredentialsRepository,
private readonly sharedWorkflowRepository: SharedWorkflowRepository,
private readonly activeWorkflowRunner: ActiveWorkflowRunner,
private readonly mailer: UserManagementMailer,
private readonly jwtService: JwtService,
private readonly roleService: RoleService,
private readonly userService: UserService,
private readonly postHog?: PostHogClient,
) {}
/**
* Send email invite(s) to one or multiple users and create user shell(s).

View file

@ -55,6 +55,14 @@ import { MfaService } from '@/Mfa/mfa.service';
import { TOTPService } from '@/Mfa/totp.service';
import { UserSettings } from 'n8n-core';
import { MetricsService } from '@/services/metrics.service';
import {
SettingsRepository,
SharedCredentialsRepository,
SharedWorkflowRepository,
} from '@/databases/repositories';
import { JwtService } from '@/services/jwt.service';
import { RoleService } from '@/services/role.service';
import { UserService } from '@/services/user.service';
/**
* Plugin to prefix a path segment into a request URL pathname.
@ -189,6 +197,7 @@ export const setupTestServer = ({
const internalHooks = Container.get(InternalHooks);
const mailer = Container.get(UserManagementMailer);
const mfaService = new MfaService(repositories.User, new TOTPService(), encryptionKey);
const userService = Container.get(UserService);
for (const group of functionEndpoints) {
switch (group) {
@ -202,7 +211,7 @@ export const setupTestServer = ({
registerController(
app,
config,
new AuthController({ config, logger, internalHooks, repositories, mfaService }),
new AuthController(config, logger, internalHooks, mfaService, userService),
);
break;
case 'mfa':
@ -235,52 +244,55 @@ export const setupTestServer = ({
registerController(
app,
config,
new MeController({
logger,
externalHooks,
internalHooks,
}),
new MeController(logger, externalHooks, internalHooks, userService),
);
break;
case 'passwordReset':
registerController(
app,
config,
new PasswordResetController({
new PasswordResetController(
config,
logger,
externalHooks,
internalHooks,
mailer,
userService,
Container.get(JwtService),
mfaService,
}),
),
);
break;
case 'owner':
registerController(
app,
config,
new OwnerController({
new OwnerController(
config,
logger,
internalHooks,
repositories,
}),
Container.get(SettingsRepository),
userService,
),
);
break;
case 'users':
registerController(
app,
config,
new UsersController({
new UsersController(
config,
mailer,
logger,
externalHooks,
internalHooks,
repositories,
activeWorkflowRunner: Container.get(ActiveWorkflowRunner),
logger,
}),
Container.get(SharedCredentialsRepository),
Container.get(SharedWorkflowRepository),
Container.get(ActiveWorkflowRunner),
mailer,
Container.get(JwtService),
Container.get(RoleService),
userService,
),
);
break;
case 'tags':

View file

@ -9,20 +9,14 @@ import { AUTH_COOKIE_NAME } from '@/constants';
import { BadRequestError } from '@/ResponseHelper';
import type { AuthenticatedRequest, MeRequest } from '@/requests';
import { badPasswords } from '../shared/testData';
import { UserService } from '@/services/user.service';
import Container from 'typedi';
import type { UserService } from '@/services/user.service';
describe('MeController', () => {
const logger = mock<ILogger>();
const externalHooks = mock<IExternalHooksClass>();
const internalHooks = mock<IInternalHooksClass>();
const userService = mock<UserService>();
Container.set(UserService, userService);
const controller = new MeController({
logger,
externalHooks,
internalHooks,
});
const controller = new MeController(logger, externalHooks, internalHooks, userService);
describe('updateCurrentUser', () => {
it('should throw BadRequestError if email is missing in the payload', async () => {

View file

@ -12,7 +12,6 @@ import { OwnerController } from '@/controllers';
import { badPasswords } from '../shared/testData';
import { AUTH_COOKIE_NAME } from '@/constants';
import { UserService } from '@/services/user.service';
import Container from 'typedi';
import { mockInstance } from '../../integration/shared/utils';
describe('OwnerController', () => {
@ -20,16 +19,14 @@ describe('OwnerController', () => {
const logger = mock<ILogger>();
const internalHooks = mock<IInternalHooksClass>();
const userService = mockInstance(UserService);
Container.set(UserService, userService);
const settingsRepository = mock<SettingsRepository>();
const controller = new OwnerController({
const controller = new OwnerController(
config,
logger,
internalHooks,
repositories: {
Settings: settingsRepository,
},
});
settingsRepository,
userService,
);
describe('setupOwner', () => {
it('should throw a BadRequestError if the instance owner is already setup', async () => {