11import { join } from 'node:path' ;
22import values from 'lodash/values' ;
33import isEmpty from 'lodash/isEmpty' ;
4- import { log , handleAndLogError } from '@contentstack/cli-utilities' ;
4+ import { log , handleAndLogError , CLIProgressManager } from '@contentstack/cli-utilities' ;
55import { PATH_CONSTANTS } from '../../constants' ;
66
77import BaseClass , { ApiOptions } from './base-class' ;
8- import { fsUtil , fileHelper , MODULE_CONTEXTS , MODULE_NAMES , PROCESS_STATUS , PROCESS_NAMES } from '../../utils' ;
8+ import {
9+ fsUtil ,
10+ fileHelper ,
11+ MODULE_CONTEXTS ,
12+ MODULE_NAMES ,
13+ PROCESS_STATUS ,
14+ PROCESS_NAMES ,
15+ readEnvUidMapperSync ,
16+ warnIfEnvMapperEmpty ,
17+ serializePublishTaxonomies ,
18+ } from '../../utils' ;
919import { ModuleClassParams , TaxonomiesConfig } from '../../types' ;
1020
1121export default class ImportTaxonomies extends BaseClass {
@@ -19,6 +29,7 @@ export default class ImportTaxonomies extends BaseClass {
1929 private termsSuccessPath : string ;
2030 private termsFailsPath : string ;
2131 private localesFilePath : string ;
32+ private envUidMapperPath : string ;
2233 private isLocaleBasedStructure : boolean = false ;
2334 public createdTaxonomies : Record < string , unknown > = { } ;
2435 public failedTaxonomies : Record < string , unknown > = { } ;
@@ -46,8 +57,16 @@ export default class ImportTaxonomies extends BaseClass {
4657 importConfig . modules . locales . dirName ,
4758 importConfig . modules . locales . fileName ,
4859 ) ;
60+ this . envUidMapperPath = join (
61+ importConfig . backupDir ,
62+ PATH_CONSTANTS . MAPPER ,
63+ PATH_CONSTANTS . MAPPER_MODULES . ENVIRONMENTS ,
64+ PATH_CONSTANTS . FILES . UID_MAPPING ,
65+ ) ;
4966 }
5067
68+ // --- Lifecycle ---
69+
5170 /**
5271 * @method start
5372 * @returns {Promise<void> } Promise<void>
@@ -56,7 +75,7 @@ export default class ImportTaxonomies extends BaseClass {
5675 try {
5776 log . debug ( 'Starting taxonomies import process...' , this . importConfig . context ) ;
5877
59- const [ taxonomiesCount ] = await this . analyzeTaxonomies ( ) ;
78+ const [ taxonomiesCount , publishJobCount ] = await this . analyzeTaxonomies ( ) ;
6079 if ( taxonomiesCount === 0 ) {
6180 log . info ( 'No taxonomies found to import' , this . importConfig . context ) ;
6281 return ;
@@ -67,8 +86,12 @@ export default class ImportTaxonomies extends BaseClass {
6786 // Check if locale-based structure exists before import
6887 this . isLocaleBasedStructure = this . detectAndScanLocaleStructure ( ) ;
6988
70- const progress = this . createSimpleProgress ( this . currentModuleName , taxonomiesCount ) ;
71- progress . updateStatus ( PROCESS_STATUS [ PROCESS_NAMES . TAXONOMIES_IMPORT ] . IMPORTING ) ;
89+ const progress = this . createNestedProgress ( this . currentModuleName ) ;
90+ this . initializeTaxonomiesProgress ( progress , taxonomiesCount , publishJobCount ) ;
91+
92+ progress
93+ . startProcess ( PROCESS_NAMES . TAXONOMIES_IMPORT )
94+ . updateStatus ( PROCESS_STATUS [ PROCESS_NAMES . TAXONOMIES_IMPORT ] . IMPORTING , PROCESS_NAMES . TAXONOMIES_IMPORT ) ;
7295 log . debug ( 'Starting taxonomies import' , this . importConfig . context ) ;
7396
7497 if ( this . isLocaleBasedStructure ) {
@@ -79,6 +102,19 @@ export default class ImportTaxonomies extends BaseClass {
79102 await this . importTaxonomiesLegacy ( ) ;
80103 }
81104
105+ progress . completeProcess ( PROCESS_NAMES . TAXONOMIES_IMPORT , true ) ;
106+
107+ if ( publishJobCount > 0 ) {
108+ progress
109+ . startProcess ( PROCESS_NAMES . TAXONOMIES_PUBLISH )
110+ . updateStatus (
111+ PROCESS_STATUS [ PROCESS_NAMES . TAXONOMIES_PUBLISH ] . PUBLISHING ,
112+ PROCESS_NAMES . TAXONOMIES_PUBLISH ,
113+ ) ;
114+ await this . processTaxonomyPublishing ( ) ;
115+ progress . completeProcess ( PROCESS_NAMES . TAXONOMIES_PUBLISH , true ) ;
116+ }
117+
82118 this . createSuccessAndFailedFile ( ) ;
83119 this . completeProgressWithMessage ( ) ;
84120 } catch ( error ) {
@@ -87,6 +123,8 @@ export default class ImportTaxonomies extends BaseClass {
87123 }
88124 }
89125
126+ // --- Import ---
127+
90128 /**
91129 * create taxonomy and enter success & failure related data into taxonomies mapper file
92130 * @method importTaxonomies
@@ -344,6 +382,121 @@ export default class ImportTaxonomies extends BaseClass {
344382 return true ;
345383 }
346384
385+ // --- Progress ---
386+
387+ /**
388+ * Registers nested progress for taxonomy import and optional taxonomy publish when publish jobs exist.
389+ */
390+ initializeTaxonomiesProgress ( progress : CLIProgressManager , taxonomyCount : number , publishJobCount : number ) : void {
391+ progress . addProcess ( PROCESS_NAMES . TAXONOMIES_IMPORT , taxonomyCount ) ;
392+ if ( publishJobCount > 0 ) {
393+ progress . addProcess ( PROCESS_NAMES . TAXONOMIES_PUBLISH , publishJobCount ) ;
394+ }
395+ }
396+
397+ // --- Publish ---
398+
399+ private countPublishEligibleTaxonomies ( envMapper : Record < string , string > ) : number {
400+ let count = 0 ;
401+ for ( const key of Object . keys ( this . taxonomies || { } ) ) {
402+ const meta = this . taxonomies [ key ] as Record < string , any > ;
403+ const taxonomyUid = meta ?. uid || key ;
404+ const filePath = this . findTaxonomyFilePath ( taxonomyUid ) ;
405+ if ( ! filePath ) continue ;
406+
407+ const details = this . loadTaxonomyFile ( filePath ) ;
408+ const tax = details ?. taxonomy as Record < string , any > | undefined ;
409+ if ( ! tax ?. publish_details ?. length || ! tax ?. locale ) continue ;
410+
411+ const hasMapped = ( tax . publish_details as any [ ] ) . some (
412+ ( p : any ) => p ?. environment && envMapper [ String ( p . environment ) ] ,
413+ ) ;
414+ if ( hasMapped ) count ++ ;
415+ }
416+ return count ;
417+ }
418+
419+ private collectTaxonomyPublishJobs ( ) : Array < { taxonomy : Record < string , any > } > {
420+ const jobs : Array < { taxonomy : Record < string , any > } > = [ ] ;
421+ const seen = new Set < string > ( ) ;
422+
423+ for ( const key of Object . keys ( this . taxonomies || { } ) ) {
424+ const meta = this . taxonomies [ key ] as Record < string , any > ;
425+ const taxonomyUid = meta ?. uid || key ;
426+ if ( seen . has ( taxonomyUid ) ) continue ;
427+
428+ const filePath = this . findTaxonomyFilePath ( taxonomyUid ) ;
429+ if ( ! filePath ) continue ;
430+
431+ const details = this . loadTaxonomyFile ( filePath ) ;
432+ const tax = details ?. taxonomy as Record < string , any > | undefined ;
433+ if ( ! tax ?. publish_details ?. length || ! tax ?. locale ) continue ;
434+
435+ seen . add ( taxonomyUid ) ;
436+ jobs . push ( { taxonomy : tax } ) ;
437+ }
438+
439+ return jobs ;
440+ }
441+
442+ async processTaxonomyPublishing ( ) : Promise < void > {
443+ const envUidMapper = readEnvUidMapperSync ( this . envUidMapperPath , this . importConfig . context ) ;
444+ warnIfEnvMapperEmpty ( envUidMapper , this . importConfig . context ) ;
445+ const jobs = this . collectTaxonomyPublishJobs ( ) ;
446+
447+ if ( jobs . length === 0 ) {
448+ log . debug ( 'No taxonomies with publish_details to publish' , this . importConfig . context ) ;
449+ return ;
450+ }
451+
452+ log . info ( 'Starting taxonomy publishing process' , this . importConfig . context ) ;
453+
454+ const onSuccess = ( { apiData } : any ) => {
455+ const taxonomyUid = apiData ?. items ?. [ 0 ] ?. uid ;
456+ this . progressManager ?. tick (
457+ true ,
458+ `taxonomy published: ${ taxonomyUid } ` ,
459+ null ,
460+ PROCESS_NAMES . TAXONOMIES_PUBLISH ,
461+ ) ;
462+ log . success ( `Published taxonomy '${ taxonomyUid } '` , this . importConfig . context ) ;
463+ } ;
464+
465+ const onReject = ( { error, apiData } : any ) => {
466+ const taxonomyUid = apiData ?. items ?. [ 0 ] ?. uid ;
467+ handleAndLogError (
468+ error ,
469+ { ...this . importConfig . context , taxonomyUid } ,
470+ `Failed to publish taxonomy '${ taxonomyUid } '` ,
471+ ) ;
472+ this . progressManager ?. tick (
473+ false ,
474+ `taxonomy publish: ${ taxonomyUid } ` ,
475+ ( error as Error ) ?. message || `Failed to publish taxonomy '${ taxonomyUid } '` ,
476+ PROCESS_NAMES . TAXONOMIES_PUBLISH ,
477+ ) ;
478+ } ;
479+
480+ await this . makeConcurrentCall (
481+ {
482+ apiContent : jobs as unknown as Record < string , any > [ ] ,
483+ processName : 'publish taxonomies' ,
484+ apiParams : {
485+ serializeData : ( opts : ApiOptions ) => serializePublishTaxonomies ( opts , envUidMapper ) ,
486+ reject : onReject ,
487+ resolve : onSuccess ,
488+ entity : 'publish-taxonomies' ,
489+ includeParamOnCompletion : true ,
490+ } ,
491+ concurrencyLimit : this . importConfig . concurrency || this . importConfig . fetchConcurrency || 1 ,
492+ } ,
493+ undefined ,
494+ false ,
495+ ) ;
496+ }
497+
498+ // --- Mapper output ---
499+
347500 /**
348501 * create taxonomies success and fail in (mapper/taxonomies)
349502 * create terms success and fail in (mapper/taxonomies/terms)
@@ -396,25 +549,36 @@ export default class ImportTaxonomies extends BaseClass {
396549 }
397550 }
398551
399- private async analyzeTaxonomies ( ) : Promise < [ number ] > {
552+ // --- Analyze & prepare ---
553+
554+ private async analyzeTaxonomies ( ) : Promise < [ number , number ] > {
400555 return this . withLoadingSpinner ( 'TAXONOMIES: Analyzing import data...' , async ( ) => {
401556 log . debug ( 'Checking for taxonomies folder existence' , this . importConfig . context ) ;
402557
403- if ( fileHelper . fileExistsSync ( this . taxonomiesFolderPath ) ) {
404- log . debug ( `Found taxonomies folder: ${ this . taxonomiesFolderPath } ` , this . importConfig . context ) ;
405-
406- this . taxonomies = fsUtil . readFile ( join ( this . taxonomiesFolderPath , 'taxonomies.json' ) , true ) as Record <
407- string ,
408- unknown
409- > ;
410-
411- const taxonomyCount = Object . keys ( this . taxonomies || { } ) . length ;
412- log . debug ( `Loaded ${ taxonomyCount } taxonomy items from file` , this . importConfig . context ) ;
413- return [ taxonomyCount ] ;
414- } else {
558+ if ( ! fileHelper . fileExistsSync ( this . taxonomiesFolderPath ) ) {
415559 log . info ( `No Taxonomies Found! - '${ this . taxonomiesFolderPath } '` , this . importConfig . context ) ;
416- return [ 0 ] ;
560+ return [ 0 , 0 ] ;
417561 }
562+
563+ log . debug ( `Found taxonomies folder: ${ this . taxonomiesFolderPath } ` , this . importConfig . context ) ;
564+
565+ this . taxonomies = fsUtil . readFile ( join ( this . taxonomiesFolderPath , 'taxonomies.json' ) , true ) as Record <
566+ string ,
567+ unknown
568+ > ;
569+
570+ this . isLocaleBasedStructure = this . detectAndScanLocaleStructure ( ) ;
571+
572+ const taxonomyCount = Object . keys ( this . taxonomies || { } ) . length ;
573+ const envMapper = readEnvUidMapperSync ( this . envUidMapperPath , this . importConfig . context ) ;
574+ const publishJobCount = this . countPublishEligibleTaxonomies ( envMapper ) ;
575+
576+ log . debug (
577+ `Loaded ${ taxonomyCount } taxonomy items; ${ publishJobCount } eligible for publish (mapped environments).` ,
578+ this . importConfig . context ,
579+ ) ;
580+
581+ return [ taxonomyCount , publishJobCount ] ;
418582 } ) ;
419583 }
420584
0 commit comments