Skip to content
/ qp-n8n Public
forked from n8n-io/n8n

Commit

Permalink
refactor(core): Use injectable classes for db repositories (part-1) (…
Browse files Browse the repository at this point in the history
…no-changelog) (n8n-io#5953)

Co-authored-by: ricardo <[email protected]>
  • Loading branch information
2 people authored and sunilrr committed Apr 24, 2023
1 parent e3da646 commit d8d6f66
Show file tree
Hide file tree
Showing 67 changed files with 557 additions and 270 deletions.
77 changes: 43 additions & 34 deletions packages/cli/src/Db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,8 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable no-case-declarations */
/* eslint-disable @typescript-eslint/naming-convention */
import type {
DataSourceOptions as ConnectionOptions,
EntityManager,
EntityTarget,
LoggerOptions,
ObjectLiteral,
Repository,
} from 'typeorm';
import { Container } from 'typedi';
import type { DataSourceOptions as ConnectionOptions, EntityManager, LoggerOptions } from 'typeorm';
import { DataSource as Connection } from 'typeorm';
import type { TlsOptions } from 'tls';
import type { DatabaseType, IDatabaseCollections } from '@/Interfaces';
Expand All @@ -25,24 +19,38 @@ import {
getPostgresConnectionOptions,
getSqliteConnectionOptions,
} from '@db/config';
import {
AuthIdentityRepository,
AuthProviderSyncHistoryRepository,
CredentialsRepository,
EventDestinationsRepository,
ExecutionMetadataRepository,
ExecutionRepository,
InstalledNodesRepository,
InstalledPackagesRepository,
RoleRepository,
SettingsRepository,
SharedCredentialsRepository,
SharedWorkflowRepository,
TagRepository,
UserRepository,
WebhookRepository,
WorkflowRepository,
WorkflowStatisticsRepository,
WorkflowTagMappingRepository,
} from '@db/repositories';

export let isInitialized = false;
export const collections = {} as IDatabaseCollections;

export let connection: Connection;
let connection: Connection;

export const getConnection = () => connection!;

export async function transaction<T>(fn: (entityManager: EntityManager) => Promise<T>): Promise<T> {
return connection.transaction(fn);
}

export function linkRepository<Entity extends ObjectLiteral>(
entityClass: EntityTarget<Entity>,
): Repository<Entity> {
return connection.getRepository(entityClass);
}

export function getConnectionOptions(dbType: DatabaseType): ConnectionOptions {
switch (dbType) {
case 'postgresdb':
Expand Down Expand Up @@ -114,6 +122,7 @@ export async function init(
});

connection = new Connection(connectionOptions);
Container.set(Connection, connection);
await connection.initialize();

if (dbType === 'postgresdb') {
Expand Down Expand Up @@ -148,31 +157,31 @@ export async function init(
if (migrations.length === 0) {
await connection.destroy();
connection = new Connection(connectionOptions);
Container.set(Connection, connection);
await connection.initialize();
}
} else {
await connection.runMigrations({ transaction: 'each' });
}

collections.Credentials = linkRepository(entities.CredentialsEntity);
collections.Execution = linkRepository(entities.ExecutionEntity);
collections.Workflow = linkRepository(entities.WorkflowEntity);
collections.Webhook = linkRepository(entities.WebhookEntity);
collections.Tag = linkRepository(entities.TagEntity);
collections.WorkflowTagMapping = linkRepository(entities.WorkflowTagMapping);
collections.Role = linkRepository(entities.Role);
collections.User = linkRepository(entities.User);
collections.AuthIdentity = linkRepository(entities.AuthIdentity);
collections.AuthProviderSyncHistory = linkRepository(entities.AuthProviderSyncHistory);
collections.SharedCredentials = linkRepository(entities.SharedCredentials);
collections.SharedWorkflow = linkRepository(entities.SharedWorkflow);
collections.Settings = linkRepository(entities.Settings);
collections.InstalledPackages = linkRepository(entities.InstalledPackages);
collections.InstalledNodes = linkRepository(entities.InstalledNodes);
collections.WorkflowStatistics = linkRepository(entities.WorkflowStatistics);
collections.ExecutionMetadata = linkRepository(entities.ExecutionMetadata);

collections.EventDestinations = linkRepository(entities.EventDestinations);
collections.AuthIdentity = Container.get(AuthIdentityRepository);
collections.AuthProviderSyncHistory = Container.get(AuthProviderSyncHistoryRepository);
collections.Credentials = Container.get(CredentialsRepository);
collections.EventDestinations = Container.get(EventDestinationsRepository);
collections.Execution = Container.get(ExecutionRepository);
collections.ExecutionMetadata = Container.get(ExecutionMetadataRepository);
collections.InstalledNodes = Container.get(InstalledNodesRepository);
collections.InstalledPackages = Container.get(InstalledPackagesRepository);
collections.Role = Container.get(RoleRepository);
collections.Settings = Container.get(SettingsRepository);
collections.SharedCredentials = Container.get(SharedCredentialsRepository);
collections.SharedWorkflow = Container.get(SharedWorkflowRepository);
collections.Tag = Container.get(TagRepository);
collections.User = Container.get(UserRepository);
collections.Webhook = Container.get(WebhookRepository);
collections.Workflow = Container.get(WorkflowRepository);
collections.WorkflowStatistics = Container.get(WorkflowStatisticsRepository);
collections.WorkflowTagMapping = Container.get(WorkflowTagMappingRepository);

isInitialized = true;

Expand Down
71 changes: 40 additions & 31 deletions packages/cli/src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,35 @@ import type { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import type { WorkflowExecute } from 'n8n-core';

import type PCancelable from 'p-cancelable';
import type { FindOperator, Repository } from 'typeorm';
import type { FindOperator } from 'typeorm';

import type { ChildProcess } from 'child_process';

import type { AuthIdentity, AuthProviderType } from '@db/entities/AuthIdentity';
import type { AuthProviderSyncHistory } from '@db/entities/AuthProviderSyncHistory';
import type { InstalledNodes } from '@db/entities/InstalledNodes';
import type { InstalledPackages } from '@db/entities/InstalledPackages';
import type { AuthProviderType } from '@db/entities/AuthIdentity';
import type { Role } from '@db/entities/Role';
import type { Settings } from '@db/entities/Settings';
import type { SharedCredentials } from '@db/entities/SharedCredentials';
import type { SharedWorkflow } from '@db/entities/SharedWorkflow';
import type { TagEntity } from '@db/entities/TagEntity';
import type { User } from '@db/entities/User';
import type { WebhookEntity } from '@db/entities/WebhookEntity';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import type { WorkflowStatistics } from '@db/entities/WorkflowStatistics';
import type { WorkflowTagMapping } from '@db/entities/WorkflowTagMapping';
import type { EventDestinations } from '@db/entities/MessageEventBusDestinationEntity';
import type { ExecutionMetadata } from '@db/entities/ExecutionMetadata';
import type {
AuthIdentityRepository,
AuthProviderSyncHistoryRepository,
CredentialsRepository,
EventDestinationsRepository,
ExecutionMetadataRepository,
ExecutionRepository,
InstalledNodesRepository,
InstalledPackagesRepository,
RoleRepository,
SettingsRepository,
SharedCredentialsRepository,
SharedWorkflowRepository,
TagRepository,
UserRepository,
WebhookRepository,
WorkflowRepository,
WorkflowStatisticsRepository,
WorkflowTagMappingRepository,
} from '@db/repositories';

export interface IActivationError {
time: number;
Expand All @@ -76,24 +85,24 @@ export interface ICredentialsOverwrite {
}

export interface IDatabaseCollections {
AuthIdentity: Repository<AuthIdentity>;
AuthProviderSyncHistory: Repository<AuthProviderSyncHistory>;
Credentials: Repository<ICredentialsDb>;
Execution: Repository<IExecutionFlattedDb>;
Workflow: Repository<WorkflowEntity>;
Webhook: Repository<WebhookEntity>;
Tag: Repository<TagEntity>;
WorkflowTagMapping: Repository<WorkflowTagMapping>;
Role: Repository<Role>;
User: Repository<User>;
SharedCredentials: Repository<SharedCredentials>;
SharedWorkflow: Repository<SharedWorkflow>;
Settings: Repository<Settings>;
InstalledPackages: Repository<InstalledPackages>;
InstalledNodes: Repository<InstalledNodes>;
WorkflowStatistics: Repository<WorkflowStatistics>;
EventDestinations: Repository<EventDestinations>;
ExecutionMetadata: Repository<ExecutionMetadata>;
AuthIdentity: AuthIdentityRepository;
AuthProviderSyncHistory: AuthProviderSyncHistoryRepository;
Credentials: CredentialsRepository;
EventDestinations: EventDestinationsRepository;
Execution: ExecutionRepository;
ExecutionMetadata: ExecutionMetadataRepository;
InstalledNodes: InstalledNodesRepository;
InstalledPackages: InstalledPackagesRepository;
Role: RoleRepository;
Settings: SettingsRepository;
SharedCredentials: SharedCredentialsRepository;
SharedWorkflow: SharedWorkflowRepository;
Tag: TagRepository;
User: UserRepository;
Webhook: WebhookRepository;
Workflow: WorkflowRepository;
WorkflowStatistics: WorkflowStatisticsRepository;
WorkflowTagMapping: WorkflowTagMappingRepository;
}

// ----------------------------------
Expand Down
11 changes: 7 additions & 4 deletions packages/cli/src/InternalHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ function userToPayload(user: User): {
export class InternalHooks implements IInternalHooksClass {
private instanceId: string;

constructor(private telemetry: Telemetry, private nodeTypes: NodeTypes) {}
constructor(
private telemetry: Telemetry,
private nodeTypes: NodeTypes,
private roleService: RoleService,
) {}

async init(instanceId: string) {
this.instanceId = instanceId;
Expand Down Expand Up @@ -155,7 +159,7 @@ export class InternalHooks implements IInternalHooksClass {

let userRole: 'owner' | 'sharee' | undefined = undefined;
if (user.id && workflow.id) {
const role = await RoleService.getUserRoleForWorkflow(user.id, workflow.id);
const role = await this.roleService.getUserRoleForWorkflow(user.id, workflow.id);
if (role) {
userRole = role.name === 'owner' ? 'owner' : 'sharee';
}
Expand Down Expand Up @@ -342,8 +346,7 @@ export class InternalHooks implements IInternalHooksClass {

let userRole: 'owner' | 'sharee' | undefined = undefined;
if (userId) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const role = await RoleService.getUserRoleForWorkflow(userId, workflow.id);
const role = await this.roleService.getUserRoleForWorkflow(userId, workflow.id);
if (role) {
userRole = role.name === 'owner' ? 'owner' : 'sharee';
}
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/Ldap/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import config from '@/config';
import type { Role } from '@db/entities/Role';
import { User } from '@db/entities/User';
import { AuthIdentity } from '@db/entities/AuthIdentity';
import { RoleRepository } from '@db/repositories';
import type { AuthProviderSyncHistory } from '@db/entities/AuthProviderSyncHistory';
import { isUserManagementEnabled } from '@/UserManagement/UserManagementHelper';
import { LdapManager } from './LdapManager.ee';
Expand Down Expand Up @@ -93,7 +94,7 @@ export const randomPassword = (): string => {
* Return the user role to be assigned to LDAP users
*/
export const getLdapUserRole = async (): Promise<Role> => {
return Db.collections.Role.findOneByOrFail({ scope: 'global', name: 'member' });
return Container.get(RoleRepository).findGlobalMemberRoleOrFail();
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { ICredentialsDb } from '@/Interfaces';
import { CredentialsEntity } from '@db/entities/CredentialsEntity';
import { SharedCredentials } from '@db/entities/SharedCredentials';
import type { User } from '@db/entities/User';
import { RoleRepository } from '@db/repositories';
import { ExternalHooks } from '@/ExternalHooks';
import type { IDependency, IJsonSchema } from '../../../types';
import type { CredentialRequest } from '@/requests';
Expand Down Expand Up @@ -58,10 +59,7 @@ export async function saveCredential(
user: User,
encryptedData: ICredentialsDb,
): Promise<CredentialsEntity> {
const role = await Db.collections.Role.findOneByOrFail({
name: 'owner',
scope: 'credential',
});
const role = await Container.get(RoleRepository).findCredentialOwnerRoleOrFail();

await Container.get(ExternalHooks).run('credentials.create', [encryptedData]);

Expand Down
8 changes: 3 additions & 5 deletions packages/cli/src/PublicApi/v1/handlers/users/users.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Db from '@/Db';
import { Container } from 'typedi';
import { RoleRepository } from '@db/repositories';
import type { Role } from '@db/entities/Role';
import type { User } from '@db/entities/User';

Expand All @@ -7,8 +8,5 @@ export function isInstanceOwner(user: User): boolean {
}

export async function getWorkflowOwnerRole(): Promise<Role> {
return Db.collections.Role.findOneByOrFail({
name: 'owner',
scope: 'workflow',
});
return Container.get(RoleRepository).findWorkflowOwnerRoleOrFail();
}
16 changes: 6 additions & 10 deletions packages/cli/src/UserManagement/UserManagementHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
import { In } from 'typeorm';
import type express from 'express';
import { compare, genSaltSync, hash } from 'bcryptjs';
import Container from 'typedi';
import { Container } from 'typedi';

import * as Db from '@/Db';
import * as ResponseHelper from '@/ResponseHelper';
import type { CurrentUser, PublicUser, WhereClause } from '@/Interfaces';
import type { User } from '@db/entities/User';
import { MAX_PASSWORD_LENGTH, MIN_PASSWORD_LENGTH } from '@db/entities/User';
import type { Role } from '@db/entities/Role';
import { RoleRepository } from '@db/repositories';
import type { AuthenticatedRequest } from '@/requests';
import config from '@/config';
import { getWebhookBaseUrl } from '@/WebhookHelpers';
import { License } from '@/License';
import { RoleService } from '@/role/role.service';
import type { PostHogClient } from '@/posthog';

export async function getWorkflowOwner(workflowId: string): Promise<User> {
const workflowOwnerRole = await RoleService.get({ name: 'owner', scope: 'workflow' });
const workflowOwnerRole = await Container.get(RoleRepository).findWorkflowOwnerRole();

const sharedWorkflow = await Db.collections.SharedWorkflow.findOneOrFail({
where: { workflowId, roleId: workflowOwnerRole?.id ?? undefined },
Expand Down Expand Up @@ -61,13 +61,9 @@ export function isSharingEnabled(): boolean {
}

export async function getRoleId(scope: Role['scope'], name: Role['name']): Promise<Role['id']> {
return Db.collections.Role.findOneOrFail({
select: ['id'],
where: {
name,
scope,
},
}).then((role) => role.id);
return Container.get(RoleRepository)
.findRoleOrFail(scope, name)
.then((role) => role.id);
}

export async function getInstanceOwner(): Promise<User> {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/WaitTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export class WaitTracker {
executionId,
ResponseHelper.flattenExecutionData({
...fullExecutionData,
}),
}) as IExecutionFlattedDb,
);

return {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/WorkflowExecuteAdditionalData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import { PermissionChecker } from './UserManagement/PermissionChecker';
import { WorkflowsService } from './workflows/workflows.services';
import { Container } from 'typedi';
import { InternalHooks } from '@/InternalHooks';
import type { ExecutionMetadata } from './databases/entities/ExecutionMetadata';
import type { ExecutionMetadata } from '@db/entities/ExecutionMetadata';

const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType');

Expand Down
11 changes: 3 additions & 8 deletions packages/cli/src/WorkflowHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { WorkflowRunner } from '@/WorkflowRunner';
import config from '@/config';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import type { User } from '@db/entities/User';
import { RoleRepository } from '@db/repositories';
import { whereClause } from '@/UserManagement/UserManagementHelper';
import omit from 'lodash.omit';
import { PermissionChecker } from './UserManagement/PermissionChecker';
Expand Down Expand Up @@ -389,17 +390,11 @@ export async function isBelowOnboardingThreshold(user: User): Promise<boolean> {
let belowThreshold = true;
const skippedTypes = ['n8n-nodes-base.start', 'n8n-nodes-base.stickyNote'];

const workflowOwnerRoleId = await Db.collections.Role.findOne({
select: ['id'],
where: {
name: 'owner',
scope: 'workflow',
},
}).then((role) => role?.id);
const workflowOwnerRole = await Container.get(RoleRepository).findWorkflowOwnerRole();
const ownedWorkflowsIds = await Db.collections.SharedWorkflow.find({
where: {
userId: user.id,
roleId: workflowOwnerRoleId,
roleId: workflowOwnerRole?.id,
},
select: ['workflowId'],
}).then((ownedWorkflows) => ownedWorkflows.map(({ workflowId }) => workflowId));
Expand Down
Loading

0 comments on commit d8d6f66

Please sign in to comment.