@contentstack/cli-cm-import 1.28.0 → 2.0.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +5 -7
  2. package/lib/commands/cm/stacks/import.d.ts +2 -0
  3. package/lib/commands/cm/stacks/import.js +46 -11
  4. package/lib/config/index.js +0 -1
  5. package/lib/import/module-importer.d.ts +2 -2
  6. package/lib/import/module-importer.js +9 -25
  7. package/lib/import/modules/assets.d.ts +6 -0
  8. package/lib/import/modules/assets.js +102 -25
  9. package/lib/import/modules/base-class.d.ts +17 -0
  10. package/lib/import/modules/base-class.js +45 -0
  11. package/lib/import/modules/content-types.d.ts +7 -10
  12. package/lib/import/modules/content-types.js +132 -68
  13. package/lib/import/modules/custom-roles.d.ts +6 -2
  14. package/lib/import/modules/custom-roles.js +80 -69
  15. package/lib/import/modules/entries.d.ts +7 -0
  16. package/lib/import/modules/entries.js +278 -163
  17. package/lib/import/modules/environments.d.ts +3 -0
  18. package/lib/import/modules/environments.js +69 -38
  19. package/lib/import/modules/extensions.d.ts +3 -0
  20. package/lib/import/modules/extensions.js +99 -64
  21. package/lib/import/modules/global-fields.d.ts +8 -1
  22. package/lib/import/modules/global-fields.js +123 -63
  23. package/lib/import/modules/index.d.ts +1 -0
  24. package/lib/import/modules/index.js +1 -0
  25. package/lib/import/modules/labels.d.ts +3 -0
  26. package/lib/import/modules/labels.js +104 -54
  27. package/lib/import/modules/locales.d.ts +15 -4
  28. package/lib/import/modules/locales.js +194 -94
  29. package/lib/import/modules/marketplace-apps.d.ts +6 -3
  30. package/lib/import/modules/marketplace-apps.js +177 -102
  31. package/lib/import/modules/personalize.d.ts +11 -4
  32. package/lib/import/modules/personalize.js +138 -47
  33. package/lib/import/modules/stack.d.ts +6 -0
  34. package/lib/import/modules/stack.js +71 -27
  35. package/lib/import/modules/taxonomies.d.ts +4 -2
  36. package/lib/import/modules/taxonomies.js +60 -46
  37. package/lib/import/modules/variant-entries.d.ts +7 -4
  38. package/lib/import/modules/variant-entries.js +76 -35
  39. package/lib/import/modules/webhooks.d.ts +3 -0
  40. package/lib/import/modules/webhooks.js +71 -40
  41. package/lib/import/modules/workflows.d.ts +3 -0
  42. package/lib/import/modules/workflows.js +98 -48
  43. package/lib/types/default-config.d.ts +0 -1
  44. package/lib/types/import-config.d.ts +0 -1
  45. package/lib/types/index.d.ts +1 -12
  46. package/lib/utils/backup-handler.js +1 -2
  47. package/lib/utils/constants.d.ts +243 -0
  48. package/lib/utils/constants.js +264 -0
  49. package/lib/utils/import-config-handler.js +2 -7
  50. package/lib/utils/import-path-resolver.d.ts +1 -1
  51. package/lib/utils/import-path-resolver.js +5 -5
  52. package/lib/utils/index.d.ts +1 -1
  53. package/lib/utils/index.js +6 -2
  54. package/lib/utils/marketplace-app-helper.js +3 -8
  55. package/lib/utils/progress-strategy-registry.d.ts +7 -0
  56. package/lib/utils/progress-strategy-registry.js +72 -0
  57. package/lib/utils/setup-branch.js +1 -1
  58. package/oclif.manifest.json +2 -2
  59. package/package.json +2 -2
  60. package/lib/import/modules-js/assets.d.ts +0 -33
  61. package/lib/import/modules-js/assets.js +0 -428
  62. package/lib/import/modules-js/content-types.d.ts +0 -34
  63. package/lib/import/modules-js/content-types.js +0 -204
  64. package/lib/import/modules-js/custom-roles.d.ts +0 -15
  65. package/lib/import/modules-js/custom-roles.js +0 -143
  66. package/lib/import/modules-js/entries.d.ts +0 -54
  67. package/lib/import/modules-js/entries.js +0 -1280
  68. package/lib/import/modules-js/environments.d.ts +0 -13
  69. package/lib/import/modules-js/environments.js +0 -85
  70. package/lib/import/modules-js/extensions.d.ts +0 -18
  71. package/lib/import/modules-js/extensions.js +0 -86
  72. package/lib/import/modules-js/global-fields.d.ts +0 -13
  73. package/lib/import/modules-js/global-fields.js +0 -106
  74. package/lib/import/modules-js/index.d.ts +0 -1
  75. package/lib/import/modules-js/index.js +0 -33
  76. package/lib/import/modules-js/labels.d.ts +0 -20
  77. package/lib/import/modules-js/labels.js +0 -148
  78. package/lib/import/modules-js/locales.d.ts +0 -24
  79. package/lib/import/modules-js/locales.js +0 -196
  80. package/lib/import/modules-js/marketplace-apps.d.ts +0 -63
  81. package/lib/import/modules-js/marketplace-apps.js +0 -429
  82. package/lib/import/modules-js/webhooks.d.ts +0 -17
  83. package/lib/import/modules-js/webhooks.js +0 -85
  84. package/lib/import/modules-js/workflows.d.ts +0 -19
  85. package/lib/import/modules-js/workflows.js +0 -170
  86. package/lib/utils/log.d.ts +0 -12
  87. package/lib/utils/log.js +0 -31
