mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
refactor(core): Move tag collection into repository (no-changelog) (#6860)
* refactor(core): Move tag collection into repository * Fix tests * Address feedback * Fix missing spot
This commit is contained in:
parent
8de28fe4d0
commit
11440bfd3c
|
@ -35,7 +35,6 @@ import {
|
|||
SettingsRepository,
|
||||
SharedCredentialsRepository,
|
||||
SharedWorkflowRepository,
|
||||
TagRepository,
|
||||
UserRepository,
|
||||
VariablesRepository,
|
||||
WorkflowRepository,
|
||||
|
@ -177,7 +176,6 @@ export async function init(testConnectionOptions?: ConnectionOptions): Promise<v
|
|||
collections.InstalledPackages = Container.get(InstalledPackagesRepository);
|
||||
collections.SharedCredentials = Container.get(SharedCredentialsRepository);
|
||||
collections.SharedWorkflow = Container.get(SharedWorkflowRepository);
|
||||
collections.Tag = Container.get(TagRepository);
|
||||
collections.Variables = Container.get(VariablesRepository);
|
||||
collections.WorkflowStatistics = Container.get(WorkflowStatisticsRepository);
|
||||
collections.WorkflowTagMapping = Container.get(WorkflowTagMappingRepository);
|
||||
|
|
|
@ -54,7 +54,6 @@ import type {
|
|||
SettingsRepository,
|
||||
SharedCredentialsRepository,
|
||||
SharedWorkflowRepository,
|
||||
TagRepository,
|
||||
UserRepository,
|
||||
VariablesRepository,
|
||||
WorkflowRepository,
|
||||
|
@ -100,7 +99,6 @@ export interface IDatabaseCollections extends Record<string, Repository<any>> {
|
|||
Settings: SettingsRepository;
|
||||
SharedCredentials: SharedCredentialsRepository;
|
||||
SharedWorkflow: SharedWorkflowRepository;
|
||||
Tag: TagRepository;
|
||||
User: UserRepository;
|
||||
Variables: VariablesRepository;
|
||||
Workflow: WorkflowRepository;
|
||||
|
|
|
@ -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<WorkflowEntity | null
|
|||
* Intersection! e.g. workflow needs to have all provided tags.
|
||||
*/
|
||||
export async function getWorkflowIdsViaTags(tags: string[]): Promise<string[]> {
|
||||
const dbTags = await Db.collections.Tag.find({
|
||||
const dbTags = await Container.get(TagRepository).find({
|
||||
where: { name: In(tags) },
|
||||
relations: ['workflows'],
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<TagEntity> => {
|
||||
const tag = new TagEntity();
|
||||
tag.name = name;
|
||||
const tag = Container.get(TagRepository).create({ name: name.trim() });
|
||||
return transactionManager.save<TagEntity>(tag);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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<IDatabaseCollections, 'Tag'>;
|
||||
}) {
|
||||
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<TagEntity> {
|
||||
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<TagEntity> {
|
||||
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);
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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<ExportResult> {
|
||||
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 {
|
||||
|
|
|
@ -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 <strong>${tag.name}</strong> already exists locally.<br />Please either rename the local tag, or the remote one with the id <strong>${tag.id}</strong> in the tags.json file.`,
|
||||
);
|
||||
}
|
||||
await Db.collections.Tag.upsert(
|
||||
await this.tagRepository.upsert(
|
||||
{
|
||||
...tag,
|
||||
},
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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<TagEntity> = {}) {
|
||||
const { name } = attributes;
|
||||
|
||||
return Db.collections.Tag.save({
|
||||
return Container.get(TagRepository).save({
|
||||
id: generateNanoId(),
|
||||
name: name ?? randomName(),
|
||||
...attributes,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue