mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(core): Add subFolderCount to GET /workflows and /folders (no-changelog) (#13548)
This commit is contained in:
@@ -11,6 +11,7 @@ const VALID_SELECT_FIELDS = [
|
||||
'tags',
|
||||
'parentFolder',
|
||||
'workflowCount',
|
||||
'subFolderCount',
|
||||
] as const;
|
||||
|
||||
const VALID_SORT_OPTIONS = [
|
||||
|
||||
@@ -13,8 +13,9 @@ import { Project } from './project';
|
||||
import { TagEntity } from './tag-entity';
|
||||
import { type WorkflowEntity } from './workflow-entity';
|
||||
|
||||
export type FolderWithWorkflowCount = Folder & {
|
||||
export type FolderWithWorkflowAndSubFolderCount = Folder & {
|
||||
workflowCount: boolean;
|
||||
subFolderCount: number;
|
||||
};
|
||||
|
||||
@Entity()
|
||||
@@ -26,6 +27,12 @@ export class Folder extends WithTimestampsAndStringId {
|
||||
@JoinColumn({ name: 'parentFolderId' })
|
||||
parentFolder: Folder | null;
|
||||
|
||||
@OneToMany(
|
||||
() => Folder,
|
||||
(folder) => folder.parentFolder,
|
||||
)
|
||||
subFolders: Folder[];
|
||||
|
||||
@ManyToOne(() => Project)
|
||||
@JoinColumn({ name: 'projectId' })
|
||||
homeProject: Project;
|
||||
|
||||
@@ -366,6 +366,24 @@ describe('FolderRepository', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should return id, name and subFolderCount when specified', async () => {
|
||||
const [folders] = await folderRepository.getManyAndCount({
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
subFolderCount: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(folders).toHaveLength(2);
|
||||
folders.forEach((folder) => {
|
||||
expect(Object.keys(folder).sort()).toEqual(['id', 'name', 'subFolderCount']);
|
||||
expect(folder.id).toBeDefined();
|
||||
expect(folder.name).toBeDefined();
|
||||
expect(folder.subFolderCount).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return timestamps when specified', async () => {
|
||||
const [folders] = await folderRepository.getManyAndCount({
|
||||
select: {
|
||||
@@ -400,6 +418,7 @@ describe('FolderRepository', () => {
|
||||
icon: null,
|
||||
},
|
||||
workflowCount: expect.any(Number),
|
||||
subFolderCount: expect.any(Number),
|
||||
tags: expect.any(Array),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,30 +5,32 @@ import { PROJECT_ROOT } from 'n8n-workflow';
|
||||
|
||||
import type { ListQuery } from '@/requests';
|
||||
|
||||
import type { FolderWithWorkflowCount } from '../entities/folder';
|
||||
import type { FolderWithWorkflowAndSubFolderCount } from '../entities/folder';
|
||||
import { Folder } from '../entities/folder';
|
||||
import { FolderTagMapping } from '../entities/folder-tag-mapping';
|
||||
import { TagEntity } from '../entities/tag-entity';
|
||||
|
||||
@Service()
|
||||
export class FolderRepository extends Repository<FolderWithWorkflowCount> {
|
||||
export class FolderRepository extends Repository<FolderWithWorkflowAndSubFolderCount> {
|
||||
constructor(dataSource: DataSource) {
|
||||
super(Folder, dataSource.manager);
|
||||
}
|
||||
|
||||
async getManyAndCount(
|
||||
options: ListQuery.Options = {},
|
||||
): Promise<[FolderWithWorkflowCount[], number]> {
|
||||
): Promise<[FolderWithWorkflowAndSubFolderCount[], number]> {
|
||||
const query = this.getManyQuery(options);
|
||||
return await query.getManyAndCount();
|
||||
}
|
||||
|
||||
async getMany(options: ListQuery.Options = {}): Promise<FolderWithWorkflowCount[]> {
|
||||
async getMany(options: ListQuery.Options = {}): Promise<FolderWithWorkflowAndSubFolderCount[]> {
|
||||
const query = this.getManyQuery(options);
|
||||
return await query.getMany();
|
||||
}
|
||||
|
||||
getManyQuery(options: ListQuery.Options = {}): SelectQueryBuilder<FolderWithWorkflowCount> {
|
||||
getManyQuery(
|
||||
options: ListQuery.Options = {},
|
||||
): SelectQueryBuilder<FolderWithWorkflowAndSubFolderCount> {
|
||||
const query = this.createQueryBuilder('folder');
|
||||
|
||||
this.applySelections(query, options.select);
|
||||
@@ -40,7 +42,7 @@ export class FolderRepository extends Repository<FolderWithWorkflowCount> {
|
||||
}
|
||||
|
||||
private applySelections(
|
||||
query: SelectQueryBuilder<FolderWithWorkflowCount>,
|
||||
query: SelectQueryBuilder<FolderWithWorkflowAndSubFolderCount>,
|
||||
select?: Record<string, boolean>,
|
||||
): void {
|
||||
if (select) {
|
||||
@@ -50,12 +52,13 @@ export class FolderRepository extends Repository<FolderWithWorkflowCount> {
|
||||
}
|
||||
}
|
||||
|
||||
private applyDefaultSelect(query: SelectQueryBuilder<FolderWithWorkflowCount>): void {
|
||||
private applyDefaultSelect(query: SelectQueryBuilder<FolderWithWorkflowAndSubFolderCount>): void {
|
||||
query
|
||||
.leftJoinAndSelect('folder.homeProject', 'homeProject')
|
||||
.leftJoinAndSelect('folder.parentFolder', 'parentFolder')
|
||||
.leftJoinAndSelect('folder.tags', 'tags')
|
||||
.loadRelationCountAndMap('folder.workflowCount', 'folder.workflows')
|
||||
.loadRelationCountAndMap('folder.subFolderCount', 'folder.subFolders')
|
||||
.select([
|
||||
'folder',
|
||||
...this.getProjectFields('homeProject'),
|
||||
@@ -65,7 +68,7 @@ export class FolderRepository extends Repository<FolderWithWorkflowCount> {
|
||||
}
|
||||
|
||||
private applyCustomSelect(
|
||||
query: SelectQueryBuilder<FolderWithWorkflowCount>,
|
||||
query: SelectQueryBuilder<FolderWithWorkflowAndSubFolderCount>,
|
||||
select?: Record<string, boolean>,
|
||||
): void {
|
||||
const selections = ['folder.id'];
|
||||
@@ -83,7 +86,7 @@ export class FolderRepository extends Repository<FolderWithWorkflowCount> {
|
||||
}
|
||||
|
||||
private addRelationFields(
|
||||
query: SelectQueryBuilder<FolderWithWorkflowCount>,
|
||||
query: SelectQueryBuilder<FolderWithWorkflowAndSubFolderCount>,
|
||||
selections: string[],
|
||||
select?: Record<string, boolean>,
|
||||
): void {
|
||||
@@ -105,6 +108,12 @@ export class FolderRepository extends Repository<FolderWithWorkflowCount> {
|
||||
if (select?.workflowCount) {
|
||||
query.loadRelationCountAndMap('folder.workflowCount', 'folder.workflows');
|
||||
}
|
||||
|
||||
if (select?.subFolderCount) {
|
||||
if (!query.hasRelation(Folder, 'folder.parentFolder')) {
|
||||
query.loadRelationCountAndMap('folder.subFolderCount', 'folder.subFolders');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getProjectFields(alias: string): string[] {
|
||||
@@ -120,7 +129,7 @@ export class FolderRepository extends Repository<FolderWithWorkflowCount> {
|
||||
}
|
||||
|
||||
private applyFilters(
|
||||
query: SelectQueryBuilder<FolderWithWorkflowCount>,
|
||||
query: SelectQueryBuilder<FolderWithWorkflowAndSubFolderCount>,
|
||||
filter?: ListQuery.Options['filter'],
|
||||
): void {
|
||||
if (!filter) return;
|
||||
@@ -130,7 +139,7 @@ export class FolderRepository extends Repository<FolderWithWorkflowCount> {
|
||||
}
|
||||
|
||||
private applyBasicFilters(
|
||||
query: SelectQueryBuilder<FolderWithWorkflowCount>,
|
||||
query: SelectQueryBuilder<FolderWithWorkflowAndSubFolderCount>,
|
||||
filter: ListQuery.Options['filter'],
|
||||
): void {
|
||||
if (filter?.folderIds && Array.isArray(filter.folderIds)) {
|
||||
@@ -163,7 +172,7 @@ export class FolderRepository extends Repository<FolderWithWorkflowCount> {
|
||||
}
|
||||
|
||||
private applyTagsFilter(
|
||||
query: SelectQueryBuilder<FolderWithWorkflowCount>,
|
||||
query: SelectQueryBuilder<FolderWithWorkflowAndSubFolderCount>,
|
||||
tags?: string[],
|
||||
): void {
|
||||
if (!Array.isArray(tags) || tags.length === 0) return;
|
||||
@@ -177,7 +186,7 @@ export class FolderRepository extends Repository<FolderWithWorkflowCount> {
|
||||
}
|
||||
|
||||
private createTagsSubQuery(
|
||||
query: SelectQueryBuilder<FolderWithWorkflowCount>,
|
||||
query: SelectQueryBuilder<FolderWithWorkflowAndSubFolderCount>,
|
||||
tags: string[],
|
||||
): SelectQueryBuilder<FolderTagMapping> {
|
||||
return query
|
||||
@@ -192,7 +201,10 @@ export class FolderRepository extends Repository<FolderWithWorkflowCount> {
|
||||
});
|
||||
}
|
||||
|
||||
private applySorting(query: SelectQueryBuilder<FolderWithWorkflowCount>, sortBy?: string): void {
|
||||
private applySorting(
|
||||
query: SelectQueryBuilder<FolderWithWorkflowAndSubFolderCount>,
|
||||
sortBy?: string,
|
||||
): void {
|
||||
if (!sortBy) {
|
||||
query.orderBy('folder.updatedAt', 'DESC');
|
||||
return;
|
||||
@@ -208,7 +220,7 @@ export class FolderRepository extends Repository<FolderWithWorkflowCount> {
|
||||
}
|
||||
|
||||
private applySortingByField(
|
||||
query: SelectQueryBuilder<FolderWithWorkflowCount>,
|
||||
query: SelectQueryBuilder<FolderWithWorkflowAndSubFolderCount>,
|
||||
field: string,
|
||||
direction: 'DESC' | 'ASC',
|
||||
): void {
|
||||
@@ -222,7 +234,7 @@ export class FolderRepository extends Repository<FolderWithWorkflowCount> {
|
||||
}
|
||||
|
||||
private applyPagination(
|
||||
query: SelectQueryBuilder<FolderWithWorkflowCount>,
|
||||
query: SelectQueryBuilder<FolderWithWorkflowAndSubFolderCount>,
|
||||
options: ListQuery.Options,
|
||||
): void {
|
||||
if (options?.take) {
|
||||
|
||||
@@ -16,7 +16,7 @@ import type { ListQuery } from '@/requests';
|
||||
import { isStringArray } from '@/utils';
|
||||
|
||||
import { FolderRepository } from './folder.repository';
|
||||
import type { Folder, FolderWithWorkflowCount } from '../entities/folder';
|
||||
import type { Folder, FolderWithWorkflowAndSubFolderCount } from '../entities/folder';
|
||||
import { TagEntity } from '../entities/tag-entity';
|
||||
import { WebhookEntity } from '../entities/webhook-entity';
|
||||
import { WorkflowEntity } from '../entities/workflow-entity';
|
||||
@@ -36,7 +36,7 @@ type WorkflowFolderUnionRow = {
|
||||
export type WorkflowFolderUnionFull = (
|
||||
| ListQuery.Workflow.Plain
|
||||
| ListQuery.Workflow.WithSharing
|
||||
| FolderWithWorkflowCount
|
||||
| FolderWithWorkflowAndSubFolderCount
|
||||
) & {
|
||||
resource: ResourceType;
|
||||
};
|
||||
|
||||
@@ -1321,6 +1321,7 @@ describe('GET /workflows?includeFolders=true', () => {
|
||||
},
|
||||
parentFolder: null,
|
||||
workflowCount: 0,
|
||||
subFolderCount: 0,
|
||||
}),
|
||||
]),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user