package/README.md CHANGED
@@ -47,7 +47,7 @@ $ npm install -g @contentstack/cli-cm-import
47
47
  $ csdx COMMAND
48
48
  running command...
49
49
  $ csdx (--version)
50
- @contentstack/cli-cm-import/1.28.0 linux-x64 node-v22.19.0
50
+ @contentstack/cli-cm-import/2.0.0-beta linux-x64 node-v22.20.0
51
51
  $ csdx --help [COMMAND]
52
52
  USAGE
53
53
  $ csdx COMMAND
@@ -91,9 +91,8 @@ FLAGS
91
91
  extensions, marketplace-apps, global-fields, labels, locales, webhooks,
92
92
  workflows, custom-roles, personalize projects, and taxonomies.
93
93
  -y, --yes [optional] Force override all Marketplace prompts.
94
- --branch-alias=<value> The alias of the branch where you want to import your content. If you don't
95
- mention the branch alias, then by default the content will be imported to the
96
- main branch.
94
+ --branch-alias=<value> Specify the branch alias where you want to import your content. If not
95
+ specified, the content is imported into the main branch by default.
97
96
  --exclude-global-modules Excludes the branch-independent module from the import operation.
98
97
  --import-webhook-status=<option> [default: disable] [default: disable] (optional) This webhook state keeps the
99
98
  same state of webhooks as the source stack. <options: disable|current>
@@ -160,9 +159,8 @@ FLAGS
160
159
  extensions, marketplace-apps, global-fields, labels, locales, webhooks,
161
160
  workflows, custom-roles, personalize projects, and taxonomies.
162
161
  -y, --yes [optional] Force override all Marketplace prompts.
163
- --branch-alias=<value> The alias of the branch where you want to import your content. If you don't
164
- mention the branch alias, then by default the content will be imported to the
165
- main branch.
162
+ --branch-alias=<value> Specify the branch alias where you want to import your content. If not
163
+ specified, the content is imported into the main branch by default.
166
164
  --exclude-global-modules Excludes the branch-independent module from the import operation.
167
165
  --import-webhook-status=<option> [default: disable] [default: disable] (optional) This webhook state keeps the
168
166
  same state of webhooks as the source stack. <options: disable|current>
@@ -7,5 +7,7 @@ export default class ImportCommand extends Command {
7
7
  static aliases: string[];
8
8
  static usage: string;
9
9
  run(): Promise<void>;
10
+ private logAndPrintErrorDetails;
11
+ private logSuccessAndBackupMessages;
10
12
  private createImportContext;
11
13
  }
@@ -26,6 +26,12 @@ class ImportCommand extends cli_command_1.Command {
26
26
  if (this.personalizeUrl)
27
27
  importConfig.modules.personalize.baseURL[importConfig.region.name] = this.personalizeUrl;
28
28
  const managementAPIClient = await (0, cli_utilities_1.managementSDKClient)(importConfig);
29
+ if (flags.branch) {
30
+ cli_utilities_1.CLIProgressManager.initializeGlobalSummary(`IMPORT-${flags.branch}`, flags.branch, `Importing content into "${flags.branch}" branch...`);
31
+ }
32
+ else {
33
+ cli_utilities_1.CLIProgressManager.initializeGlobalSummary(`IMPORT`, flags.branch, 'Importing content...');
34
+ }
29
35
  const moduleImporter = new import_1.ModuleImporter(managementAPIClient, importConfig);
30
36
  const result = await moduleImporter.start();
31
37
  backupDir = importConfig.backupDir;
@@ -35,18 +41,48 @@ class ImportCommand extends cli_command_1.Command {
35
41
  : `The content has been imported to the stack ${importConfig.apiKey} successfully!`;
36
42
  cli_utilities_1.log.success(successMessage, importConfig.context);
37
43
  }
38
- cli_utilities_1.log.success(`The log has been stored at '${(0, cli_utilities_1.getLogPath)()}'`, importConfig.context);
39
- cli_utilities_1.log.info(`The backup content has been stored at '${backupDir}'`, importConfig.context);
44
+ cli_utilities_1.CLIProgressManager.printGlobalSummary();
45
+ this.logSuccessAndBackupMessages(backupDir, importConfig);
46
+ // Clear progress module setting now that import is complete
47
+ (0, cli_utilities_1.clearProgressModuleSetting)();
40
48
  }
