@@ -176,148 +176,6 @@ let _ = require('lodash'),
176
176
return tree ;
177
177
} ,
178
178
179
- // _generateTreeFromPaths = function (openapi, { includeDeprecated }) {
180
- // /**
181
- // * We will create a unidirectional graph
182
- // */
183
- // let tree = new Graph();
184
-
185
- // tree.setNode('root:collection', {
186
- // type: 'collection',
187
- // data: {},
188
- // meta: {}
189
- // });
190
-
191
- // _.forEach(openapi.paths, function (methods, path) {
192
- // let pathSplit = path === '/' ? [path] : _.compact(path.split('/'));
193
-
194
- // // if after path split we just have one entry
195
- // // that means no folders need to be generated.
196
- // // check for all the methods inside it and expand.
197
- // if (pathSplit.length === 1) {
198
- // /**
199
- // * Always first try to find the node if it already exists.
200
- // * if yes, bail out nothing is needed to be done.
201
- // *
202
- // * if the path length is 1, then also generate
203
- // * the folder otherwise /pet and /pet/:id will never be in same folder.
204
- // */
205
- // // if (!tree.hasNode(`path:${pathSplit[0]}`)) {
206
- // // tree.setNode(`path:${pathSplit[0]}`, {
207
- // // type: 'folder',
208
- // // meta: {
209
- // // name: pathSplit[0],
210
- // // path: pathSplit[0],
211
- // // pathIdentifier: pathIdentifier
212
- // // },
213
- // // data: {}
214
- // // });
215
-
216
- // // tree.setEdge('root:collection', `path:${pathSplit[0]}`);
217
- // // }
218
-
219
-
220
- // _.forEach(methods, function (data, method) {
221
- // if (!ALLOWED_HTTP_METHODS[method]) {
222
- // return;
223
- // }
224
-
225
- // /**
226
- // * include deprecated handling.
227
- // * If true, add in the postman collection. If false ignore the request.
228
- // */
229
- // if (!includeDeprecated && data.deprecated) {
230
- // return;
231
- // }
232
-
233
- // tree.setNode(`path:${pathSplit[0]}:${method}`, {
234
- // type: 'request',
235
- // meta: {
236
- // path: path,
237
- // method: method,
238
- // pathIdentifier: pathSplit[0]
239
- // },
240
- // data: {}
241
- // });
242
-
243
- // tree.setEdge(`path:${pathSplit[0]}`, `path:${pathSplit[0]}:${method}`);
244
- // });
245
- // }
246
-
247
- // else {
248
- // _.forEach(pathSplit, function (p, index) {
249
- // let previousPathIdentified = pathSplit.slice(0, index).join('/'),
250
- // pathIdentifier = pathSplit.slice(0, index + 1).join('/');
251
-
252
- // /**
253
- // * Always first try to find the node if it already exists.
254
- // * if yes, bail out nothing is needed to be done.
255
- // */
256
- // if (tree.hasNode(`path:${pathIdentifier}`)) {
257
- // return;
258
- // }
259
-
260
- // else {
261
- // tree.setNode(`path:${pathIdentifier}`, {
262
- // type: 'folder',
263
- // meta: {
264
- // name: p,
265
- // path: p,
266
- // pathIdentifier: pathIdentifier
267
- // },
268
- // data: {}
269
- // });
270
-
271
- // /**
272
- // * If index is 0, this means that we are on the first level.
273
- // * Hence it is folder/request to be added on the first level
274
- // *
275
- // * If after the split we have more than one paths, then we need
276
- // * to add to the previous node.
277
- // */
278
- // tree.setEdge(index === 0 ? 'root:collection' : `path:${previousPathIdentified}`, `path:${pathIdentifier}`);
279
- // }
280
- // });
281
-
282
- // /**
283
- // * Now for all the methods present in the path, add the request nodes.
284
- // */
285
-
286
- // _.forEach(methods, function (data, method) {
287
- // if (!ALLOWED_HTTP_METHODS[method]) {
288
- // return;
289
- // }
290
-
291
- // /**
292
- // * include deprecated handling.
293
- // * If true, add in the postman collection. If false ignore the request.
294
- // */
295
- // if (!includeDeprecated && data.deprecated) {
296
- // return;
297
- // }
298
-
299
- // // join till the last path i.e. the folder.
300
- // let previousPathIdentified = pathSplit.slice(0, (pathSplit.length)).join('/'),
301
- // pathIdentifier = `${pathSplit.join('/')}:${method}`;
302
-
303
- // tree.setNode(`path:${pathIdentifier}`, {
304
- // type: 'request',
305
- // data: {},
306
- // meta: {
307
- // path: path,
308
- // method: method,
309
- // pathIdentifier: pathIdentifier
310
- // }
311
- // });
312
-
313
- // tree.setEdge(`path:${previousPathIdentified}`, `path:${pathIdentifier}`);
314
- // });
315
- // }
316
- // });
317
-
318
- // return tree;
319
- // },
320
-
321
179
_generateTreeFromTags = function ( openapi , { includeDeprecated } ) {
322
180
let tree = new Graph ( ) ,
323
181
@@ -424,6 +282,124 @@ let _ = require('lodash'),
424
282
return tree ;
425
283
} ,
426
284
285
+ /**
286
+ * Generates tree structure with nested folders based on tag order
287
+ * @param {Object } openapi - OpenAPI specification
288
+ * @param {Object } options - Generation options
289
+ * @param {boolean } options.includeDeprecated - Whether to include deprecated operations
290
+ * @returns {Object } - Graph tree with nested folder structure
291
+ */
292
+ _generateTreeFromNestedTags = function ( openapi , { includeDeprecated } ) {
293
+ let tree = new Graph ( ) ,
294
+
295
+ tagDescMap = _ . reduce ( openapi . tags , function ( acc , data ) {
296
+ acc [ data . name ] = data . description ;
297
+
298
+ return acc ;
299
+ } , { } ) ;
300
+
301
+ tree . setNode ( 'root:collection' , {
302
+ type : 'collection' ,
303
+ data : { } ,
304
+ meta : { }
305
+ } ) ;
306
+
307
+ /**
308
+ * Helper function to create nested folder structure for tags
309
+ * @param {Array } tags - Array of tags to create nested folders for
310
+ * @returns {String } - Node ID of the deepest folder created
311
+ */
312
+ const createNestedFolders = function ( tags ) {
313
+ if ( ! tags || tags . length === 0 ) {
314
+ return 'root:collection' ;
315
+ }
316
+
317
+ let parentNodeId = 'root:collection' ;
318
+
319
+ // Create nested folder structure based on tag order
320
+ for ( let i = 0 ; i < tags . length ; i ++ ) {
321
+ const tag = tags [ i ] ,
322
+ folderPath = tags . slice ( 0 , i + 1 ) . join ( ':' ) ,
323
+ currentNodeId = `path:${ folderPath } ` ;
324
+
325
+ // Create folder node if it doesn't exist
326
+ if ( ! tree . hasNode ( currentNodeId ) ) {
327
+ tree . setNode ( currentNodeId , {
328
+ type : 'folder' ,
329
+ meta : {
330
+ path : '' ,
331
+ name : tag ,
332
+ description : tagDescMap [ tag ] || ''
333
+ } ,
334
+ data : { }
335
+ } ) ;
336
+
337
+ // Connect to parent (either root collection or previous folder)
338
+ tree . setEdge ( parentNodeId , currentNodeId ) ;
339
+ }
340
+
341
+ parentNodeId = currentNodeId ;
342
+ }
343
+
344
+ return parentNodeId ;
345
+ } ;
346
+
347
+ _ . forEach ( openapi . paths , function ( methods , path ) {
348
+ _ . forEach ( methods , function ( data , method ) {
349
+ if ( ! ALLOWED_HTTP_METHODS [ method ] ) {
350
+ return ;
351
+ }
352
+
353
+ /**
354
+ * include deprecated handling.
355
+ * If true, add in the postman collection. If false ignore the request.
356
+ */
357
+ if ( ! includeDeprecated && data . deprecated ) {
358
+ return ;
359
+ }
360
+
361
+ /**
362
+ * Create nested folder structure based on tags order
363
+ * and place the request in the deepest folder
364
+ */
365
+ if ( data . tags && data . tags . length > 0 ) {
366
+ // Create nested folder structure and get the deepest folder node
367
+ const deepestFolderNodeId = createNestedFolders ( data . tags ) ,
368
+ // Create a unique request node ID (one per operation)
369
+ requestNodeId = `request:${ path } :${ method } ` ;
370
+
371
+ tree . setNode ( requestNodeId , {
372
+ type : 'request' ,
373
+ data : { } ,
374
+ meta : {
375
+ tags : data . tags ,
376
+ path : path ,
377
+ method : method
378
+ }
379
+ } ) ;
380
+
381
+ // Connect request to the deepest folder
382
+ tree . setEdge ( deepestFolderNodeId , requestNodeId ) ;
383
+ }
384
+ else {
385
+ // No tags - place request directly under root collection
386
+ tree . setNode ( `path:${ path } :${ method } ` , {
387
+ type : 'request' ,
388
+ data : { } ,
389
+ meta : {
390
+ path : path ,
391
+ method : method
392
+ }
393
+ } ) ;
394
+
395
+ tree . setEdge ( 'root:collection' , `path:${ path } :${ method } ` ) ;
396
+ }
397
+ } ) ;
398
+ } ) ;
399
+
400
+ return tree ;
401
+ } ,
402
+
427
403
_generateWebhookEndpoints = function ( openapi , tree , { includeDeprecated } ) {
428
404
if ( ! _ . isEmpty ( openapi . webhooks ) ) {
429
405
tree . setNode ( `${ PATH_WEBHOOK } :folder` , {
@@ -470,12 +446,18 @@ let _ = require('lodash'),
470
446
*
471
447
* @returns {Object } - tree format
472
448
*/
473
- module . exports = function ( openapi , { folderStrategy, includeWebhooks, includeDeprecated } ) {
449
+ module . exports = function ( openapi , { folderStrategy, includeWebhooks, includeDeprecated, nestedFolderHierarchy } ) {
474
450
let skeletonTree ;
475
451
476
452
switch ( folderStrategy ) {
477
453
case 'tags' :
478
- skeletonTree = _generateTreeFromTags ( openapi , { includeDeprecated } ) ;
454
+ if ( nestedFolderHierarchy ) {
455
+ skeletonTree = _generateTreeFromNestedTags ( openapi , { includeDeprecated } ) ;
456
+ }
457
+ else {
458
+ skeletonTree = _generateTreeFromTags ( openapi , { includeDeprecated } ) ;
459
+ }
460
+
479
461
break ;
480
462
481
463
case 'paths' :
0 commit comments