Skip to content

Commit

Permalink
refactor(core): Switch Tags queries from QueryBuilder to Repository A…
Browse files Browse the repository at this point in the history
…PI (no-changelog) (n8n-io#5819)

Co-authored-by: Omar Ajoue <[email protected]>
  • Loading branch information
2 people authored and believe-Mahesh committed Apr 4, 2023
1 parent 90af8b8 commit b46eb38
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 81 deletions.
1 change: 1 addition & 0 deletions packages/cli/src/Db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 5 additions & 2 deletions packages/cli/src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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>;
Expand Down Expand Up @@ -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
Expand Down
66 changes: 1 addition & 65 deletions packages/cli/src/TagHelpers.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
Expand Down
14 changes: 11 additions & 3 deletions packages/cli/src/controllers/tags.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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'] });
Expand Down
6 changes: 5 additions & 1 deletion packages/cli/src/databases/entities/TagEntity.ts
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -19,4 +20,7 @@ export class TagEntity extends AbstractEntity {

@ManyToMany('WorkflowEntity', 'tags')
workflows: WorkflowEntity[];

@OneToMany('WorkflowTagMapping', 'tags')
workflowMappings: WorkflowTagMapping[];
}
4 changes: 4 additions & 0 deletions packages/cli/src/databases/entities/WorkflowEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -73,6 +74,9 @@ export class WorkflowEntity extends AbstractEntity implements IWorkflowDb {
})
tags?: TagEntity[];

@OneToMany('WorkflowTagMapping', 'workflows')
tagMappings: WorkflowTagMapping[];

@OneToMany('SharedWorkflow', 'workflow')
shared: SharedWorkflow[];

Expand Down
21 changes: 21 additions & 0 deletions packages/cli/src/databases/entities/WorkflowTagMapping.ts
Original file line number Diff line number Diff line change
@@ -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[];
}
2 changes: 2 additions & 0 deletions packages/cli/src/databases/entities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -33,6 +34,7 @@ export const entities = {
User,
WebhookEntity,
WorkflowEntity,
WorkflowTagMapping,
WorkflowStatistics,
ExecutionMetadata,
};
18 changes: 8 additions & 10 deletions packages/cli/src/workflows/workflows.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export class WorkflowsService {
user: User,
workflow: WorkflowEntity,
workflowId: string,
tags?: string[],
tagIds?: string[],
forceSave?: boolean,
roles?: string[],
): Promise<WorkflowEntity> {
Expand Down Expand Up @@ -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'];
Expand All @@ -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,
});
}

Expand Down

0 comments on commit b46eb38

Please sign in to comment.