41
49
  catch (error) {
50
+ // Clear progress module setting even on error
51
+ (0, cli_utilities_1.clearProgressModuleSetting)();
42
52
  (0, cli_utilities_1.handleAndLogError)(error);
43
- cli_utilities_1.log.info(`The log has been stored at '${(0, cli_utilities_1.getLogPath)()}'`);
44
- if (importConfig === null || importConfig === void 0 ? void 0 : importConfig.backupDir) {
45
- cli_utilities_1.log.info(`The backup content has been stored at '${importConfig === null || importConfig === void 0 ? void 0 : importConfig.backupDir}'`);
46
- }
47
- else {
48
- cli_utilities_1.log.info('No backup directory was created due to early termination');
49
- }
53
+ this.logAndPrintErrorDetails(error, importConfig);
54
+ }
55
+ }
56
+ logAndPrintErrorDetails(error, importConfig) {
57
+ var _a;
58
+ cli_utilities_1.cliux.print('\n');
59
+ const logPath = (0, cli_utilities_1.getLogPath)();
60
+ const logMsg = `The log has been stored at '${logPath}'`;
61
+ const backupDir = importConfig === null || importConfig === void 0 ? void 0 : importConfig.backupDir;
62
+ const backupDirMsg = backupDir
63
+ ? `The backup content has been stored at '${backupDir}'`
64
+ : 'No backup directory was created due to early termination';
65
+ cli_utilities_1.log.info(logMsg);
66
+ cli_utilities_1.log.info(backupDirMsg);
67
+ const showConsoleLogs = (_a = cli_utilities_1.configHandler.get('log')) === null || _a === void 0 ? void 0 : _a.showConsoleLogs;
68
+ if (!showConsoleLogs) {
69
+ cli_utilities_1.cliux.print(`Error: ${error}`, { color: 'red' });
70
+ cli_utilities_1.cliux.print(logMsg, { color: 'blue' });
71
+ cli_utilities_1.cliux.print(backupDirMsg, { color: 'blue' });
72
+ }
73
+ }
74
+ logSuccessAndBackupMessages(backupDir, importConfig) {
75
+ var _a;
76
+ cli_utilities_1.cliux.print('\n');
77
+ const logPath = (0, cli_utilities_1.getLogPath)();
78
+ const logMsg = `The log has been stored at '${logPath}'`;
79
+ const backupDirMsg = `The backup content has been stored at '${backupDir}'`;
80
+ cli_utilities_1.log.success(logMsg, importConfig.context);
81
+ cli_utilities_1.log.info(backupDirMsg, importConfig.context);
82
+ const showConsoleLogs = (_a = cli_utilities_1.configHandler.get('log')) === null || _a === void 0 ? void 0 : _a.showConsoleLogs;
83
+ if (!showConsoleLogs) {
84
+ cli_utilities_1.cliux.print(logMsg, { color: 'blue' });
85
+ cli_utilities_1.cliux.print(backupDirMsg, { color: 'blue' });
50
86
  }
51
87
  }
52
88
  // Create export context object
@@ -56,7 +92,6 @@ class ImportCommand extends cli_command_1.Command {
56
92
  command: ((_b = (_a = this.context) === null || _a === void 0 ? void 0 : _a.info) === null || _b === void 0 ? void 0 : _b.command) || 'cm:stacks:import',
57
93
  module: '',
58
94
  userId: cli_utilities_1.configHandler.get('userUid') || '',
59
- email: cli_utilities_1.configHandler.get('email') || '',
60
95
  sessionId: (_c = this.context) === null || _c === void 0 ? void 0 : _c.sessionId,
61
96
  apiKey: apiKey || '',
62
97
  orgId: cli_utilities_1.configHandler.get('oauthOrgUid') || '',
@@ -133,7 +168,7 @@ ImportCommand.flags = {
133
168
  exclusive: ['branch-alias'],
134
169
  }),
135
170
  'branch-alias': cli_utilities_1.flags.string({
136
- description: "The alias of the branch where you want to import your content. If you don't mention the branch alias, then by default the content will be imported to the main branch.",
171
+ description: "Specify the branch alias where you want to import your content. If not specified, the content is imported into the main branch by default.",
137
172
  exclusive: ['branch'],
138
173
  }),
