@contentstack/cli-variants 1.3.2 → 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 (55) hide show
  1. package/lib/export/attributes.d.ts +2 -2
  2. package/lib/export/attributes.js +51 -23
  3. package/lib/export/audiences.d.ts +2 -2
  4. package/lib/export/audiences.js +50 -24
  5. package/lib/export/events.d.ts +2 -2
  6. package/lib/export/events.js +52 -24
  7. package/lib/export/experiences.js +87 -54
  8. package/lib/export/projects.d.ts +3 -2
  9. package/lib/export/projects.js +55 -63
  10. package/lib/export/variant-entries.d.ts +19 -0
  11. package/lib/export/variant-entries.js +76 -1
  12. package/lib/import/attribute.d.ts +2 -0
  13. package/lib/import/attribute.js +83 -37
  14. package/lib/import/audiences.d.ts +2 -0
  15. package/lib/import/audiences.js +85 -41
  16. package/lib/import/events.d.ts +3 -1
  17. package/lib/import/events.js +86 -30
  18. package/lib/import/experiences.d.ts +2 -0
  19. package/lib/import/experiences.js +93 -39
  20. package/lib/import/project.d.ts +2 -0
  21. package/lib/import/project.js +81 -22
  22. package/lib/import/variant-entries.d.ts +10 -0
  23. package/lib/import/variant-entries.js +139 -53
  24. package/lib/index.d.ts +1 -0
  25. package/lib/index.js +1 -0
  26. package/lib/types/export-config.d.ts +0 -2
  27. package/lib/types/import-config.d.ts +0 -1
  28. package/lib/types/utils.d.ts +1 -1
  29. package/lib/utils/constants.d.ts +91 -0
  30. package/lib/utils/constants.js +93 -0
  31. package/lib/utils/personalization-api-adapter.d.ts +34 -1
  32. package/lib/utils/personalization-api-adapter.js +180 -53
  33. package/lib/utils/variant-api-adapter.d.ts +28 -1
  34. package/lib/utils/variant-api-adapter.js +89 -32
  35. package/package.json +2 -2
  36. package/src/export/attributes.ts +84 -34
  37. package/src/export/audiences.ts +87 -41
  38. package/src/export/events.ts +84 -41
  39. package/src/export/experiences.ts +155 -83
  40. package/src/export/projects.ts +71 -39
  41. package/src/export/variant-entries.ts +136 -12
  42. package/src/import/attribute.ts +105 -49
  43. package/src/import/audiences.ts +110 -54
  44. package/src/import/events.ts +104 -41
  45. package/src/import/experiences.ts +140 -62
  46. package/src/import/project.ts +108 -38
  47. package/src/import/variant-entries.ts +188 -75
  48. package/src/index.ts +2 -1
  49. package/src/types/export-config.ts +0 -2
  50. package/src/types/import-config.ts +0 -1
  51. package/src/types/utils.ts +1 -1
  52. package/src/utils/constants.ts +98 -0
  53. package/src/utils/personalization-api-adapter.ts +212 -76
  54. package/src/utils/variant-api-adapter.ts +137 -50
  55. package/tsconfig.json +1 -1
@@ -40,7 +40,7 @@ class VariantHttpClient extends adapter_helper_1.AdapterHelper {
40
40
  }
