@@ -4,21 +4,26 @@ import { Service } from '@n8n/di';
4
4
import { FolderRepository } from '@/databases/repositories/folder.repository' ;
5
5
import { FolderNotFoundError } from '@/errors/folder-not-found.error' ;
6
6
7
+ export interface SimpleFolderNode {
8
+ id : string ;
9
+ name : string ;
10
+ children : SimpleFolderNode [ ] ;
11
+ }
12
+
13
+ interface FolderPathRow {
14
+ folder_id : string ;
15
+ folder_name : string ;
16
+ folder_parent_folder_id : string | null ;
17
+ }
18
+
7
19
@Service ( )
8
20
export class FolderService {
9
21
constructor ( private readonly folderRepository : FolderRepository ) { }
10
22
11
23
async createFolder ( { parentFolderId, name } : CreateFolderDto , projectId : string ) {
12
24
let parentFolder = null ;
13
25
if ( parentFolderId ) {
14
- try {
15
- parentFolder = await this . folderRepository . findOneOrFailFolderInProject (
16
- parentFolderId ,
17
- projectId ,
18
- ) ;
19
- } catch {
20
- throw new FolderNotFoundError ( parentFolderId ) ;
21
- }
26
+ parentFolder = await this . getFolderInProject ( parentFolderId , projectId ) ;
22
27
}
23
28
24
29
const folderEntity = this . folderRepository . create ( {
@@ -31,4 +36,83 @@ export class FolderService {
31
36
32
37
return folder ;
33
38
}
39
+
40
+ async getFolderInProject ( folderId : string , projectId : string ) {
41
+ try {
42
+ return await this . folderRepository . findOneOrFailFolderInProject ( folderId , projectId ) ;
43
+ } catch {
44
+ throw new FolderNotFoundError ( folderId ) ;
45
+ }
46
+ }
47
+
48
+ async getFolderTree ( folderId : string , projectId : string ) : Promise < SimpleFolderNode [ ] > {
49
+ await this . getFolderInProject ( folderId , projectId ) ;
50
+
51
+ const baseQuery = this . folderRepository
52
+ . createQueryBuilder ( 'folder' )
53
+ . select ( 'folder.id' , 'id' )
54
+ . addSelect ( 'folder.parentFolderId' , 'parentFolderId' )
55
+ . where ( 'folder.id = :folderId' , { folderId } ) ;
56
+
57
+ const recursiveQuery = this . folderRepository
58
+ . createQueryBuilder ( 'f' )
59
+ . select ( 'f.id' , 'id' )
60
+ . addSelect ( 'f.parentFolderId' , 'parentFolderId' )
61
+ . innerJoin ( 'folder_path' , 'fp' , 'f.id = fp.parentFolderId' ) ;
62
+
63
+ const mainQuery = this . folderRepository
64
+ . createQueryBuilder ( 'folder' )
65
+ . select ( 'folder.id' , 'folder_id' )
66
+ . addSelect ( 'folder.name' , 'folder_name' )
67
+ . addSelect ( 'folder.parentFolderId' , 'folder_parent_folder_id' )
68
+ . addCommonTableExpression (
69
+ `${ baseQuery . getQuery ( ) } UNION ALL ${ recursiveQuery . getQuery ( ) } ` ,
70
+ 'folder_path' ,
71
+ { recursive : true } ,
72
+ )
73
+ . where ( ( qb ) => {
74
+ const subQuery = qb . subQuery ( ) . select ( 'fp.id' ) . from ( 'folder_path' , 'fp' ) . getQuery ( ) ;
75
+ return `folder.id IN ${ subQuery } ` ;
76
+ } )
77
+ . setParameters ( {
78
+ folderId,
79
+ } ) ;
80
+
81
+ const result = await mainQuery . getRawMany < FolderPathRow > ( ) ;
82
+
83
+ return this . transformFolderPathToTree ( result ) ;
84
+ }
85
+
86
+ private transformFolderPathToTree ( flatPath : FolderPathRow [ ] ) : SimpleFolderNode [ ] {
87
+ if ( ! flatPath || flatPath . length === 0 ) {
88
+ return [ ] ;
89
+ }
90
+
91
+ const folderMap = new Map < string , SimpleFolderNode > ( ) ;
92
+
93
+ // First pass: create all nodes
94
+ flatPath . forEach ( ( folder ) => {
95
+ folderMap . set ( folder . folder_id , {
96
+ id : folder . folder_id ,
97
+ name : folder . folder_name ,
98
+ children : [ ] ,
99
+ } ) ;
100
+ } ) ;
101
+
102
+ let rootNode : SimpleFolderNode | null = null ;
103
+
104
+ // Second pass: build the tree
105
+ flatPath . forEach ( ( folder ) => {
106
+ const currentNode = folderMap . get ( folder . folder_id ) ! ;
107
+
108
+ if ( folder . folder_parent_folder_id && folderMap . has ( folder . folder_parent_folder_id ) ) {
109
+ const parentNode = folderMap . get ( folder . folder_parent_folder_id ) ! ;
110
+ parentNode . children = [ currentNode ] ;
111
+ } else {
112
+ rootNode = currentNode ;
113
+ }
114
+ } ) ;
115
+
116
+ return rootNode ? [ rootNode ] : [ ] ;
117
+ }
34
118
}
0 commit comments