diff --git a/packages/cli/src/Db.ts b/packages/cli/src/Db.ts index ead4d247a..1e10d496f 100644 --- a/packages/cli/src/Db.ts +++ b/packages/cli/src/Db.ts @@ -35,7 +35,6 @@ import { SettingsRepository, SharedCredentialsRepository, SharedWorkflowRepository, - TagRepository, UserRepository, VariablesRepository, WorkflowRepository, @@ -177,7 +176,6 @@ export async function init(testConnectionOptions?: ConnectionOptions): Promise> { Settings: SettingsRepository; SharedCredentials: SharedCredentialsRepository; SharedWorkflow: SharedWorkflowRepository; - Tag: TagRepository; User: UserRepository; Variables: VariablesRepository; Workflow: WorkflowRepository; diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts index 6afe5e891..4c0bde59d 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts @@ -8,6 +8,8 @@ import { WorkflowEntity } from '@db/entities/WorkflowEntity'; import { SharedWorkflow } from '@db/entities/SharedWorkflow'; import type { Role } from '@db/entities/Role'; import config from '@/config'; +import { TagRepository } from '@/databases/repositories'; +import Container from 'typedi'; function insertIf(condition: boolean, elements: string[]): string[] { return condition ? elements : []; @@ -62,7 +64,7 @@ export async function getWorkflowById(id: string): Promise { - const dbTags = await Db.collections.Tag.find({ + const dbTags = await Container.get(TagRepository).find({ where: { name: In(tags) }, relations: ['workflows'], }); diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 16e3c46f5..052ee5732 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -482,7 +482,7 @@ export class Server extends AbstractServer { logger, jwtService, }), - new TagsController({ config, repositories, externalHooks }), + Container.get(TagsController), new TranslationController(config, this.credentialTypes), new UsersController({ config, diff --git a/packages/cli/src/TagHelpers.ts b/packages/cli/src/TagHelpers.ts index b43314923..bbc974194 100644 --- a/packages/cli/src/TagHelpers.ts +++ b/packages/cli/src/TagHelpers.ts @@ -1,6 +1,8 @@ import type { EntityManager } from 'typeorm'; -import { TagEntity } from '@db/entities/TagEntity'; +import type { TagEntity } from '@db/entities/TagEntity'; import type { ITagToImport, IWorkflowToImport } from '@/Interfaces'; +import { TagRepository } from './databases/repositories'; +import Container from 'typedi'; // ---------------------------------- // utils @@ -26,8 +28,7 @@ export function sortByRequestOrder( // ---------------------------------- const createTag = async (transactionManager: EntityManager, name: string): Promise => { - const tag = new TagEntity(); - tag.name = name; + const tag = Container.get(TagRepository).create({ name: name.trim() }); return transactionManager.save(tag); }; diff --git a/packages/cli/src/commands/import/workflow.ts b/packages/cli/src/commands/import/workflow.ts index f3c54f793..2cd8bd2b2 100644 --- a/packages/cli/src/commands/import/workflow.ts +++ b/packages/cli/src/commands/import/workflow.ts @@ -18,6 +18,7 @@ import { replaceInvalidCredentials } from '@/WorkflowHelpers'; import { BaseCommand, UM_FIX_INSTRUCTION } from '../BaseCommand'; import { generateNanoId } from '@db/utils/generators'; import { RoleService } from '@/services/role.service'; +import { TagRepository } from '@/databases/repositories'; function assertHasWorkflowsToImport(workflows: unknown): asserts workflows is IWorkflowToImport[] { if (!Array.isArray(workflows)) { @@ -92,7 +93,7 @@ export class ImportWorkflowsCommand extends BaseCommand { const user = flags.userId ? await this.getAssignee(flags.userId) : await this.getOwner(); const credentials = await Db.collections.Credentials.find(); - const tags = await Db.collections.Tag.find(); + const tags = await Container.get(TagRepository).find(); let totalImported = 0; diff --git a/packages/cli/src/controllers/tags.controller.ts b/packages/cli/src/controllers/tags.controller.ts index 856f7df14..1ed0a3a1c 100644 --- a/packages/cli/src/controllers/tags.controller.ts +++ b/packages/cli/src/controllers/tags.controller.ts @@ -1,35 +1,25 @@ import { Request, Response, NextFunction } from 'express'; -import type { Config } from '@/config'; +import config from '@/config'; import { Authorized, Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators'; -import type { IDatabaseCollections, IExternalHooksClass, ITagWithCountDb } from '@/Interfaces'; -import { TagEntity } from '@db/entities/TagEntity'; -import type { TagRepository } from '@db/repositories'; +import { type ITagWithCountDb } from '@/Interfaces'; +import type { TagEntity } from '@db/entities/TagEntity'; +import { TagRepository } from '@db/repositories'; import { validateEntity } from '@/GenericHelpers'; import { BadRequestError } from '@/ResponseHelper'; import { TagsRequest } from '@/requests'; +import { Service } from 'typedi'; +import { ExternalHooks } from '@/ExternalHooks'; @Authorized() @RestController('/tags') +@Service() export class TagsController { - private config: Config; + private config = config; - private externalHooks: IExternalHooksClass; - - private tagsRepository: TagRepository; - - constructor({ - config, - externalHooks, - repositories, - }: { - config: Config; - externalHooks: IExternalHooksClass; - repositories: Pick; - }) { - this.config = config; - this.externalHooks = externalHooks; - this.tagsRepository = repositories.Tag; - } + constructor( + private tagsRepository: TagRepository, + private externalHooks: ExternalHooks, + ) {} // TODO: move this into a new decorator `@IfEnabled('workflowTagsDisabled')` @Middleware() @@ -63,8 +53,7 @@ export class TagsController { // Creates a tag @Post('/') async createTag(req: TagsRequest.Create): Promise { - const newTag = new TagEntity(); - newTag.name = req.body.name.trim(); + const newTag = this.tagsRepository.create({ name: req.body.name.trim() }); await this.externalHooks.run('tag.beforeCreate', [newTag]); await validateEntity(newTag); @@ -77,12 +66,7 @@ export class TagsController { // Updates a tag @Patch('/:id(\\w+)') async updateTag(req: TagsRequest.Update): Promise { - const { name } = req.body; - const { id } = req.params; - - const newTag = new TagEntity(); - newTag.id = id; - newTag.name = name.trim(); + const newTag = this.tagsRepository.create({ id: req.params.id, name: req.body.name.trim() }); await this.externalHooks.run('tag.beforeUpdate', [newTag]); await validateEntity(newTag); diff --git a/packages/cli/src/environments/sourceControl/sourceControl.service.ee.ts b/packages/cli/src/environments/sourceControl/sourceControl.service.ee.ts index f3cc7a833..7cc87e57a 100644 --- a/packages/cli/src/environments/sourceControl/sourceControl.service.ee.ts +++ b/packages/cli/src/environments/sourceControl/sourceControl.service.ee.ts @@ -1,6 +1,5 @@ import Container, { Service } from 'typedi'; import path from 'path'; -import * as Db from '@/Db'; import { getTagsPath, getTrackingInformationFromPostPushResult, @@ -39,6 +38,7 @@ import type { Variables } from '@db/entities/Variables'; import type { SourceControlWorkflowVersionId } from './types/sourceControlWorkflowVersionId'; import type { ExportableCredential } from './types/exportableCredential'; import { InternalHooks } from '@/InternalHooks'; +import { TagRepository } from '@/databases/repositories'; @Service() export class SourceControlService { private sshKeyName: string; @@ -52,6 +52,7 @@ export class SourceControlService { private sourceControlPreferencesService: SourceControlPreferencesService, private sourceControlExportService: SourceControlExportService, private sourceControlImportService: SourceControlImportService, + private tagRepository: TagRepository, ) { const userFolder = UserSettings.getUserN8nFolderPath(); this.sshFolder = path.join(userFolder, SOURCE_CONTROL_SSH_FOLDER); @@ -682,7 +683,7 @@ export class SourceControlService { options: SourceControlGetStatus, sourceControlledFiles: SourceControlledFile[], ) { - const lastUpdatedTag = await Db.collections.Tag.find({ + const lastUpdatedTag = await this.tagRepository.find({ order: { updatedAt: 'DESC' }, take: 1, select: ['updatedAt'], diff --git a/packages/cli/src/environments/sourceControl/sourceControlExport.service.ee.ts b/packages/cli/src/environments/sourceControl/sourceControlExport.service.ee.ts index 76cb8c245..8d53fceae 100644 --- a/packages/cli/src/environments/sourceControl/sourceControlExport.service.ee.ts +++ b/packages/cli/src/environments/sourceControl/sourceControlExport.service.ee.ts @@ -26,6 +26,7 @@ import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import { In } from 'typeorm'; import type { SourceControlledFile } from './types/sourceControlledFile'; import { VariablesService } from '../variables/variables.service'; +import { TagRepository } from '@/databases/repositories'; @Service() export class SourceControlExportService { @@ -35,7 +36,10 @@ export class SourceControlExportService { private credentialExportFolder: string; - constructor(private readonly variablesService: VariablesService) { + constructor( + private readonly variablesService: VariablesService, + private readonly tagRepository: TagRepository, + ) { const userFolder = UserSettings.getUserN8nFolderPath(); this.gitFolder = path.join(userFolder, SOURCE_CONTROL_GIT_FOLDER); this.workflowExportFolder = path.join(this.gitFolder, SOURCE_CONTROL_WORKFLOW_EXPORT_FOLDER); @@ -167,7 +171,7 @@ export class SourceControlExportService { async exportTagsToWorkFolder(): Promise { try { sourceControlFoldersExistCheck([this.gitFolder]); - const tags = await Db.collections.Tag.find(); + const tags = await this.tagRepository.find(); // do not export empty tags if (tags.length === 0) { return { diff --git a/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts b/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts index c01a55efa..2bd5881c8 100644 --- a/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts +++ b/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts @@ -27,6 +27,7 @@ import { getCredentialExportPath, getWorkflowExportPath } from './sourceControlH import type { SourceControlledFile } from './types/sourceControlledFile'; import { RoleService } from '@/services/role.service'; import { VariablesService } from '../variables/variables.service'; +import { TagRepository } from '@/databases/repositories'; @Service() export class SourceControlImportService { @@ -39,6 +40,7 @@ export class SourceControlImportService { constructor( private readonly variablesService: VariablesService, private readonly activeWorkflowRunner: ActiveWorkflowRunner, + private readonly tagRepository: TagRepository, ) { const userFolder = UserSettings.getUserN8nFolderPath(); this.gitFolder = path.join(userFolder, SOURCE_CONTROL_GIT_FOLDER); @@ -265,7 +267,7 @@ export class SourceControlImportService { tags: TagEntity[]; mappings: WorkflowTagMapping[]; }> { - const localTags = await Db.collections.Tag.find({ + const localTags = await this.tagRepository.find({ select: ['id', 'name'], }); const localMappings = await Db.collections.WorkflowTagMapping.find({ @@ -481,7 +483,7 @@ export class SourceControlImportService { await Promise.all( mappedTags.tags.map(async (tag) => { - const findByName = await Db.collections.Tag.findOne({ + const findByName = await this.tagRepository.findOne({ where: { name: tag.name }, select: ['id'], }); @@ -490,7 +492,7 @@ export class SourceControlImportService { `A tag with the name ${tag.name} already exists locally.
Please either rename the local tag, or the remote one with the id ${tag.id} in the tags.json file.`, ); } - await Db.collections.Tag.upsert( + await this.tagRepository.upsert( { ...tag, }, diff --git a/packages/cli/src/workflows/workflows.controller.ee.ts b/packages/cli/src/workflows/workflows.controller.ee.ts index 40327c2b2..8332bb63c 100644 --- a/packages/cli/src/workflows/workflows.controller.ee.ts +++ b/packages/cli/src/workflows/workflows.controller.ee.ts @@ -20,6 +20,7 @@ import { In } from 'typeorm'; import { Container } from 'typedi'; import { InternalHooks } from '@/InternalHooks'; import { RoleService } from '@/services/role.service'; +import { TagRepository } from '@/databases/repositories'; // eslint-disable-next-line @typescript-eslint/naming-convention export const EEWorkflowController = express.Router(); @@ -134,7 +135,7 @@ EEWorkflowController.post( const { tags: tagIds } = req.body; if (tagIds?.length && !config.getEnv('workflowTagsDisabled')) { - newWorkflow.tags = await Db.collections.Tag.find({ + newWorkflow.tags = await Container.get(TagRepository).find({ select: ['id', 'name'], where: { id: In(tagIds), diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index b81739a76..83b539ad9 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -24,6 +24,7 @@ import { In } from 'typeorm'; import { Container } from 'typedi'; import { InternalHooks } from '@/InternalHooks'; import { RoleService } from '@/services/role.service'; +import { TagRepository } from '@/databases/repositories'; export const workflowsController = express.Router(); @@ -62,7 +63,7 @@ workflowsController.post( const { tags: tagIds } = req.body; if (tagIds?.length && !config.getEnv('workflowTagsDisabled')) { - newWorkflow.tags = await Db.collections.Tag.find({ + newWorkflow.tags = await Container.get(TagRepository).find({ select: ['id', 'name'], where: { id: In(tagIds), diff --git a/packages/cli/test/integration/shared/testDb.ts b/packages/cli/test/integration/shared/testDb.ts index 695389e64..d6da8cbde 100644 --- a/packages/cli/test/integration/shared/testDb.ts +++ b/packages/cli/test/integration/shared/testDb.ts @@ -35,6 +35,8 @@ import type { ExecutionData } from '@db/entities/ExecutionData'; import { generateNanoId } from '@db/utils/generators'; import { RoleService } from '@/services/role.service'; import { VariablesService } from '@/environments/variables/variables.service'; +import { TagRepository } from '@/databases/repositories'; +import { separate } from '@/utils'; export type TestDBType = 'postgres' | 'mysql'; @@ -113,7 +115,13 @@ export async function terminate() { * Truncate specific DB tables in a test DB. */ export async function truncate(collections: CollectionName[]) { - for (const collection of collections) { + const [tag, rest] = separate(collections, (c) => c === 'Tag'); + + if (tag) { + await Container.get(TagRepository).delete({}); + } + + for (const collection of rest) { await Db.collections[collection].delete({}); } } @@ -384,7 +392,7 @@ export async function createWaitingExecution(workflow: WorkflowEntity) { export async function createTag(attributes: Partial = {}) { const { name } = attributes; - return Db.collections.Tag.save({ + return Container.get(TagRepository).save({ id: generateNanoId(), name: name ?? randomName(), ...attributes, diff --git a/packages/cli/test/integration/shared/utils/testServer.ts b/packages/cli/test/integration/shared/utils/testServer.ts index 1e6704be6..5ef7f8926 100644 --- a/packages/cli/test/integration/shared/utils/testServer.ts +++ b/packages/cli/test/integration/shared/utils/testServer.ts @@ -272,11 +272,7 @@ export const setupTestServer = ({ ); break; case 'tags': - registerController( - app, - config, - new TagsController({ config, externalHooks, repositories }), - ); + registerController(app, config, Container.get(TagsController)); break; } } diff --git a/packages/cli/test/integration/tags.api.test.ts b/packages/cli/test/integration/tags.api.test.ts index a8aa61a8b..eabe0d931 100644 --- a/packages/cli/test/integration/tags.api.test.ts +++ b/packages/cli/test/integration/tags.api.test.ts @@ -1,7 +1,8 @@ -import * as Db from '@/Db'; import * as utils from './shared/utils/'; import * as testDb from './shared/testDb'; import type { SuperAgentTest } from 'supertest'; +import { TagRepository } from '@/databases/repositories'; +import Container from 'typedi'; let authOwnerAgent: SuperAgentTest; const testServer = utils.setupTestServer({ endpointGroups: ['tags'] }); @@ -21,18 +22,18 @@ describe('POST /tags', () => { const resp = await authOwnerAgent.post('/tags').send({ name: 'test' }); expect(resp.statusCode).toBe(200); - const dbTag = await Db.collections.Tag.findBy({ name: 'test' }); + const dbTag = await Container.get(TagRepository).findBy({ name: 'test' }); expect(dbTag.length === 1); }); test('should not create duplicate tag', async () => { - const newTag = Db.collections.Tag.create({ name: 'test' }); - await Db.collections.Tag.save(newTag); + const newTag = Container.get(TagRepository).create({ name: 'test' }); + await Container.get(TagRepository).save(newTag); const resp = await authOwnerAgent.post('/tags').send({ name: 'test' }); expect(resp.status).toBe(500); - const dbTag = await Db.collections.Tag.findBy({ name: 'test' }); + const dbTag = await Container.get(TagRepository).findBy({ name: 'test' }); expect(dbTag.length).toBe(1); }); });