139
174
  'import-webhook-status': cli_utilities_1.flags.string({
@@ -436,7 +436,6 @@ const config = {
436
436
  getEncryptionKeyMaxRetry: 3,
437
437
  // useBackedupDir: '',
438
438
  // backupConcurrency: 10,
439
- onlyTSModules: ['taxonomies', 'personalize', 'variant-entries', 'stack'],
440
439
  auditConfig: {
441
440
  noLog: false,
442
441
  skipConfirm: true,
@@ -1,4 +1,4 @@
1
- import { ContentstackClient, Logger } from '@contentstack/cli-utilities';
1
+ import { ContentstackClient } from '@contentstack/cli-utilities';
2
2
  import { ImportConfig, Modules } from '../types';
3
3
  declare class ModuleImporter {
4
4
  private managementAPIClient;
@@ -20,6 +20,6 @@ declare class ModuleImporter {
20
20
  * @returns The function `auditImportData()` returns a boolean value. It returns `true` if there is a
21
21
  * fix available and the user confirms to proceed with the fix, otherwise it returns `false`.
22
22
  */
23
- auditImportData(logger: Logger): Promise<boolean>;
23
+ auditImportData(): Promise<boolean>;
24
24
  }
25
25
  export default ModuleImporter;
@@ -6,7 +6,6 @@ const cli_audit_1 = require("@contentstack/cli-audit");
6
6
  const messages_1 = tslib_1.__importStar(require("@contentstack/cli-audit/lib/messages"));
7
7
  const cli_utilities_1 = require("@contentstack/cli-utilities");
8
8
  const modules_1 = tslib_1.__importDefault(require("./modules"));
9
- const modules_js_1 = tslib_1.__importDefault(require("./modules-js"));
10
9
  const utils_1 = require("../utils");
11
10
  class ModuleImporter {
12
11
  constructor(managementAPIClient, importConfig) {
@@ -41,13 +40,11 @@ class ModuleImporter {
41
40
  // To support the old config
42
41
  this.importConfig.data = backupDir;
43
42
  }
44
- // NOTE init log
45
- const logger = (0, utils_1.initLogger)(this.importConfig);
46
43
  // NOTE audit and fix the import content.
47
44
  if (!this.importConfig.skipAudit &&
48
45
  (!this.importConfig.moduleName ||
49
46
  ['content-types', 'global-fields', 'entries', 'extensions', 'workflows', 'custom-roles', 'assets'].includes(this.importConfig.moduleName))) {
50
- if (!(await this.auditImportData(logger))) {
47
+ if (!(await this.auditImportData())) {
51
48
  return { noSuccessMsg: true };
52
49
  }
53
50
  }
@@ -60,7 +57,7 @@ class ModuleImporter {
60
57
  return this.import();
61
58
  }
62
59
  async import() {
63
- cli_utilities_1.log.info(`Starting to import content version ${this.importConfig.contentVersion}`, this.importConfig.context);
60
+ cli_utilities_1.log.info(`Starting to import`, this.importConfig.context);
64
61
  // checks for single module or all modules
65
62
  if (this.importConfig.singleModuleImport) {
66
63
  return this.importByModuleByName(this.importConfig.moduleName);
@@ -71,24 +68,11 @@ class ModuleImporter {
71
68
  cli_utilities_1.log.info(`Starting import of ${moduleName} module`, this.importConfig.context);
72
69
  // import the modules by name
73
70
  // calls the module runner which inturn calls the module itself
74
- // NOTE: Implement a mechanism to determine whether module is new or old
75
- if (this.importConfig.contentVersion === 2) {
76
- return (0, modules_1.default)({
77
- stackAPIClient: this.stackAPIClient,
78
- importConfig: this.importConfig,
79
- moduleName,
80
- });
81
- }
82
- else {
83
- //NOTE - new modules support only ts
84
- if (this.importConfig.onlyTSModules.indexOf(moduleName) === -1) {
85
- return (0, modules_js_1.default)({
86
- stackAPIClient: this.stackAPIClient,
87
- importConfig: this.importConfig,
88
- moduleName,
89
- });
90
- }
91
- }
71
+ return (0, modules_1.default)({
72
+ stackAPIClient: this.stackAPIClient,
73
+ importConfig: this.importConfig,
74
+ moduleName,
75
+ });
92
76
  }
93
77
  async importAllModules() {
94
78
  // use the algorithm to determine the parallel and sequential execution of modules
@@ -120,7 +104,7 @@ class ModuleImporter {
120
104
  * @returns The function `auditImportData()` returns a boolean value. It returns `true` if there is a
121
105
  * fix available and the user confirms to proceed with the fix, otherwise it returns `false`.
122
106
  */
123
- async auditImportData(logger) {
107
+ async auditImportData() {
124
108
  const basePath = (0, path_1.resolve)(this.importConfig.cliLogsPath || this.importConfig.backupDir, 'logs', 'audit');
125
109
  const auditConfig = this.importConfig.auditConfig;
126
110
  auditConfig.config.basePath = basePath;
@@ -151,7 +135,7 @@ class ModuleImporter {
151
135
  if (result) {
152
136
  const { hasFix, config } = result;
153
137
  if (hasFix) {
154
- logger.log((0, messages_1.$t)(messages_1.default.FINAL_REPORT_PATH, { path: config.reportPath }), 'warn');
138
+ cli_utilities_1.log.warn((0, messages_1.$t)(messages_1.default.FINAL_REPORT_PATH, { path: config.reportPath }), this.importConfig.context);
155
139
  if (this.importConfig.forceStopMarketplaceAppsPrompt ||
156
140
  (await cli_utilities_1.cliux.inquire({
157
141
  type: 'confirm',
@@ -60,4 +60,10 @@ export default class ImportAssets extends BaseClass {
60
60
  * @returns {Array<Record<string, any>>} Array<Record<string, any>>
61
61
  */
62
62
  constructFolderImportOrder(folders: any): Array<Record<string, any>>;
63
+ private analyzeImportData;
64
+ private initializeProgress;
65
+ private countFolders;
66
+ private countAssets;
67
+ private countPublishableAssets;
68
+ private executeStep;
63
69
  }
@@ -24,7 +24,8 @@ class ImportAssets extends base_class_1.default {
24
24
  this.assetsUidMap = {};
25
25
  this.assetsUrlMap = {};
26
26
  this.assetsFolderMap = {};
27
- this.importConfig.context.module = 'assets';
27
+ this.importConfig.context.module = utils_1.MODULE_CONTEXTS.ASSETS;
28
+ this.currentModuleName = utils_1.MODULE_NAMES[utils_1.MODULE_CONTEXTS.ASSETS];
28
29
  this.assetsPath = (0, node_path_1.join)(this.importConfig.backupDir, 'assets');
29
30
  this.mapperDirPath = (0, node_path_1.join)(this.importConfig.backupDir, 'mapper', 'assets');
30
31
  this.assetUidMapperPath = (0, node_path_1.join)(this.mapperDirPath, 'uid-mapping.json');
@@ -40,31 +41,35 @@ class ImportAssets extends base_class_1.default {
40
41
  */
41
42
  async start() {
42
43
  try {
43
- // NOTE Step 1: Import folders and create uid mapping file
44
- cli_utilities_1.log.debug('Starting folder import process', this.importConfig.context);
45
- await this.importFolders();
46
- // NOTE Step 2: Import versioned assets and create it mapping files (uid, url)
47
- if (this.assetConfig.includeVersionedAssets) {
48
- const versionsPath = `${this.assetsPath}/versions`;
49
- if ((0, node_fs_1.existsSync)(versionsPath)) {
50
- cli_utilities_1.log.debug('Starting versioned assets import', this.importConfig.context);
51
- await this.importAssets(true);
52
- }
53
- else {
54
- cli_utilities_1.log.info('No Versioned assets found to import', this.importConfig.context);
55
- }
44
+ cli_utilities_1.log.debug('Starting assets import process...', this.importConfig.context);
45
+ // Step 1: Analyze import data
46
+ const [foldersCount, assetsCount, versionedAssetsCount, publishableAssetsCount] = await this.withLoadingSpinner('ASSETS: Analyzing import data...', () => this.analyzeImportData());
47
+ // Step 2: Initialize progress tracking
48
+ const progress = this.createNestedProgress(this.currentModuleName);
49
+ this.initializeProgress(progress, {
50
+ foldersCount,
51
+ assetsCount,
52
+ versionedAssetsCount,
53
+ publishableAssetsCount,
54
+ });
55
+ // Step 3: Perform import steps based on data
56
+ if (foldersCount > 0) {
57
+ await this.executeStep(progress, utils_1.PROCESS_NAMES.ASSET_FOLDERS, utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.ASSET_FOLDERS].CREATING, () => this.importFolders());
58
+ }
59
+ if (this.assetConfig.includeVersionedAssets && versionedAssetsCount > 0) {
60
+ await this.executeStep(progress, utils_1.PROCESS_NAMES.ASSET_VERSIONS, utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.ASSET_VERSIONS].IMPORTING, () => this.importAssets(true));
61
+ }
62
+ if (assetsCount > 0) {
63
+ await this.executeStep(progress, utils_1.PROCESS_NAMES.ASSET_UPLOAD, utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.ASSET_UPLOAD].UPLOADING, () => this.importAssets());
56
64
  }
57
- // NOTE Step 3: Import Assets and create it mapping files (uid, url)
58
- cli_utilities_1.log.debug('Starting assets import', this.importConfig.context);
59
- await this.importAssets();
60
- // NOTE Step 4: Publish assets
61
- if (!this.importConfig.skipAssetsPublish) {
62
- cli_utilities_1.log.debug('Starting assets publishing', this.importConfig.context);
63
- await this.publish();
65
+ if (!this.importConfig.skipAssetsPublish && publishableAssetsCount > 0) {
66
+ await this.executeStep(progress, utils_1.PROCESS_NAMES.ASSET_PUBLISH, utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.ASSET_PUBLISH].PUBLISHING, () => this.publish());
64
67
  }
68
+ this.completeProgress(true);
65
69
  cli_utilities_1.log.success('Assets imported successfully!', this.importConfig.context);
66
70
  }
67
71
  catch (error) {
72
+ this.completeProgress(false, (error === null || error === void 0 ? void 0 : error.message) || 'Asset import failed');
68
73
  (0, cli_utilities_1.handleAndLogError)(error, Object.assign({}, this.importConfig.context));
69
74
  }
70
75
  }
@@ -84,11 +89,15 @@ class ImportAssets extends base_class_1.default {
84
89
  const batches = this.constructFolderImportOrder(folders);
85
90
  cli_utilities_1.log.debug(`Organized folders into ${batches.length} batches for import`, this.importConfig.context);
86
91
  const onSuccess = ({ response, apiData: { uid, name } = { uid: null, name: '' } }) => {
92
+ var _a;
87
93
  this.assetsFolderMap[uid] = response.uid;
94
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(true, `folder: ${name || uid}`, null, utils_1.PROCESS_NAMES.ASSET_FOLDERS);
88
95
  cli_utilities_1.log.debug(`Created folder: ${name} (Mapped ${uid} → ${response.uid})`, this.importConfig.context);
89
96
  cli_utilities_1.log.success(`Created folder: '${name}'`, this.importConfig.context);
90
97
  };
91
- const onReject = ({ error, apiData: { name } = { name: '' } }) => {
98
+ const onReject = ({ error, apiData: { name, uid } = { name: '', uid: '' } }) => {
99
+ var _a;
100
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(false, `folder: ${name || uid}`, (error === null || error === void 0 ? void 0 : error.message) || utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.ASSET_FOLDERS].FAILED, utils_1.PROCESS_NAMES.ASSET_FOLDERS);
92
101
  cli_utilities_1.log.error(`${name} folder creation failed.!`, this.importConfig.context);
93
102
  (0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.importConfig.context), { name }));
94
103
  };
@@ -134,18 +143,23 @@ class ImportAssets extends base_class_1.default {
134
143
  const processName = isVersion ? 'import versioned assets' : 'import assets';
135
144
  const indexFileName = isVersion ? 'versioned-assets.json' : 'assets.json';
136
145
  const basePath = isVersion ? (0, node_path_1.join)(this.assetsPath, 'versions') : this.assetsPath;
146
+ const progressProcessName = isVersion ? utils_1.PROCESS_NAMES.ASSET_VERSIONS : utils_1.PROCESS_NAMES.ASSET_UPLOAD;
137
147
  cli_utilities_1.log.debug(`Importing ${processName} from ${basePath}`, this.importConfig.context);
138
148
  const fs = new cli_utilities_1.FsUtility({ basePath, indexFileName });
139
149
  const indexer = fs.indexFileContent;
140
150
  const indexerCount = (0, values_1.default)(indexer).length;
141
151
  cli_utilities_1.log.debug(`Found ${indexerCount} asset chunks to process`, this.importConfig.context);
142
152
  const onSuccess = ({ response = {}, apiData: { uid, url, title } = undefined }) => {
153
+ var _a;
143
154
  this.assetsUidMap[uid] = response.uid;
144
155
  this.assetsUrlMap[url] = response.url;
156
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(true, `asset: ${title || uid}`, null, progressProcessName);
145
157
  cli_utilities_1.log.debug(`Created asset: ${title} (Mapped ${uid} → ${response.uid})`, this.importConfig.context);
146
158
  cli_utilities_1.log.success(`Created asset: '${title}'`, this.importConfig.context);
147
159
  };
148
- const onReject = ({ error, apiData: { title } = undefined }) => {
160
+ const onReject = ({ error, apiData: { title, uid } = undefined }) => {
161
+ var _a;
162
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(false, `asset: ${title || uid}`, (error === null || error === void 0 ? void 0 : error.message) || utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.ASSET_UPLOAD].FAILED, progressProcessName);
149
163
  cli_utilities_1.log.error(`${title} asset upload failed.!`, this.importConfig.context);
150
164
  (0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.importConfig.context), { title }));
151
165
  };
@@ -196,12 +210,12 @@ class ImportAssets extends base_class_1.default {
196
210
  }
197
211
  if (!isVersion) {
198
212
  if (!(0, isEmpty_1.default)(this.assetsUidMap)) {
199
- const uidMappingCount = Object.keys(this.assetsUidMap).length;
213
+ const uidMappingCount = Object.keys(this.assetsUidMap || {}).length;
200
214
  cli_utilities_1.log.debug(`Writing ${uidMappingCount} UID mappings`, this.importConfig.context);
201
215
  this.fs.writeFile(this.assetUidMapperPath, this.assetsUidMap);
202
216
  }
203
217
  if (!(0, isEmpty_1.default)(this.assetsUrlMap)) {
204
- const urlMappingCount = Object.keys(this.assetsUrlMap).length;
218
+ const urlMappingCount = Object.keys(this.assetsUrlMap || {}).length;
205
219
  cli_utilities_1.log.debug(`Writing ${urlMappingCount} URL mappings`, this.importConfig.context);
206
220
  this.fs.writeFile(this.assetUrlMapperPath, this.assetsUrlMap);
207
221
  }
@@ -254,9 +268,13 @@ class ImportAssets extends base_class_1.default {
254
268
  const indexerCount = (0, values_1.default)(indexer).length;
255
269
  cli_utilities_1.log.debug(`Found ${indexerCount} asset chunks to publish`, this.importConfig.context);
256
270
  const onSuccess = ({ apiData: { uid, title } = undefined }) => {
271
+ var _a;
272
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(true, `published: ${title || uid}`, null, utils_1.PROCESS_NAMES.ASSET_PUBLISH);
257
273
  cli_utilities_1.log.success(`Asset '${uid}: ${title}' published successfully`, this.importConfig.context);
258
274
  };
259
275
  const onReject = ({ error, apiData: { uid, title } = undefined }) => {
276
+ var _a;
277
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(false, `publish failed: ${title || uid}`, (error === null || error === void 0 ? void 0 : error.message) || utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.ASSET_PUBLISH].FAILED, utils_1.PROCESS_NAMES.ASSET_PUBLISH);
260
278
  cli_utilities_1.log.error(`Asset '${uid}: ${title}' not published`, this.importConfig.context);
261
279
  (0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.importConfig.context), { uid, title }));
262
280
  };
@@ -354,5 +372,64 @@ class ImportAssets extends base_class_1.default {
354
372
  }
355
373
  return importOrder;
356
374
  }
375
+ async analyzeImportData() {
376
+ const foldersCount = this.countFolders();
377
+ const assetsCount = await this.countAssets(this.assetsPath, 'assets.json');
378
+ let versionedAssetsCount = 0;
379
+ if (this.assetConfig.includeVersionedAssets && (0, node_fs_1.existsSync)(`${this.assetsPath}/versions`)) {
380
+ versionedAssetsCount = await this.countAssets(`${this.assetsPath}/versions`, 'versioned-assets.json');
381
+ }
382
+ let publishableAssetsCount = 0;
383
+ if (!this.importConfig.skipAssetsPublish) {
384
+ publishableAssetsCount = await this.countPublishableAssets();
385
+ }
386
+ cli_utilities_1.log.debug(`Analysis complete: ${foldersCount} folders, ${assetsCount} assets, ${versionedAssetsCount} versioned, ${publishableAssetsCount} publishable`, this.importConfig.context);
387
+ return [foldersCount, assetsCount, versionedAssetsCount, publishableAssetsCount];
388
+ }
389
+ initializeProgress(progress, counts) {
390
+ const { foldersCount, assetsCount, versionedAssetsCount, publishableAssetsCount } = counts;
391
+ if (foldersCount > 0) {
392
+ progress.addProcess(utils_1.PROCESS_NAMES.ASSET_FOLDERS, foldersCount);
393
+ }
394
+ if (versionedAssetsCount > 0) {
395
+ progress.addProcess(utils_1.PROCESS_NAMES.ASSET_VERSIONS, versionedAssetsCount);
396
+ }
397
+ if (assetsCount > 0) {
398
+ progress.addProcess(utils_1.PROCESS_NAMES.ASSET_UPLOAD, assetsCount);
399
+ }
400
+ if (publishableAssetsCount > 0) {
401
+ progress.addProcess(utils_1.PROCESS_NAMES.ASSET_PUBLISH, publishableAssetsCount);
402
+ }
403
+ }
404
+ countFolders() {
405
+ const foldersPath = (0, node_path_1.resolve)(this.assetsRootPath, 'folders.json');
406
+ const folders = this.fs.readFile(foldersPath) || [];
407
+ return Array.isArray(folders) ? folders.length : 0;
408
+ }
409
+ async countAssets(basePath, indexFileName) {
410
+ const fsUtil = new cli_utilities_1.FsUtility({ basePath, indexFileName });
411
+ let count = 0;
412
+ for (const _ of (0, values_1.default)(fsUtil.indexFileContent)) {
413
+ const chunkData = await fsUtil.readChunkFiles.next().catch(() => ({}));
414
+ count += (0, values_1.default)(chunkData).length;
415
+ }
416
+ return count;
417
+ }
418
+ async countPublishableAssets() {
419
+ const fsUtil = new cli_utilities_1.FsUtility({ basePath: this.assetsPath, indexFileName: 'assets.json' });
420
+ let count = 0;
421
+ for (const _ of (0, values_1.default)(fsUtil.indexFileContent)) {
422
+ const chunkData = await fsUtil.readChunkFiles.next().catch(() => ({}));
423
+ const publishableAssets = (0, filter_1.default)((0, values_1.default)(chunkData), ({ publish_details }) => !(0, isEmpty_1.default)(publish_details));
424
+ count += publishableAssets.length;
425
+ }
426
+ return count;
427
+ }
428
+ async executeStep(progress, name, status, action) {
429
+ progress.startProcess(name).updateStatus(status, name);
430
+ cli_utilities_1.log.debug(`Starting ${name.toLowerCase()}`, this.importConfig.context);
431
+ await action();
432
+ progress.completeProcess(name, true);
433
+ }
357
434
  }
358
435
  exports.default = ImportAssets;
@@ -1,4 +1,5 @@
1
1
  import { Stack } from '@contentstack/management/types/stack';
2
+ import { CLIProgressManager } from '@contentstack/cli-utilities';
2
3
  import { ImportConfig, ModuleClassParams } from '../../types';
3
4
  export type AdditionalKeys = {
4
5
  backupDir: string;
@@ -36,8 +37,24 @@ export default abstract class BaseClass {
36
37
  readonly client: Stack;
37
38
  importConfig: ImportConfig;
38
39
  modulesConfig: any;
40
+ protected progressManager: CLIProgressManager | null;
41
+ protected currentModuleName: string;
39
42
  constructor({ importConfig, stackAPIClient }: Omit<ModuleClassParams, 'moduleName'>);
40
43
  get stack(): Stack;
44
+ static printFinalSummary(): void;
45
+ /**
46
+ * Create simple progress manager
47
+ */
48
+ protected createSimpleProgress(moduleName: string, total?: number): CLIProgressManager;
49
+ /**
50
+ * Create nested progress manager
51
+ */
52
+ protected createNestedProgress(moduleName: string): CLIProgressManager;
53
+ /**
54
+ * Complete progress manager
55
+ */
56
+ protected completeProgress(success?: boolean, error?: string): void;
57
+ protected withLoadingSpinner<T>(message: string, action: () => Promise<T>): Promise<T>;
41
58
  /**
42
59
  * @method delay
43
60
  * @param {number} ms number
@@ -12,6 +12,8 @@ const cli_utilities_1 = require("@contentstack/cli-utilities");
12
12
  const cloneDeep_1 = tslib_1.__importDefault(require("lodash/cloneDeep"));
13
13
  class BaseClass {
14
14
  constructor({ importConfig, stackAPIClient }) {
15
+ this.progressManager = null;
16
+ this.currentModuleName = '';
15
17
  this.client = stackAPIClient;
16
18
  this.importConfig = importConfig;
17
19
  this.modulesConfig = importConfig.modules;
@@ -19,6 +21,49 @@ class BaseClass {
19
21
  get stack() {
20
22
  return this.client;
21
23
  }
24
+ static printFinalSummary() {
25
+ cli_utilities_1.CLIProgressManager.printGlobalSummary();
26
+ }
27
+ /**
28
+ * Create simple progress manager
29
+ */
30
+ createSimpleProgress(moduleName, total) {
31
+ var _a;
32
+ this.currentModuleName = moduleName;
33
+ const logConfig = cli_utilities_1.configHandler.get('log') || {};
34
+ const showConsoleLogs = (_a = logConfig.showConsoleLogs) !== null && _a !== void 0 ? _a : false; // Default to true for better UX
35
+ this.progressManager = cli_utilities_1.CLIProgressManager.createSimple(moduleName, total, showConsoleLogs);
36
+ return this.progressManager;
37
+ }
38
+ /**
39
+ * Create nested progress manager
40
+ */
41
+ createNestedProgress(moduleName) {
42
+ var _a;
43
+ this.currentModuleName = moduleName;
44
+ const logConfig = cli_utilities_1.configHandler.get('log') || {};
45
+ const showConsoleLogs = (_a = logConfig.showConsoleLogs) !== null && _a !== void 0 ? _a : false; // Default to true for better UX
46
+ this.progressManager = cli_utilities_1.CLIProgressManager.createNested(moduleName, showConsoleLogs);
47
+ return this.progressManager;
48
+ }
49
+ /**
50
+ * Complete progress manager
51
+ */
52
+ completeProgress(success = true, error) {
53
+ var _a;
54
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.complete(success, error);
55
+ this.progressManager = null;
56
+ }
57
+ async withLoadingSpinner(message, action) {
58
+ var _a;
59
+ const logConfig = cli_utilities_1.configHandler.get('log') || {};
60
+ const showConsoleLogs = (_a = logConfig.showConsoleLogs) !== null && _a !== void 0 ? _a : false;
61
+ if (showConsoleLogs) {
62
+ // If console logs are enabled, don't show spinner, just execute the action
63
+ return await action();
64
+ }
65
+ return await cli_utilities_1.CLIProgressManager.withLoadingSpinner(message, action);
66
+ }
22
67
  /**
23
68
  * @method delay
24
69
  * @param {number} ms number
@@ -1,14 +1,8 @@
1
- /*!
2
- * Contentstack Import
3
- * Copyright (c) 2024 Contentstack LLC
4
- * MIT Licensed
5
- */
6
1
  import { ModuleClassParams } from '../../types';
7
2
  import BaseClass, { ApiOptions } from './base-class';
8
3
  export default class ContentTypesImport extends BaseClass {
9
4
  private cTsMapperPath;
10
5
  private cTsFolderPath;
11
- private cTsFailsPath;
12
6
  private cTsSuccessPath;
13
7
  private gFsPendingPath;
14
8
  private pendingGFs;
@@ -16,12 +10,8 @@ export default class ContentTypesImport extends BaseClass {
16
10
  private gFsFolderPath;
17
11
  private gFsMapperFolderPath;
18
12
  private gFs;
19
- private failedCTs;
20
13
  private createdCTs;
21
14
  private cTs;
22
- private cTsUidMapper;
23
- private config;
24
- private stackAPIClient;
25
15
  private marketplaceAppMapperPath;
26
16
  private reqConcurrency;
27
17
  private ignoredFilesInContentTypesFolder;
@@ -34,6 +24,7 @@ export default class ContentTypesImport extends BaseClass {
34
24
  taxonomies: Record<string, unknown>;
35
25
  private extPendingPath;
36
26
  private isExtensionsUpdate;
27
+ private pendingExts;
37
28
  constructor({ importConfig, stackAPIClient }: ModuleClassParams);
38
29
  start(): Promise<any>;
39
30
  seedCTs(): Promise<any>;
@@ -58,4 +49,10 @@ export default class ContentTypesImport extends BaseClass {
58
49
  */
59
50
  serializeUpdateGFs(apiOptions: ApiOptions): ApiOptions;
60
51
  updatePendingExtensions(): Promise<any>;
52
+ analyzeImportData(): Promise<void>;
53
+ initializeProgress(): import("@contentstack/cli-utilities").CLIProgressManager;
54
+ handlePendingGlobalFields(progress: any): Promise<void>;
55
+ handleContentTypesCreation(progress: any): Promise<void>;
56
+ handleContentTypesUpdate(progress: any): Promise<void>;
57
+ handlePendingExtensions(progress: any): Promise<void>;
61
58
  }