41
41
  init() {
42
42
  return __awaiter(this, void 0, void 0, function* () {
43
- var _a, _b, _c, _d, _e;
43
+ var _a, _b, _c, _d;
44
44
  cli_utilities_1.log.debug('Initializing VariantHttpClient...', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
45
45
  yield cli_utilities_1.authenticationHandler.getAuthDetails();
46
46
  const token = cli_utilities_1.authenticationHandler.accessToken;
@@ -53,7 +53,6 @@ class VariantHttpClient extends adapter_helper_1.AdapterHelper {
53
53
  cli_utilities_1.log.debug('Setting authtoken header', (_d = this.exportConfig) === null || _d === void 0 ? void 0 : _d.context);
54
54
  this.apiClient.headers({ authtoken: token });
55
55
  }
56
- cli_utilities_1.log.debug('VariantHttpClient initialization completed', (_e = this.exportConfig) === null || _e === void 0 ? void 0 : _e.context);
57
56
  });
58
57
  }
59
58
  variantEntry(options) {
@@ -171,28 +170,24 @@ class VariantHttpClient extends adapter_helper_1.AdapterHelper {
171
170
  createVariantEntry(input, options, apiParams) {
172
171
  return __awaiter(this, void 0, void 0, function* () {
173
172
  var _a, _b;
174
- const { reject, resolve, variantUid, log } = apiParams;
173
+ const { reject, resolve, variantUid } = apiParams;
175
174
  const variantConfig = this.config.modules.variantEntry;
176
175
  const { locale = variantConfig.query.locale || 'en-us', variant_id, entry_uid, content_type_uid } = options;
177
- log.debug(`Creating variant entry for content type: ${content_type_uid}, entry: ${entry_uid}, variant: ${variant_id}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
176
+ cli_utilities_1.log.debug(`Creating variant entry for content type: ${content_type_uid}, entry: ${entry_uid}, variant: ${variant_id}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
178
177
  let endpoint = `content_types/${content_type_uid}/entries/${entry_uid}/variants/${variant_id}?locale=${locale}`;
179
178
  const query = this.constructQuery((0, omit_1.default)(variantConfig.query, ['locale']));
180
179
  if (query) {
181
180
  endpoint = endpoint.concat(query);
182
181
  }
183
- log.debug(`Making API call to: ${endpoint}`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
182
+ cli_utilities_1.log.debug(`Making API call to: ${endpoint}`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
184
183
  const onSuccess = (response) => {
185
- var _a;
186
- log.debug(`Variant entry created successfully: ${variantUid}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
187
- resolve({ response, apiData: { variantUid, entryUid: entry_uid }, log });
184
+ resolve({ response, apiData: { variantUid, entryUid: entry_uid }, log: cli_utilities_1.log });
188
185
  };
189
186
  const onReject = (error) => {
190
- var _a;
191
- log.debug(`Failed to create variant entry: ${variantUid}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
192
187
  reject({
193
188
  error,
194
189
  apiData: { variantUid, entryUid: entry_uid },
195
- log,
190
+ log: cli_utilities_1.log,
196
191
  });
197
192
  };
198
193
  try {
@@ -222,23 +217,23 @@ class VariantHttpClient extends adapter_helper_1.AdapterHelper {
222
217
  publishVariantEntry(input, options, apiParams) {
223
218
  return __awaiter(this, void 0, void 0, function* () {
224
219
  var _a, _b;
225
- const { reject, resolve, log, variantUid } = apiParams;
220
+ const { reject, resolve, variantUid } = apiParams;
226
221
  const { entry_uid, content_type_uid } = options;
227
- log.debug(`Publishing variant entry for content type: ${content_type_uid}, entry: ${entry_uid}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
222
+ cli_utilities_1.log.debug(`Publishing variant entry for content type: ${content_type_uid}, entry: ${entry_uid}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
228
223
  let endpoint = `content_types/${content_type_uid}/entries/${entry_uid}/publish`;
229
- log.debug(`Making API call to: ${endpoint}`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
224
+ cli_utilities_1.log.debug(`Making API call to: ${endpoint}`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
230
225
  const onSuccess = (response) => {
231
226
  var _a;
232
- log.debug(`Variant entry published successfully: ${entry_uid}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
233
- resolve({ response, apiData: { entryUid: entry_uid, variantUid, locales: input.entry.locales }, log });
227
+ cli_utilities_1.log.debug(`Variant entry published successfully: ${entry_uid}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
228
+ resolve({ response, apiData: { entryUid: entry_uid, variantUid, locales: input.entry.locales }, log: cli_utilities_1.log });
234
229
  };
235
230
  const onReject = (error) => {
236
231
  var _a;
237
- log.debug(`Failed to publish variant entry: ${entry_uid}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
232
+ cli_utilities_1.log.debug(`Failed to publish variant entry: ${entry_uid}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
238
233
  reject({
239
234
  error,
240
235
  apiData: { entryUid: entry_uid, variantUid, locales: input.entry.locales },
241
- log,
236
+ log: cli_utilities_1.log,
242
237
  });
243
238
  };
244
239
  try {
@@ -275,9 +270,7 @@ class VariantHttpClient extends adapter_helper_1.AdapterHelper {
275
270
  cli_utilities_1.log.debug(`API request failed with status: ${status}`, (_c = this.exportConfig) === null || _c === void 0 ? void 0 : _c.context);
276
271
  // Refresh the access token if the response status is 401
277
272
  yield cli_utilities_1.authenticationHandler.refreshAccessToken(res);
278
- const errorMsg = (data === null || data === void 0 ? void 0 : data.errors)
279
- ? (0, error_helper_1.formatErrors)(data.errors)
280
- : (data === null || data === void 0 ? void 0 : data.error_message) || (data === null || data === void 0 ? void 0 : data.message) || data;
273
+ const errorMsg = (data === null || data === void 0 ? void 0 : data.errors) ? (0, error_helper_1.formatErrors)(data.errors) : (data === null || data === void 0 ? void 0 : data.error_message) || (data === null || data === void 0 ? void 0 : data.message) || data;
281
274
  cli_utilities_1.log.debug(`API error: ${errorMsg}`, (_d = this.exportConfig) === null || _d === void 0 ? void 0 : _d.context);
282
275
  throw errorMsg;
283
276
  });
@@ -287,38 +280,27 @@ exports.VariantHttpClient = VariantHttpClient;
287
280
  class VariantManagementSDK extends adapter_helper_1.AdapterHelper {
288
281
  init() {
289
282
  return __awaiter(this, void 0, void 0, function* () {
290
- var _a, _b;
291
- cli_utilities_1.log.debug('Initializing VariantManagementSDK...', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
292
283
  this.apiClient = yield (0, cli_utilities_1.managementSDKClient)(this.config);
293
- cli_utilities_1.log.debug('VariantManagementSDK initialized successfully', (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
294
284
  });
295
285
  }
296
286
  variantEntry(options) {
297
287
  return __awaiter(this, void 0, void 0, function* () {
298
- var _a;
299
- cli_utilities_1.log.debug('VariantEntry method called (SDK placeholder implementation)', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
300
288
  // TODO SDK implementation
301
289
  return { entry: {} };
302
290
  });
303
291
  }
304
292
  variantEntries(options) {
305
293
  return __awaiter(this, void 0, void 0, function* () {
306
- var _a;
307
- cli_utilities_1.log.debug('VariantEntries method called (SDK placeholder implementation)', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
308
294
  // TODO SDK implementation
309
295
  return { entries: [{}] };
310
296
  });
311
297
  }
312
298
  createVariantEntry(input, options, apiParams) {
313
- var _a;
314
- cli_utilities_1.log.debug('CreateVariantEntry method called (SDK placeholder implementation)', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
315
299
  // FIXME placeholder
316
300
  return Promise.resolve({});
317
301
  }
318
302
  handleVariantAPIRes(res) {
319
303
  return __awaiter(this, void 0, void 0, function* () {
320
- var _a;
321
- cli_utilities_1.log.debug('HandleVariantAPIRes method called (SDK implementation)', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
322
304
  return res.data;
323
305
  });
324
306
  }
@@ -337,6 +319,9 @@ exports.VariantManagementSDK = VariantManagementSDK;
337
319
  class VariantAdapter {
338
320
  constructor(config, options) {
339
321
  var _a, _b, _c, _d;
322
+ this.progressManager = null;
323
+ this.parentProgressManager = null;
324
+ this.currentModuleName = '';
340
325
  cli_utilities_1.log.debug('Initializing VariantAdapter...', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
341
326
  if (config.httpClient) {
342
327
  cli_utilities_1.log.debug('Using HTTP client variant instance', (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
@@ -351,6 +336,78 @@ class VariantAdapter {
351
336
  this.messages = messages_1.default;
352
337
  cli_utilities_1.log.debug('VariantAdapter initialized successfully', (_d = this.exportConfig) === null || _d === void 0 ? void 0 : _d.context);
353
338
  }
339
+ /**
340
+ * Set parent progress manager for sub-module integration
341
+ */
342
+ setParentProgressManager(parentProgress) {
343
+ this.parentProgressManager = parentProgress;
344
+ this.progressManager = parentProgress;
345
+ }
346
+ /**
347
+ * Create simple progress manager for single process tracking
348
+ */
349
+ createSimpleProgress(moduleName, total) {
350
+ var _a;
351
+ this.currentModuleName = moduleName;
352
+ // If we have a parent progress manager, use it instead of creating a new one
353
+ if (this.parentProgressManager) {
354
+ this.progressManager = this.parentProgressManager;
355
+ return this.progressManager;
356
+ }
357
+ const logConfig = cli_utilities_1.configHandler.get('log') || {};
358
+ const showConsoleLogs = (_a = logConfig.showConsoleLogs) !== null && _a !== void 0 ? _a : false;
359
+ this.progressManager = cli_utilities_1.CLIProgressManager.createSimple(moduleName, total, showConsoleLogs);
360
+ return this.progressManager;
361
+ }
362
+ /**
363
+ * Create nested progress manager for multi-process tracking
364
+ */
365
+ createNestedProgress(moduleName) {
366
+ var _a;
367
+ this.currentModuleName = moduleName;
368
+ // If we have a parent progress manager, use it instead of creating a new one
369
+ if (this.parentProgressManager) {
370
+ this.progressManager = this.parentProgressManager;
371
+ return this.progressManager;
372
+ }
373
+ const logConfig = cli_utilities_1.configHandler.get('log') || {};
374
+ const showConsoleLogs = (_a = logConfig.showConsoleLogs) !== null && _a !== void 0 ? _a : false;
375
+ this.progressManager = cli_utilities_1.CLIProgressManager.createNested(moduleName, showConsoleLogs);
376
+ return this.progressManager;
377
+ }
378
+ /**
379
+ * Complete progress manager
380
+ */
381
+ completeProgress(success = true, error) {
382
+ var _a;
383
+ // Only complete progress if we own the progress manager (no parent)
384
+ if (!this.parentProgressManager) {
385
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.complete(success, error);
386
+ }
387
+ this.progressManager = null;
388
+ }
389
+ /**
390
+ * Execute action with loading spinner for initial setup tasks
391
+ */
392
+ withLoadingSpinner(message, action) {
393
+ return __awaiter(this, void 0, void 0, function* () {
394
+ var _a;
395
+ const logConfig = cli_utilities_1.configHandler.get('log') || {};
396
+ const showConsoleLogs = (_a = logConfig.showConsoleLogs) !== null && _a !== void 0 ? _a : false;
397
+ if (showConsoleLogs) {
398
+ // If console logs are enabled, don't show spinner, just execute the action
399
+ return yield action();
400
+ }
401
+ return yield cli_utilities_1.CLIProgressManager.withLoadingSpinner(message, action);
402
+ });
403
+ }
404
+ /**
405
+ * Update progress for a specific item
406
+ */
407
+ updateProgress(success, itemName, error, processName) {
408
+ var _a;
409
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(success, itemName, error, processName);
410
+ }
354
411
  }
355
412
  exports.VariantAdapter = VariantAdapter;
356
413
  exports.default = VariantAdapter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentstack/cli-variants",
3
- "version": "1.3.2",
3
+ "version": "2.0.0-beta",
4
4
  "description": "Variants plugin",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -27,7 +27,7 @@
27
27
  "typescript": "^5.8.3"
28
28
  },
29
29
  "dependencies": {
30
- "@contentstack/cli-utilities": "~1.14.0",
30
+ "@contentstack/cli-utilities": "~1.14.1",
31
31
  "@oclif/core": "^4.3.0",
32
32
  "@oclif/plugin-help": "^6.2.28",
33
33
  "lodash": "^4.17.21",
@@ -1,16 +1,18 @@
1
1
  import omit from 'lodash/omit';
2
2
  import { resolve as pResolve } from 'node:path';
3
3
  import { sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
4
- import {fsUtil, PersonalizationAdapter } from '../utils';
5
4
  import { PersonalizeConfig, ExportConfig, AttributesConfig, AttributeStruct } from '../types';
5
+ import { fsUtil, PersonalizationAdapter } from '../utils';
6
+ import { PROCESS_NAMES, MODULE_CONTEXTS, EXPORT_PROCESS_STATUS } from '../utils/constants';
6
7
 
7
8
  export default class ExportAttributes extends PersonalizationAdapter<ExportConfig> {
8
9
  private attributesConfig: AttributesConfig;
9
10
  private attributesFolderPath: string;
10
11
  private attributes: Record<string, unknown>[];
12
+ public exportConfig: ExportConfig;
11
13
  public personalizeConfig: PersonalizeConfig;
12
14
 
13
- constructor(readonly exportConfig: ExportConfig) {
15
+ constructor(exportConfig: ExportConfig) {
14
16
  super({
15
17
  config: exportConfig,
16
18
  baseURL: exportConfig.modules.personalize.baseURL[exportConfig.region.name],
@@ -26,45 +28,73 @@ export default class ExportAttributes extends PersonalizationAdapter<ExportConfi
26
28
  sanitizePath(this.attributesConfig.dirName),
27
29
  );
28
30
  this.attributes = [];
29
- this.exportConfig.context.module = 'attributes';
31
+ this.exportConfig.context.module = MODULE_CONTEXTS.ATTRIBUTES;
30
32
  }
31
33
 
32
34
  async start() {
33
35
  try {
34
36
  log.info('Starting attributes export', this.exportConfig.context);
35
-
36
- log.debug('Initializing personalization adapter...', this.exportConfig.context);
37
- await this.init();
38
- log.debug('Personalization adapter initialized successfully', this.exportConfig.context);
39
-
40
- log.debug(`Creating attributes directory at: ${this.attributesFolderPath}`, this.exportConfig.context);
41
- await fsUtil.makeDirectory(this.attributesFolderPath);
42
- log.debug('Attributes directory created successfully', this.exportConfig.context);
43
-
44
- log.debug('Fetching attributes from personalization API...', this.exportConfig.context);
45
- this.attributes = (await this.getAttributes()) as AttributeStruct[];
46
- log.debug(`Fetched ${this.attributes?.length || 0} attributes`, this.exportConfig.context);
37
+
38
+ // Initial setup with loading spinner
39
+ await this.withLoadingSpinner('ATTRIBUTES: Initializing export and fetching data...', async () => {
40
+ log.debug('Initializing personalization adapter...', this.exportConfig.context);
41
+ await this.init();
42
+ log.debug('Personalization adapter initialized successfully', this.exportConfig.context);
43
+
44
+ log.debug(`Creating attributes directory at: ${this.attributesFolderPath}`, this.exportConfig.context);
45
+ await fsUtil.makeDirectory(this.attributesFolderPath);
46
+ log.debug('Attributes directory created successfully', this.exportConfig.context);
47
+ log.debug('Fetching attributes from personalization API...', this.exportConfig.context);
48
+ this.attributes = (await this.getAttributes()) as AttributeStruct[];
49
+ log.debug(`Fetched ${this.attributes?.length || 0} attributes`, this.exportConfig.context);
50
+ });
47
51
 
48
52
  if (!this.attributes?.length) {
49
53
  log.debug('No attributes found, completing export', this.exportConfig.context);
50
54
  log.info('No Attributes found with the given project!', this.exportConfig.context);
55
+ return;
56
+ }
57
+
58
+ let progress: any;
59
+
60
+ if (this.parentProgressManager) {
61
+ // Use parent progress manager - we're part of the personalize modules process
62
+ progress = this.parentProgressManager;
63
+ this.progressManager = this.parentProgressManager;
64
+
65
+ progress.updateProcessTotal(PROCESS_NAMES.ATTRIBUTES, this.attributes.length);
51
66
  } else {
52
- log.debug(`Processing ${this.attributes.length} attributes`, this.exportConfig.context);
53
- this.sanitizeAttribs();
54
- log.debug('Attributes sanitization completed', this.exportConfig.context);
55
-
56
- const attributesFilePath = pResolve(sanitizePath(this.attributesFolderPath), sanitizePath(this.attributesConfig.fileName));
57
- log.debug(`Writing attributes to: ${attributesFilePath}`, this.exportConfig.context);
58
- fsUtil.writeFile(attributesFilePath, this.attributes);
59
-
60
- log.debug('Attributes export completed successfully', this.exportConfig.context);
61
- log.success(
62
- `Attributes exported successfully! Total attributes: ${this.attributes.length}`,
63
- this.exportConfig.context,
64
- );
67
+ progress = this.createSimpleProgress(PROCESS_NAMES.ATTRIBUTES, this.attributes.length);
68
+ }
69
+
70
+ log.debug(`Processing ${this.attributes.length} attributes`, this.exportConfig.context);
71
+
72
+ progress.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.ATTRIBUTES].EXPORTING, PROCESS_NAMES.ATTRIBUTES);
73
+
74
+ this.sanitizeAttribs();
75
+ log.debug('Attributes sanitization completed', this.exportConfig.context);
76
+
77
+ progress.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.ATTRIBUTES].EXPORTING, PROCESS_NAMES.ATTRIBUTES);
78
+ const attributesFilePath = pResolve(
79
+ sanitizePath(this.attributesFolderPath),
80
+ sanitizePath(this.attributesConfig.fileName),
81
+ );
82
+ log.debug(`Writing attributes to: ${attributesFilePath}`, this.exportConfig.context);
83
+ fsUtil.writeFile(attributesFilePath, this.attributes);
84
+
85
+ // Complete progress only if we're managing our own progress
86
+ if (!this.parentProgressManager) {
87
+ this.completeProgress(true);
65
88
  }
66
- } catch (error) {
89
+
90
+ log.debug('Attributes export completed successfully', this.exportConfig.context);
91
+ log.success(
92
+ `Attributes exported successfully! Total attributes: ${this.attributes.length}`,
93
+ this.exportConfig.context,
94
+ );
95
+ } catch (error: any) {
67
96
  log.debug(`Error occurred during attributes export: ${error}`, this.exportConfig.context);
97
+ this.completeProgress(false, error?.message || 'Attributes export failed');
68
98
  handleAndLogError(error, { ...this.exportConfig.context });
69
99
  }
70
100
  }
@@ -74,10 +104,30 @@ export default class ExportAttributes extends PersonalizationAdapter<ExportConfi
74
104
  */
75
105
  sanitizeAttribs() {
76
106
  log.debug(`Sanitizing ${this.attributes?.length || 0} attributes`, this.exportConfig.context);
77
- log.debug(`Invalid keys to remove: ${JSON.stringify(this.attributesConfig.invalidKeys)}`, this.exportConfig.context);
78
-
79
- this.attributes = this.attributes?.map((audience) => omit(audience, this.attributesConfig.invalidKeys)) || [];
80
-
81
- log.debug(`Sanitization complete. Total attributes after sanitization: ${this.attributes.length}`, this.exportConfig.context);
107
+
108
+ this.attributes =
109
+ this.attributes?.map((attribute, index) => {
110
+ const sanitizedAttribute = omit(attribute, this.attributesConfig.invalidKeys);
111
+
112
+ // Update progress for each processed attribute
113
+ if (this.progressManager) {
114
+ const processName = this.parentProgressManager ? PROCESS_NAMES.ATTRIBUTES : undefined;
115
+ this.updateProgress(
116
+ true,
117
+ `attribute ${index + 1}/${this.attributes.length}: ${
118
+ (attribute as any)?.name || (attribute as any)?.uid || 'unknown'
119
+ }`,
120
+ undefined,
121
+ processName,
122
+ );
123
+ }
124
+
125
+ return sanitizedAttribute;
126
+ }) || [];
127
+
128
+ log.debug(
129
+ `Sanitization complete. Total attributes after sanitization: ${this.attributes.length}`,
130
+ this.exportConfig.context,
131
+ );
82
132
  }
83
133
  }
@@ -1,73 +1,99 @@
1
1
  import omit from 'lodash/omit';
2
2
  import { resolve as pResolve } from 'node:path';
3
- import { log, handleAndLogError } from '@contentstack/cli-utilities';
4
-
3
+ import { sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
4
+ import { PersonalizeConfig, ExportConfig, AudiencesConfig, AudienceStruct } from '../types';
5
5
  import { fsUtil, PersonalizationAdapter } from '../utils';
6
- import { PersonalizeConfig, ExportConfig, AudienceStruct, AudiencesConfig } from '../types';
6
+ import { PROCESS_NAMES, MODULE_CONTEXTS, EXPORT_PROCESS_STATUS } from '../utils/constants';
7
7
 
8
8
  export default class ExportAudiences extends PersonalizationAdapter<ExportConfig> {
9
9
  private audiencesConfig: AudiencesConfig;
10
10
  private audiencesFolderPath: string;
11
11
  private audiences: Record<string, unknown>[];
12
+ public exportConfig: ExportConfig;
12
13
  public personalizeConfig: PersonalizeConfig;
13
14
 
14
- constructor(readonly exportConfig: ExportConfig) {
15
+ constructor(exportConfig: ExportConfig) {
15
16
  super({
16
17
  config: exportConfig,
17
18
  baseURL: exportConfig.modules.personalize.baseURL[exportConfig.region.name],
18
19
  headers: { 'X-Project-Uid': exportConfig.project_id },
19
20
  });
21
+ this.exportConfig = exportConfig;
20
22
  this.personalizeConfig = exportConfig.modules.personalize;
21
23
  this.audiencesConfig = exportConfig.modules.audiences;
22
24
  this.audiencesFolderPath = pResolve(
23
- exportConfig.data,
24
- exportConfig.branchName || '',
25
- this.personalizeConfig.dirName,
26
- this.audiencesConfig.dirName,
25
+ sanitizePath(exportConfig.data),
26
+ sanitizePath(exportConfig.branchName || ''),
27
+ sanitizePath(this.personalizeConfig.dirName),
28
+ sanitizePath(this.audiencesConfig.dirName),
27
29
  );
28
30
  this.audiences = [];
29
- this.exportConfig.context.module = 'audiences';
31
+ this.exportConfig.context.module = MODULE_CONTEXTS.AUDIENCES;
30
32
  }
31
33
 
32
34
  async start() {
33
35
  try {
34
36
  log.debug('Starting audiences export process...', this.exportConfig.context);
35
37
  log.info('Starting audiences export', this.exportConfig.context);
36
-
37
- log.debug('Initializing personalization adapter...', this.exportConfig.context);
38
- await this.init();
39
- log.debug('Personalization adapter initialized successfully', this.exportConfig.context);
40
-
41
- log.debug(`Creating audiences directory at: ${this.audiencesFolderPath}`, this.exportConfig.context);
42
- await fsUtil.makeDirectory(this.audiencesFolderPath);
43
- log.debug('Audiences directory created successfully', this.exportConfig.context);
44
-
45
- log.debug('Fetching audiences from personalization API...', this.exportConfig.context);
46
- this.audiences = (await this.getAudiences()) as AudienceStruct[];
47
- log.debug(`Fetched ${this.audiences?.length || 0} audiences`, this.exportConfig.context);
38
+
39
+ await this.withLoadingSpinner('AUDIENCES: Initializing export and fetching data...', async () => {
40
+ log.debug('Initializing personalization adapter...', this.exportConfig.context);
41
+ await this.init();
42
+ log.debug('Personalization adapter initialized successfully', this.exportConfig.context);
43
+
44
+ log.debug(`Creating audiences directory at: ${this.audiencesFolderPath}`, this.exportConfig.context);
45
+ await fsUtil.makeDirectory(this.audiencesFolderPath);
46
+ log.debug('Audiences directory created successfully', this.exportConfig.context);
47
+
48
+ log.debug('Fetching audiences from personalization API...', this.exportConfig.context);
49
+ this.audiences = (await this.getAudiences()) as AudienceStruct[];
50
+ log.debug(`Fetched ${this.audiences?.length || 0} audiences`, this.exportConfig.context);
51
+ });
48
52
 
49
53
  if (!this.audiences?.length) {
50
54
  log.debug('No audiences found, completing export', this.exportConfig.context);
51
55
  log.info('No Audiences found with the given project!', this.exportConfig.context);
52
56
  return;
57
+ }
58
+
59
+ let progress: any;
60
+
61
+ if (this.parentProgressManager) {
62
+ progress = this.parentProgressManager;
63
+ this.progressManager = this.parentProgressManager;
64
+ progress.updateProcessTotal(PROCESS_NAMES.AUDIENCES, this.audiences.length);
53
65
  } else {
54
- log.debug(`Processing ${this.audiences.length} audiences`, this.exportConfig.context);
55
- this.sanitizeAttribs();
56
- log.debug('Audiences sanitization completed', this.exportConfig.context);
57
-
58
- const audiencesFilePath = pResolve(this.audiencesFolderPath, this.audiencesConfig.fileName);
59
- log.debug(`Writing audiences to: ${audiencesFilePath}`, this.exportConfig.context);
60
- fsUtil.writeFile(audiencesFilePath, this.audiences);
61
-
62
- log.debug('Audiences export completed successfully', this.exportConfig.context);
63
- log.success(
64
- `Audiences exported successfully! Total audiences: ${this.audiences.length}`,
65
- this.exportConfig.context,
66
- );
67
- return;
66
+ progress = this.createSimpleProgress(PROCESS_NAMES.AUDIENCES, this.audiences.length);
68
67
  }
69
- } catch (error) {
68
+
69
+ log.debug(`Processing ${this.audiences.length} audiences`, this.exportConfig.context);
70
+ progress.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.AUDIENCES].EXPORTING, PROCESS_NAMES.AUDIENCES);
71
+
72
+ this.sanitizeAttribs();
73
+ log.debug('Audiences sanitization completed', this.exportConfig.context);
74
+
75
+ // Write audiences to file
76
+ progress.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.AUDIENCES].EXPORTING, PROCESS_NAMES.AUDIENCES);
77
+ const audiencesFilePath = pResolve(
78
+ sanitizePath(this.audiencesFolderPath),
79
+ sanitizePath(this.audiencesConfig.fileName),
80
+ );
81
+ log.debug(`Writing audiences to: ${audiencesFilePath}`, this.exportConfig.context);
82
+ fsUtil.writeFile(audiencesFilePath, this.audiences);
83
+
84
+ // Complete progress only if we're managing our own progress
85
+ if (!this.parentProgressManager) {
86
+ this.completeProgress(true);
87
+ }
88
+
89
+ log.debug('Audiences export completed successfully', this.exportConfig.context);
90
+ log.success(
91
+ `Audiences exported successfully! Total audiences: ${this.audiences.length}`,
92
+ this.exportConfig.context,
93
+ );
94
+ } catch (error: any) {
70
95
  log.debug(`Error occurred during audiences export: ${error}`, this.exportConfig.context);
96
+ this.completeProgress(false, error?.message || 'Audiences export failed');
71
97
  handleAndLogError(error, { ...this.exportConfig.context });
72
98
  }
73
99
  }
@@ -77,10 +103,30 @@ export default class ExportAudiences extends PersonalizationAdapter<ExportConfig
77
103
  */
78
104
  sanitizeAttribs() {
79
105
  log.debug(`Sanitizing ${this.audiences?.length || 0} audiences`, this.exportConfig.context);
80
- log.debug(`Invalid keys to remove: ${JSON.stringify(this.audiencesConfig.invalidKeys)}`, this.exportConfig.context);
81
-
82
- this.audiences = this.audiences?.map((audience) => omit(audience, this.audiencesConfig.invalidKeys)) || [];
83
-
84
- log.debug(`Sanitization complete. Total audiences after sanitization: ${this.audiences.length}`, this.exportConfig.context);
106
+
107
+ this.audiences =
108
+ this.audiences?.map((audience, index) => {
109
+ const sanitizedAudience = omit(audience, this.audiencesConfig.invalidKeys);
110
+
111
+ // Update progress for each processed audience
112
+ if (this.progressManager) {
113
+ const processName = this.parentProgressManager ? PROCESS_NAMES.AUDIENCES : undefined;
114
+ this.updateProgress(
115
+ true,
116
+ `audience ${index + 1}/${this.audiences.length}: ${
117
+ (audience as any).name || (audience as any).uid || 'unknown'
118
+ }`,
119
+ undefined,
120
+ processName,
121
+ );
122
+ }
123
+
124
+ return sanitizedAudience;
125
+ }) || [];
126
+
127
+ log.debug(
128
+ `Sanitization complete. Total audiences after sanitization: ${this.audiences.length}`,
129
+ this.exportConfig.context,
130
+ );
85
131
  }
86
132
  }