refactor(core): Use injectable classes for db repositories (part-1) (no-changelog) (#5953)
Co-authored-by: ricardo <ricardoespinoza105@gmail.com>
This commit is contained in:
committed by
GitHub
parent
323e26acfd
commit
10f8c35dbb
@@ -3,7 +3,7 @@ import config from '@/config';
|
||||
import axios from 'axios';
|
||||
import syslog from 'syslog-client';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import Container from 'typedi';
|
||||
import { Container } from 'typedi';
|
||||
import type { SuperAgentTest } from 'supertest';
|
||||
import * as utils from './shared/utils';
|
||||
import * as testDb from './shared/testDb';
|
||||
@@ -112,7 +112,6 @@ beforeAll(async () => {
|
||||
|
||||
afterAll(async () => {
|
||||
jest.mock('@/eventbus/MessageEventBus/MessageEventBus');
|
||||
Container.reset();
|
||||
await testDb.terminate();
|
||||
await eventBus.close();
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import express from 'express';
|
||||
import type { Entry as LdapUser } from 'ldapts';
|
||||
import { Not } from 'typeorm';
|
||||
import Container from 'typedi';
|
||||
import { Container } from 'typedi';
|
||||
import { jsonParse } from 'n8n-workflow';
|
||||
import config from '@/config';
|
||||
import * as Db from '@/Db';
|
||||
@@ -83,7 +83,6 @@ beforeEach(async () => {
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
Container.reset();
|
||||
await testDb.terminate();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Container from 'typedi';
|
||||
import { Container } from 'typedi';
|
||||
import type { SuperAgentTest } from 'supertest';
|
||||
import type { User } from '@db/entities/User';
|
||||
import { setSamlLoginEnabled } from '@/sso/saml/samlHelpers';
|
||||
@@ -24,7 +24,6 @@ beforeAll(async () => {
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
Container.reset();
|
||||
await testDb.terminate();
|
||||
});
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ export function randomApiKey() {
|
||||
|
||||
const chooseRandomly = <T>(array: T[]) => array[Math.floor(Math.random() * array.length)];
|
||||
|
||||
export const randomInteger = (max = 1000) => Math.floor(Math.random() * max);
|
||||
|
||||
export const randomDigit = () => Math.floor(Math.random() * 10);
|
||||
|
||||
export const randomPositiveDigit = (): number => {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { UserSettings } from 'n8n-core';
|
||||
import {
|
||||
DataSource as Connection,
|
||||
DataSourceOptions as ConnectionOptions,
|
||||
Repository,
|
||||
} from 'typeorm';
|
||||
import { DataSource as Connection, DataSourceOptions as ConnectionOptions } from 'typeorm';
|
||||
import { Container } from 'typedi';
|
||||
|
||||
import config from '@/config';
|
||||
import * as Db from '@/Db';
|
||||
@@ -14,7 +11,7 @@ import { mysqlMigrations } from '@db/migrations/mysqldb';
|
||||
import { postgresMigrations } from '@db/migrations/postgresdb';
|
||||
import { sqliteMigrations } from '@db/migrations/sqlite';
|
||||
import { hashPassword } from '@/UserManagement/UserManagementHelper';
|
||||
import { AuthIdentity } from '@/databases/entities/AuthIdentity';
|
||||
import { AuthIdentity } from '@db/entities/AuthIdentity';
|
||||
import type { ExecutionEntity } from '@db/entities/ExecutionEntity';
|
||||
import { InstalledNodes } from '@db/entities/InstalledNodes';
|
||||
import { InstalledPackages } from '@db/entities/InstalledPackages';
|
||||
@@ -22,6 +19,7 @@ import type { Role } from '@db/entities/Role';
|
||||
import type { TagEntity } from '@db/entities/TagEntity';
|
||||
import type { User } from '@db/entities/User';
|
||||
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
|
||||
import { RoleRepository } from '@db/repositories';
|
||||
import { ICredentialsDb } from '@/Interfaces';
|
||||
|
||||
import { DB_INITIALIZATION_TIMEOUT } from './constants';
|
||||
@@ -104,8 +102,7 @@ export async function terminate() {
|
||||
*/
|
||||
export async function truncate(collections: CollectionName[]) {
|
||||
for (const collection of collections) {
|
||||
const repository: Repository<any> = Db.collections[collection];
|
||||
await repository.delete({});
|
||||
await Db.collections[collection].clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +139,7 @@ export async function saveCredential(
|
||||
}
|
||||
|
||||
export async function shareCredentialWithUsers(credential: CredentialsEntity, users: User[]) {
|
||||
const role = await Db.collections.Role.findOneBy({ scope: 'credential', name: 'user' });
|
||||
const role = await Container.get(RoleRepository).findCredentialUserRole();
|
||||
const newSharedCredentials = users.map((user) =>
|
||||
Db.collections.SharedCredentials.create({
|
||||
userId: user.id,
|
||||
@@ -266,38 +263,23 @@ export async function addApiKey(user: User): Promise<User> {
|
||||
// ----------------------------------
|
||||
|
||||
export async function getGlobalOwnerRole() {
|
||||
return Db.collections.Role.findOneByOrFail({
|
||||
name: 'owner',
|
||||
scope: 'global',
|
||||
});
|
||||
return Container.get(RoleRepository).findGlobalOwnerRoleOrFail();
|
||||
}
|
||||
|
||||
export async function getGlobalMemberRole() {
|
||||
return Db.collections.Role.findOneByOrFail({
|
||||
name: 'member',
|
||||
scope: 'global',
|
||||
});
|
||||
return Container.get(RoleRepository).findGlobalMemberRoleOrFail();
|
||||
}
|
||||
|
||||
export async function getWorkflowOwnerRole() {
|
||||
return Db.collections.Role.findOneByOrFail({
|
||||
name: 'owner',
|
||||
scope: 'workflow',
|
||||
});
|
||||
return Container.get(RoleRepository).findWorkflowOwnerRoleOrFail();
|
||||
}
|
||||
|
||||
export async function getWorkflowEditorRole() {
|
||||
return Db.collections.Role.findOneByOrFail({
|
||||
name: 'editor',
|
||||
scope: 'workflow',
|
||||
});
|
||||
return Container.get(RoleRepository).findWorkflowEditorRoleOrFail();
|
||||
}
|
||||
|
||||
export async function getCredentialOwnerRole() {
|
||||
return Db.collections.Role.findOneByOrFail({
|
||||
name: 'owner',
|
||||
scope: 'credential',
|
||||
});
|
||||
return Container.get(RoleRepository).findCredentialOwnerRoleOrFail();
|
||||
}
|
||||
|
||||
export async function getAllRoles() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Container from 'typedi';
|
||||
import { Container } from 'typedi';
|
||||
import type { SuperAgentTest } from 'supertest';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import type { INode } from 'n8n-workflow';
|
||||
@@ -51,7 +51,6 @@ beforeEach(async () => {
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
Container.reset();
|
||||
await testDb.terminate();
|
||||
});
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@ import {
|
||||
|
||||
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
||||
import * as Db from '@/Db';
|
||||
import { WorkflowEntity } from '@/databases/entities/WorkflowEntity';
|
||||
import { SharedWorkflow } from '@/databases/entities/SharedWorkflow';
|
||||
import { Role } from '@/databases/entities/Role';
|
||||
import { User } from '@/databases/entities/User';
|
||||
import { WorkflowEntity } from '@db/entities/WorkflowEntity';
|
||||
import { SharedWorkflow } from '@db/entities/SharedWorkflow';
|
||||
import { Role } from '@db/entities/Role';
|
||||
import { User } from '@db/entities/User';
|
||||
import { getLogger } from '@/Logger';
|
||||
import { randomEmail, randomName } from '../integration/shared/random';
|
||||
import * as Helpers from './Helpers';
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { IRun, LoggerProxy, WorkflowExecuteMode } from 'n8n-workflow';
|
||||
import { QueryFailedError, Repository } from 'typeorm';
|
||||
import { QueryFailedError } from 'typeorm';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
|
||||
import config from '@/config';
|
||||
import * as Db from '@/Db';
|
||||
import { User } from '@db/entities/User';
|
||||
import { WorkflowStatistics } from '@db/entities/WorkflowStatistics';
|
||||
import { WorkflowStatisticsRepository } from '@db/repositories';
|
||||
import { nodeFetchedData, workflowExecutionCompleted } from '@/events/WorkflowStatistics';
|
||||
import * as UserManagementHelper from '@/UserManagement/UserManagementHelper';
|
||||
import { getLogger } from '@/Logger';
|
||||
@@ -14,7 +15,6 @@ import { InternalHooks } from '@/InternalHooks';
|
||||
import { mockInstance } from '../integration/shared/utils';
|
||||
import { UserService } from '@/user/user.service';
|
||||
|
||||
type WorkflowStatisticsRepository = Repository<WorkflowStatistics>;
|
||||
jest.mock('@/Db', () => {
|
||||
return {
|
||||
collections: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { mock, anyObject, captor } from 'jest-mock-extended';
|
||||
import type { ILogger } from 'n8n-workflow';
|
||||
import type { IExternalHooksClass, IInternalHooksClass } from '@/Interfaces';
|
||||
import type { User } from '@db/entities/User';
|
||||
import { UserRepository } from '@db/repositories';
|
||||
import { MeController } from '@/controllers';
|
||||
import { AUTH_COOKIE_NAME } from '@/constants';
|
||||
import { BadRequestError } from '@/ResponseHelper';
|
||||
@@ -15,7 +16,7 @@ describe('MeController', () => {
|
||||
const logger = mock<ILogger>();
|
||||
const externalHooks = mock<IExternalHooksClass>();
|
||||
const internalHooks = mock<IInternalHooksClass>();
|
||||
const userRepository = mock<Repository<User>>();
|
||||
const userRepository = mock<UserRepository>();
|
||||
const controller = new MeController({
|
||||
logger,
|
||||
externalHooks,
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import type { Repository } from 'typeorm';
|
||||
import type { CookieOptions, Response } from 'express';
|
||||
import { anyObject, captor, mock } from 'jest-mock-extended';
|
||||
import type { ILogger } from 'n8n-workflow';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import type { ICredentialsDb, IInternalHooksClass } from '@/Interfaces';
|
||||
import type { IInternalHooksClass } from '@/Interfaces';
|
||||
import type { User } from '@db/entities/User';
|
||||
import type { Settings } from '@db/entities/Settings';
|
||||
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
|
||||
import type {
|
||||
CredentialsRepository,
|
||||
SettingsRepository,
|
||||
UserRepository,
|
||||
WorkflowRepository,
|
||||
} from '@db/repositories';
|
||||
import type { Config } from '@/config';
|
||||
import { BadRequestError } from '@/ResponseHelper';
|
||||
import type { OwnerRequest } from '@/requests';
|
||||
@@ -18,10 +21,10 @@ describe('OwnerController', () => {
|
||||
const config = mock<Config>();
|
||||
const logger = mock<ILogger>();
|
||||
const internalHooks = mock<IInternalHooksClass>();
|
||||
const userRepository = mock<Repository<User>>();
|
||||
const settingsRepository = mock<Repository<Settings>>();
|
||||
const credentialsRepository = mock<Repository<ICredentialsDb>>();
|
||||
const workflowsRepository = mock<Repository<WorkflowEntity>>();
|
||||
const userRepository = mock<UserRepository>();
|
||||
const settingsRepository = mock<SettingsRepository>();
|
||||
const credentialsRepository = mock<CredentialsRepository>();
|
||||
const workflowsRepository = mock<WorkflowRepository>();
|
||||
const controller = new OwnerController({
|
||||
config,
|
||||
logger,
|
||||
|
||||
47
packages/cli/test/unit/repositories/role.repository.test.ts
Normal file
47
packages/cli/test/unit/repositories/role.repository.test.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Container } from 'typedi';
|
||||
import { DataSource, EntityManager } from 'typeorm';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { Role, RoleNames, RoleScopes } from '@db/entities/Role';
|
||||
import { RoleRepository } from '@db/repositories/role.repository';
|
||||
import { mockInstance } from '../../integration/shared/utils';
|
||||
import { randomInteger } from '../../integration/shared/random';
|
||||
|
||||
describe('RoleRepository', () => {
|
||||
const entityManager = mockInstance(EntityManager);
|
||||
const dataSource = mockInstance(DataSource, { manager: entityManager });
|
||||
dataSource.getMetadata.mockReturnValue(mock());
|
||||
Object.assign(entityManager, { connection: dataSource });
|
||||
const roleRepository = Container.get(RoleRepository);
|
||||
|
||||
describe('findRole', () => {
|
||||
test('should return the role when present', async () => {
|
||||
entityManager.findOne.mockResolvedValueOnce(createRole('global', 'owner'));
|
||||
const role = await roleRepository.findRole('global', 'owner');
|
||||
expect(role?.name).toEqual('owner');
|
||||
expect(role?.scope).toEqual('global');
|
||||
});
|
||||
|
||||
test('should return null otherwise', async () => {
|
||||
entityManager.findOne.mockResolvedValueOnce(null);
|
||||
const role = await roleRepository.findRole('global', 'owner');
|
||||
expect(role).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findRoleOrFail', () => {
|
||||
test('should return the role when present', async () => {
|
||||
entityManager.findOneOrFail.mockResolvedValueOnce(createRole('global', 'owner'));
|
||||
const role = await roleRepository.findRoleOrFail('global', 'owner');
|
||||
expect(role?.name).toEqual('owner');
|
||||
expect(role?.scope).toEqual('global');
|
||||
});
|
||||
|
||||
test('should throw otherwise', async () => {
|
||||
entityManager.findOneOrFail.mockRejectedValueOnce(new Error());
|
||||
expect(() => roleRepository.findRoleOrFail('global', 'owner')).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
const createRole = (scope: RoleScopes, name: RoleNames) =>
|
||||
Object.assign(new Role(), { name, scope, id: `${randomInteger()}` });
|
||||
});
|
||||
28
packages/cli/test/unit/services/role.service.test.ts
Normal file
28
packages/cli/test/unit/services/role.service.test.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
|
||||
import { Role } from '@db/entities/Role';
|
||||
import { SharedWorkflow } from '@db/entities/SharedWorkflow';
|
||||
import { RoleService } from '@/role/role.service';
|
||||
import { mockInstance } from '../../integration/shared/utils';
|
||||
|
||||
describe('RoleService', () => {
|
||||
const sharedWorkflowRepository = mockInstance(SharedWorkflowRepository);
|
||||
const roleService = new RoleService(sharedWorkflowRepository);
|
||||
|
||||
const userId = '1';
|
||||
const workflowId = '42';
|
||||
|
||||
describe('getUserRoleForWorkflow', () => {
|
||||
test('should return the role if a shared workflow is found', async () => {
|
||||
const sharedWorkflow = Object.assign(new SharedWorkflow(), { role: new Role() });
|
||||
sharedWorkflowRepository.findOne.mockResolvedValueOnce(sharedWorkflow);
|
||||
const role = await roleService.getUserRoleForWorkflow(userId, workflowId);
|
||||
expect(role).toBe(sharedWorkflow.role);
|
||||
});
|
||||
|
||||
test('should return undefined if no shared workflow is found', async () => {
|
||||
sharedWorkflowRepository.findOne.mockResolvedValueOnce(null);
|
||||
const role = await roleService.getUserRoleForWorkflow(userId, workflowId);
|
||||
expect(role).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user