refactor(core): Switch Tags queries from QueryBuilder to Repository API (no-changelog) (#5819)

Co-authored-by: Omar Ajoue <krynble@gmail.com>
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-03-30 16:25:51 +02:00 committed by GitHub
parent f8f584c136
commit 41cdee7bc7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 58 additions and 81 deletions

View file

@ -159,6 +159,7 @@ export async function init(
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);

View file

@ -48,8 +48,9 @@ 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 './databases/entities/ExecutionMetadata';
import type { ExecutionMetadata } from '@db/entities/ExecutionMetadata';
export interface IActivationError {
time: number;
@ -81,6 +82,7 @@ export interface IDatabaseCollections {
Workflow: Repository<WorkflowEntity>;
Webhook: Repository<WebhookEntity>;
Tag: Repository<TagEntity>;
WorkflowTagMapping: Repository<WorkflowTagMapping>;
Role: Repository<Role>;
User: Repository<User>;
SharedCredentials: Repository<SharedCredentials>;
@ -108,7 +110,8 @@ export type UsageCount = {
usageCount: number;
};
export type ITagWithCountDb = TagEntity & UsageCount;
export type ITagWithCountDb = Pick<TagEntity, 'id' | 'name' | 'createdAt' | 'updatedAt'> &
UsageCount;
// ----------------------------------
// workflows

View file

@ -1,10 +1,6 @@
/* eslint-disable no-param-reassign */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import type { EntityManager } from 'typeorm';
import { getConnection } from '@/Db';
import { TagEntity } from '@db/entities/TagEntity';
import type { ITagToImport, ITagWithCountDb, IWorkflowToImport } from '@/Interfaces';
import type { ITagToImport, IWorkflowToImport } from '@/Interfaces';
// ----------------------------------
// utils
@ -25,70 +21,10 @@ export function sortByRequestOrder(
return requestOrder.map((tagId) => tagMap[tagId]);
}
// ----------------------------------
// queries
// ----------------------------------
/**
* Retrieve all tags and the number of workflows each tag is related to.
*/
export async function getTagsWithCountDb(tablePrefix: string): Promise<ITagWithCountDb[]> {
return getConnection()
.createQueryBuilder()
.select(`${tablePrefix}tag_entity.id`, 'id')
.addSelect(`${tablePrefix}tag_entity.name`, 'name')
.addSelect(`${tablePrefix}tag_entity.createdAt`, 'createdAt')
.addSelect(`${tablePrefix}tag_entity.updatedAt`, 'updatedAt')
.addSelect(`COUNT(${tablePrefix}workflows_tags.workflowId)`, 'usageCount')
.from(`${tablePrefix}tag_entity`, 'tag_entity')
.leftJoin(
`${tablePrefix}workflows_tags`,
'workflows_tags',
`${tablePrefix}workflows_tags.tagId = tag_entity.id`,
)
.groupBy(`${tablePrefix}tag_entity.id`)
.getRawMany()
.then((tagsWithCount) => {
tagsWithCount.forEach((tag) => {
// NOTE: since this code doesn't use the DB entities, we need to stringify the IDs manually
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
tag.id = tag.id.toString();
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
tag.usageCount = Number(tag.usageCount);
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return tagsWithCount;
});
}
// ----------------------------------
// mutations
// ----------------------------------
/**
* Relate a workflow to one or more tags.
*/
export async function createRelations(workflowId: string, tagIds: string[], tablePrefix: string) {
return getConnection()
.createQueryBuilder()
.insert()
.into(`${tablePrefix}workflows_tags`)
.values(tagIds.map((tagId) => ({ workflowId, tagId })))
.execute();
}
/**
* Remove all tags for a workflow during a tag update operation.
*/
export async function removeRelations(workflowId: string, tablePrefix: string) {
return getConnection()
.createQueryBuilder()
.delete()
.from(`${tablePrefix}workflows_tags`)
.where('workflowId = :id', { id: workflowId })
.execute();
}
const createTag = async (transactionManager: EntityManager, name: string): Promise<TagEntity> => {
const tag = new TagEntity();
tag.name = name;

View file

@ -4,7 +4,6 @@ import type { Config } from '@/config';
import { Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators';
import type { IDatabaseCollections, IExternalHooksClass, ITagWithCountDb } from '@/Interfaces';
import { TagEntity } from '@db/entities/TagEntity';
import { getTagsWithCountDb } from '@/TagHelpers';
import { validateEntity } from '@/GenericHelpers';
import { BadRequestError, UnauthorizedError } from '@/ResponseHelper';
import { TagsRequest } from '@/requests';
@ -44,8 +43,17 @@ export class TagsController {
async getAll(req: TagsRequest.GetAll): Promise<TagEntity[] | ITagWithCountDb[]> {
const { withUsageCount } = req.query;
if (withUsageCount === 'true') {
const tablePrefix = this.config.getEnv('database.tablePrefix');
return getTagsWithCountDb(tablePrefix);
return this.tagsRepository
.find({
select: ['id', 'name', 'createdAt', 'updatedAt'],
relations: ['workflowMappings'],
})
.then((tags) =>
tags.map(({ workflowMappings, ...rest }) => ({
...rest,
usageCount: workflowMappings.length,
})),
);
}
return this.tagsRepository.find({ select: ['id', 'name', 'createdAt', 'updatedAt'] });

View file

@ -1,8 +1,9 @@
import { Column, Entity, Generated, Index, ManyToMany, PrimaryColumn } from 'typeorm';
import { Column, Entity, Generated, Index, ManyToMany, OneToMany, PrimaryColumn } from 'typeorm';
import { IsString, Length } from 'class-validator';
import { idStringifier } from '../utils/transformers';
import type { WorkflowEntity } from './WorkflowEntity';
import type { WorkflowTagMapping } from './WorkflowTagMapping';
import { AbstractEntity } from './AbstractEntity';
@Entity()
@ -19,4 +20,7 @@ export class TagEntity extends AbstractEntity {
@ManyToMany('WorkflowEntity', 'tags')
workflows: WorkflowEntity[];
@OneToMany('WorkflowTagMapping', 'tags')
workflowMappings: WorkflowTagMapping[];
}

View file

@ -19,6 +19,7 @@ import config from '@/config';
import type { TagEntity } from './TagEntity';
import type { SharedWorkflow } from './SharedWorkflow';
import type { WorkflowStatistics } from './WorkflowStatistics';
import type { WorkflowTagMapping } from './WorkflowTagMapping';
import { idStringifier, objectRetriever, sqlite } from '../utils/transformers';
import { AbstractEntity, jsonColumnType } from './AbstractEntity';
import type { IWorkflowDb } from '@/Interfaces';
@ -73,6 +74,9 @@ export class WorkflowEntity extends AbstractEntity implements IWorkflowDb {
})
tags?: TagEntity[];
@OneToMany('WorkflowTagMapping', 'workflows')
tagMappings: WorkflowTagMapping[];
@OneToMany('SharedWorkflow', 'workflow')
shared: SharedWorkflow[];

View file

@ -0,0 +1,21 @@
import { Entity, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm';
import { idStringifier } from '../utils/transformers';
import type { TagEntity } from './TagEntity';
import type { WorkflowEntity } from './WorkflowEntity';
@Entity({ name: 'workflows_tags' })
export class WorkflowTagMapping {
@PrimaryColumn({ transformer: idStringifier })
workflowId: string;
@ManyToOne('WorkflowEntity', 'tagMappings')
@JoinColumn({ name: 'workflowId' })
workflows: WorkflowEntity[];
@PrimaryColumn()
tagId: string;
@ManyToOne('TagEntity', 'workflowMappings')
@JoinColumn({ name: 'tagId' })
tags: TagEntity[];
}

View file

@ -14,6 +14,7 @@ import { TagEntity } from './TagEntity';
import { User } from './User';
import { WebhookEntity } from './WebhookEntity';
import { WorkflowEntity } from './WorkflowEntity';
import { WorkflowTagMapping } from './WorkflowTagMapping';
import { WorkflowStatistics } from './WorkflowStatistics';
import { ExecutionMetadata } from './ExecutionMetadata';
@ -33,6 +34,7 @@ export const entities = {
User,
WebhookEntity,
WorkflowEntity,
WorkflowTagMapping,
WorkflowStatistics,
ExecutionMetadata,
};

View file

@ -186,7 +186,7 @@ export class WorkflowsService {
user: User,
workflow: WorkflowEntity,
workflowId: string,
tags?: string[],
tagIds?: string[],
forceSave?: boolean,
roles?: string[],
): Promise<WorkflowEntity> {
@ -285,13 +285,11 @@ export class WorkflowsService {
]),
);
if (tags && !config.getEnv('workflowTagsDisabled')) {
const tablePrefix = config.getEnv('database.tablePrefix');
await TagHelpers.removeRelations(workflowId, tablePrefix);
if (tags.length) {
await TagHelpers.createRelations(workflowId, tags, tablePrefix);
}
if (tagIds && !config.getEnv('workflowTagsDisabled')) {
await Db.collections.WorkflowTagMapping.delete({ workflowId });
await Db.collections.WorkflowTagMapping.insert(
tagIds.map((tagId) => ({ tagId, workflowId })),
);
}
const relations = config.getEnv('workflowTagsDisabled') ? [] : ['tags'];
@ -309,9 +307,9 @@ export class WorkflowsService {
);
}
if (updatedWorkflow.tags?.length && tags?.length) {
if (updatedWorkflow.tags?.length && tagIds?.length) {
updatedWorkflow.tags = TagHelpers.sortByRequestOrder(updatedWorkflow.tags, {
requestOrder: tags,
requestOrder: tagIds,
});
}