@contentstack/cli-variants 1.3.3 → 2.0.0-beta.1

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 +132 -47
  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 +171 -44
  33. package/lib/utils/variant-api-adapter.d.ts +28 -1
  34. package/lib/utils/variant-api-adapter.js +75 -0
  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 +179 -65
  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 +202 -66
  54. package/src/utils/variant-api-adapter.ts +82 -1
  55. package/tsconfig.json +1 -1
@@ -1,9 +1,42 @@
1
1
  import { AdapterHelper } from './adapter-helper';
2
- import { HttpClient } from '@contentstack/cli-utilities';
2
+ import { HttpClient, CLIProgressManager } from '@contentstack/cli-utilities';
3
3
  import { ProjectStruct, Personalization, GetProjectsParams, CreateProjectInput, CreateAttributeInput, APIConfig, GetVariantGroupInput, EventStruct, AudienceStruct, AttributeStruct, CreateAudienceInput, CreateEventInput, CreateExperienceInput, ExperienceStruct, UpdateExperienceInput, CMSExperienceStruct, VariantAPIRes, APIResponse, VariantGroupStruct, VariantGroup, CreateExperienceVersionInput, ExportConfig } from '../types';
4
4
  export declare class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> implements Personalization<T> {
5
5
  exportConfig?: ExportConfig;
6
+ protected progressManager: CLIProgressManager | null;
7
+ protected parentProgressManager: CLIProgressManager | null;
8
+ protected currentModuleName: string;
9
+ protected cachedData: any[] | null;
6
10
  constructor(options: APIConfig);
11
+ /**
12
+ * Set parent progress manager for sub-module integration
13
+ */
14
+ setParentProgressManager(parentProgress: CLIProgressManager): void;
15
+ /**
16
+ * Set cached data to avoid redundant API calls
17
+ */
18
+ setCachedData(data: any[]): void;
19
+ /**
20
+ * Create simple progress manager for single process tracking
21
+ */
22
+ protected createSimpleProgress(moduleName: string, total?: number): CLIProgressManager;
23
+ /**
24
+ * Create nested progress manager for multi-process tracking
25
+ */
26
+ protected createNestedProgress(moduleName: string): CLIProgressManager;
27
+ /**
28
+ * Complete progress manager
29
+ */
30
+ protected completeProgress(success?: boolean, error?: string): void;
31
+ /**
32
+ * Execute action with loading spinner for initial setup tasks
33
+ */
34
+ protected withLoadingSpinner<T>(message: string, action: () => Promise<T>): Promise<T>;
35
+ /**
36
+ * Update progress for a specific item
37
+ */
38
+ protected updateProgress(success: boolean, itemName: string, error?: string, processName?: string): void;
39
+ static printFinalSummary(): void;
7
40
  init(): Promise<void>;
8
41
  projects(options: GetProjectsParams): Promise<ProjectStruct[]>;
9
42
  /**
@@ -28,45 +28,149 @@ class PersonalizationAdapter extends adapter_helper_1.AdapterHelper {
28
28
  constructor(options) {
29
29
  var _a;
30
30
  super(options);
31
+ this.progressManager = null;
32
+ this.parentProgressManager = null; // Add parent progress manager
33
+ this.currentModuleName = '';
34
+ this.cachedData = null; // Add cached data property
31
35
  cli_utilities_1.log.debug('PersonalizationAdapter initialized', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
32
36
  }
37
+ /**
38
+ * Set parent progress manager for sub-module integration
39
+ */
40
+ setParentProgressManager(parentProgress) {
41
+ this.parentProgressManager = parentProgress;
42
+ }
43
+ /**
44
+ * Set cached data to avoid redundant API calls
45
+ */
46
+ setCachedData(data) {
47
+ var _a;
48
+ this.cachedData = data;
49
+ cli_utilities_1.log.debug(`Cached data set with ${(data === null || data === void 0 ? void 0 : data.length) || 0} items`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
50
+ }
51
+ /**
52
+ * Create simple progress manager for single process tracking
53
+ */
54
+ createSimpleProgress(moduleName, total) {
55
+ var _a;
56
+ this.currentModuleName = moduleName;
57
+ // If we have a parent progress manager, use it instead of creating a new one
58
+ if (this.parentProgressManager) {
59
+ this.progressManager = this.parentProgressManager;
60
+ return this.progressManager;
61
+ }
62
+ const logConfig = cli_utilities_1.configHandler.get('log') || {};
63
+ const showConsoleLogs = (_a = logConfig.showConsoleLogs) !== null && _a !== void 0 ? _a : false;
64
+ this.progressManager = cli_utilities_1.CLIProgressManager.createSimple(moduleName, total, showConsoleLogs);
65
+ return this.progressManager;
66
+ }
67
+ /**
68
+ * Create nested progress manager for multi-process tracking
69
+ */
70
+ createNestedProgress(moduleName) {
71
+ var _a;
72
+ this.currentModuleName = moduleName;
73
+ // If we have a parent progress manager, use it instead of creating a new one
74
+ if (this.parentProgressManager) {
75
+ this.progressManager = this.parentProgressManager;
76
+ return this.progressManager;
77
+ }
78
+ const logConfig = cli_utilities_1.configHandler.get('log') || {};
79
+ const showConsoleLogs = (_a = logConfig.showConsoleLogs) !== null && _a !== void 0 ? _a : false;
80
+ this.progressManager = cli_utilities_1.CLIProgressManager.createNested(moduleName, showConsoleLogs);
81
+ return this.progressManager;
82
+ }
83
+ /**
84
+ * Complete progress manager
85
+ */
86
+ completeProgress(success = true, error) {
87
+ var _a;
88
+ // Only complete progress if we own the progress manager (no parent)
89
+ if (!this.parentProgressManager) {
90
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.complete(success, error);
91
+ }
92
+ this.progressManager = null;
93
+ }
94
+ /**
95
+ * Execute action with loading spinner for initial setup tasks
96
+ */
97
+ withLoadingSpinner(message, action) {
98
+ return __awaiter(this, void 0, void 0, function* () {
99
+ var _a;
100
+ const logConfig = cli_utilities_1.configHandler.get('log') || {};
101
+ const showConsoleLogs = (_a = logConfig.showConsoleLogs) !== null && _a !== void 0 ? _a : false;
102
+ if (showConsoleLogs) {
103
+ // If console logs are enabled, don't show spinner, just execute the action
104
+ return yield action();
105
+ }
106
+ return yield cli_utilities_1.CLIProgressManager.withLoadingSpinner(message, action);
107
+ });
108
+ }
109
+ /**
110
+ * Update progress for a specific item
111
+ */
112
+ updateProgress(success, itemName, error, processName) {
113
+ if (this.parentProgressManager) {
114
+ this.parentProgressManager.tick(success, itemName, error, processName);
115
+ }
116
+ else if (this.progressManager) {
117
+ this.progressManager.tick(success, itemName, error, processName);
118
+ }
119
+ }
120
+ static printFinalSummary() {
121
+ cli_utilities_1.CLIProgressManager.printGlobalSummary();
122
+ }
33
123
  init() {
34
124
  return __awaiter(this, void 0, void 0, function* () {
35
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
36
- cli_utilities_1.log.debug('Initializing personalization adapter...', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
37
- yield cli_utilities_1.authenticationHandler.getAuthDetails();
38
- const token = cli_utilities_1.authenticationHandler.accessToken;
39
- cli_utilities_1.log.debug(`Authentication type: ${cli_utilities_1.authenticationHandler.isOauthEnabled ? 'OAuth' : 'Token'}`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
40
- if (cli_utilities_1.authenticationHandler.isOauthEnabled) {
41
- cli_utilities_1.log.debug('Setting OAuth authorization header', (_c = this.exportConfig) === null || _c === void 0 ? void 0 : _c.context);
42
- this.apiClient.headers({ authorization: token });
43
- if (this.adapterConfig.cmaConfig) {
44
- cli_utilities_1.log.debug('Setting OAuth authorization header for CMA client', (_d = this.exportConfig) === null || _d === void 0 ? void 0 : _d.context);
45
- (_e = this.cmaAPIClient) === null || _e === void 0 ? void 0 : _e.headers({ authorization: token });
125
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
126
+ try {
127
+ cli_utilities_1.log.debug('Initializing personalization adapter...', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
128
+ yield cli_utilities_1.authenticationHandler.getAuthDetails();
129
+ const token = cli_utilities_1.authenticationHandler.accessToken;
130
+ cli_utilities_1.log.debug(`Authentication type: ${cli_utilities_1.authenticationHandler.isOauthEnabled ? 'OAuth' : 'Token'}`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
131
+ if (cli_utilities_1.authenticationHandler.isOauthEnabled) {
132
+ cli_utilities_1.log.debug('Setting OAuth authorization header', (_c = this.exportConfig) === null || _c === void 0 ? void 0 : _c.context);
133
+ this.apiClient.headers({ authorization: token });
134
+ if (this.adapterConfig.cmaConfig) {
135
+ cli_utilities_1.log.debug('Setting OAuth authorization header for CMA client', (_d = this.exportConfig) === null || _d === void 0 ? void 0 : _d.context);
136
+ (_e = this.cmaAPIClient) === null || _e === void 0 ? void 0 : _e.headers({ authorization: token });
137
+ }
46
138
  }
47
- }
48
- else {
49
- cli_utilities_1.log.debug('Setting authtoken header', (_f = this.exportConfig) === null || _f === void 0 ? void 0 : _f.context);
50
- this.apiClient.headers({ authtoken: token });
51
- if (this.adapterConfig.cmaConfig) {
52
- cli_utilities_1.log.debug('Setting authtoken header for CMA client', (_g = this.exportConfig) === null || _g === void 0 ? void 0 : _g.context);
53
- (_h = this.cmaAPIClient) === null || _h === void 0 ? void 0 : _h.headers({ authtoken: token });
139
+ else {
140
+ cli_utilities_1.log.debug('Setting authtoken header', (_f = this.exportConfig) === null || _f === void 0 ? void 0 : _f.context);
141
+ this.apiClient.headers({ authtoken: token });
142
+ if (this.adapterConfig.cmaConfig) {
143
+ cli_utilities_1.log.debug('Setting authtoken header for CMA client', (_g = this.exportConfig) === null || _g === void 0 ? void 0 : _g.context);
144
+ (_h = this.cmaAPIClient) === null || _h === void 0 ? void 0 : _h.headers({ authtoken: token });
145
+ }
54
146
  }
147
+ cli_utilities_1.log.debug('Personalization adapter initialization completed', (_j = this.exportConfig) === null || _j === void 0 ? void 0 : _j.context);
148
+ }
149
+ catch (error) {
150
+ cli_utilities_1.log.debug(`Personalization adapter initialization failed: ${error}`, (_k = this.exportConfig) === null || _k === void 0 ? void 0 : _k.context);
55
151
  }
56
- cli_utilities_1.log.debug('Personalization adapter initialization completed', (_j = this.exportConfig) === null || _j === void 0 ? void 0 : _j.context);
57
152
  });
58
153
  }
59
154
  projects(options) {
60
155
  return __awaiter(this, void 0, void 0, function* () {
61
156
  var _a, _b, _c;
62
- cli_utilities_1.log.debug(`Fetching projects for stack API key: ${options.connectedStackApiKey}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
63
157
  yield this.init();
64
158
  const getProjectEndPoint = `/projects?connectedStackApiKey=${options.connectedStackApiKey}`;
65
- cli_utilities_1.log.debug(`Making API call to: ${getProjectEndPoint}`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
66
- const data = yield this.apiClient.get(getProjectEndPoint);
67
- const result = (yield this.handleVariantAPIRes(data));
68
- cli_utilities_1.log.debug(`Fetched ${(result === null || result === void 0 ? void 0 : result.length) || 0} projects`, (_c = this.exportConfig) === null || _c === void 0 ? void 0 : _c.context);
69
- return result;
159
+ cli_utilities_1.log.debug(`Making API call to: ${getProjectEndPoint}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
160
+ try {
161
+ const data = yield this.apiClient.get(getProjectEndPoint);
162
+ const result = (yield this.handleVariantAPIRes(data));
163
+ cli_utilities_1.log.debug(`Fetched ${(result === null || result === void 0 ? void 0 : result.length) || 0} projects`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
164
+ // Update progress for each project fetched
165
+ result === null || result === void 0 ? void 0 : result.forEach((project) => {
166
+ this.updateProgress(true, `project: ${project.name || project.uid}`, undefined, 'Projects');
167
+ });
168
+ return result;
169
+ }
170
+ catch (error) {
171
+ cli_utilities_1.log.debug(`Failed to fetch projects: ${error}`, (_c = this.exportConfig) === null || _c === void 0 ? void 0 : _c.context);
172
+ throw error;
173
+ }
70
174
  });
71
175
  }
72
176
  /**
@@ -110,13 +214,23 @@ class PersonalizationAdapter extends adapter_helper_1.AdapterHelper {
110
214
  }
111
215
  getExperiences() {
112
216
  return __awaiter(this, void 0, void 0, function* () {
113
- var _a, _b;
217
+ var _a, _b, _c;
114
218
  cli_utilities_1.log.debug('Fetching experiences from personalization API', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
115
219
  const getExperiencesEndPoint = `/experiences`;
116
- const data = yield this.apiClient.get(getExperiencesEndPoint);
117
- const result = (yield this.handleVariantAPIRes(data));
118
- cli_utilities_1.log.debug(`Fetched ${(result === null || result === void 0 ? void 0 : result.length) || 0} experiences`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
119
- return result;
220
+ try {
221
+ const data = yield this.apiClient.get(getExperiencesEndPoint);
222
+ const result = (yield this.handleVariantAPIRes(data));
223
+ cli_utilities_1.log.debug(`Fetched ${(result === null || result === void 0 ? void 0 : result.length) || 0} experiences`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
224
+ // Update progress for each experience fetched
225
+ result === null || result === void 0 ? void 0 : result.forEach((experience) => {
226
+ this.updateProgress(true, `experience: ${experience.name || experience.uid}`, undefined, 'Experiences');
227
+ });
228
+ return result;
229
+ }
230
+ catch (error) {
231
+ cli_utilities_1.log.debug(`Failed to fetch experiences: ${error}`, (_c = this.exportConfig) === null || _c === void 0 ? void 0 : _c.context);
232
+ throw error;
233
+ }
120
234
  });
121
235
  }
122
236
  getExperience(experienceUid) {
@@ -154,7 +268,7 @@ class PersonalizationAdapter extends adapter_helper_1.AdapterHelper {
154
268
  const createExperiencesVersionsEndPoint = `/experiences/${experienceUid}/versions`;
155
269
  const data = yield this.apiClient.post(createExperiencesVersionsEndPoint, input);
156
270
  const result = (yield this.handleVariantAPIRes(data));
157
- cli_utilities_1.log.info(`Experience version created successfully for: ${experienceUid}`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
271
+ cli_utilities_1.log.debug(`Experience version created successfully for: ${experienceUid}`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
158
272
  return result;
159
273
  });
160
274
  }
@@ -213,32 +327,45 @@ class PersonalizationAdapter extends adapter_helper_1.AdapterHelper {
213
327
  }
214
328
  getEvents() {
215
329
  return __awaiter(this, void 0, void 0, function* () {
216
- var _a, _b;
330
+ var _a, _b, _c;
217
331
  cli_utilities_1.log.debug('Fetching events from personalization API', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
218
- const data = yield this.apiClient.get('/events');
219
- const result = (yield this.handleVariantAPIRes(data));
220
- cli_utilities_1.log.debug(`Fetched ${(result === null || result === void 0 ? void 0 : result.length) || 0} events`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
221
- return result;
332
+ try {
333
+ const data = yield this.apiClient.get('/events');
334
+ const result = (yield this.handleVariantAPIRes(data));
335
+ cli_utilities_1.log.debug(`Fetched ${(result === null || result === void 0 ? void 0 : result.length) || 0} events`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
336
+ return result;
337
+ }
338
+ catch (error) {
339
+ cli_utilities_1.log.debug(`Failed to fetch events: ${error}`, (_c = this.exportConfig) === null || _c === void 0 ? void 0 : _c.context);
340
+ // Return empty array instead of throwing to prevent spinner from hanging
341
+ throw error;
342
+ }
222
343
  });
223
344
  }
224
345
  createEvents(event) {
225
346
  return __awaiter(this, void 0, void 0, function* () {
226
- var _a, _b;
227
- cli_utilities_1.log.debug(`Creating event: ${event.key}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
347
+ var _a;
228
348
  const data = yield this.apiClient.post('/events', event);
229
349
  const result = (yield this.handleVariantAPIRes(data));
230
- cli_utilities_1.log.info(`Event created successfully: ${result === null || result === void 0 ? void 0 : result.uid}`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
350
+ cli_utilities_1.log.info(`Event created successfully: ${result === null || result === void 0 ? void 0 : result.uid}`, (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
231
351
  return result;
232
352
  });
233
353
  }
234
354
  getAudiences() {
235
355
  return __awaiter(this, void 0, void 0, function* () {
236
- var _a, _b;
356
+ var _a, _b, _c;
237
357
  cli_utilities_1.log.debug('Fetching audiences from personalization API', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
238
- const data = yield this.apiClient.get('/audiences');
239
- const result = (yield this.handleVariantAPIRes(data));
240
- cli_utilities_1.log.debug(`Fetched ${(result === null || result === void 0 ? void 0 : result.length) || 0} audiences`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
241
- return result;
358
+ try {
359
+ const data = yield this.apiClient.get('/audiences');
360
+ const result = (yield this.handleVariantAPIRes(data));
361
+ cli_utilities_1.log.debug(`Fetched ${(result === null || result === void 0 ? void 0 : result.length) || 0} audiences`, (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
362
+ return result;
363
+ }
364
+ catch (error) {
365
+ cli_utilities_1.log.debug(`Failed to fetch audiences: ${error}`, (_c = this.exportConfig) === null || _c === void 0 ? void 0 : _c.context);
366
+ // Return empty array instead of throwing to prevent spinner from hanging
367
+ throw error;
368
+ }
242
369
  });
243
370
  }
244
371
  getAttributes() {
@@ -1,4 +1,4 @@
1
- import { HttpClient, HttpClientOptions, ContentstackClient, ContentstackConfig } from '@contentstack/cli-utilities';
1
+ import { HttpClient, HttpClientOptions, ContentstackClient, ContentstackConfig, CLIProgressManager } from '@contentstack/cli-utilities';
2
2
  import { APIConfig, AdapterType, AnyProperty, ExportConfig, VariantOptions, VariantsOption, VariantInterface, VariantEntryStruct, CreateVariantEntryDto, CreateVariantEntryOptions, APIResponse, PublishVariantEntryDto, PublishVariantEntryOptions } from '../types';
3
3
  import messages from '../messages';
4
4
  import { AdapterHelper } from './adapter-helper';
@@ -77,7 +77,34 @@ export declare class VariantAdapter<T> {
77
77
  protected variantInstance: T;
78
78
  readonly messages: typeof messages;
79
79
  exportConfig?: any;
80
+ protected progressManager: CLIProgressManager | null;
81
+ protected parentProgressManager: CLIProgressManager | null;
82
+ protected currentModuleName: string;
80
83
  constructor(config: ContentstackConfig & AnyProperty & AdapterType<T, ContentstackConfig>);
81
84
  constructor(config: APIConfig & AdapterType<T, APIConfig & AnyProperty>, options?: HttpClientOptions);
85
+ /**
86
+ * Set parent progress manager for sub-module integration
87
+ */
88
+ setParentProgressManager(parentProgress: CLIProgressManager): void;
89
+ /**
90
+ * Create simple progress manager for single process tracking
91
+ */
92
+ protected createSimpleProgress(moduleName: string, total?: number): CLIProgressManager;
93
+ /**
94
+ * Create nested progress manager for multi-process tracking
95
+ */
96
+ protected createNestedProgress(moduleName: string): CLIProgressManager;
97
+ /**
98
+ * Complete progress manager
99
+ */
100
+ protected completeProgress(success?: boolean, error?: string): void;
101
+ /**
102
+ * Execute action with loading spinner for initial setup tasks
103
+ */
104
+ protected withLoadingSpinner<T>(message: string, action: () => Promise<T>): Promise<T>;
105
+ /**
106
+ * Update progress for a specific item
107
+ */
108
+ protected updateProgress(success: boolean, itemName: string, error?: string, processName?: string): void;
82
109
  }
83
110
  export default VariantAdapter;
@@ -319,6 +319,9 @@ exports.VariantManagementSDK = VariantManagementSDK;
319
319
  class VariantAdapter {
320
320
  constructor(config, options) {
321
321
  var _a, _b, _c, _d;
322
+ this.progressManager = null;
323
+ this.parentProgressManager = null;
324
+ this.currentModuleName = '';
322
325
  cli_utilities_1.log.debug('Initializing VariantAdapter...', (_a = this.exportConfig) === null || _a === void 0 ? void 0 : _a.context);
323
326
  if (config.httpClient) {
324
327
  cli_utilities_1.log.debug('Using HTTP client variant instance', (_b = this.exportConfig) === null || _b === void 0 ? void 0 : _b.context);
@@ -333,6 +336,78 @@ class VariantAdapter {
333
336
  this.messages = messages_1.default;
334
337
  cli_utilities_1.log.debug('VariantAdapter initialized successfully', (_d = this.exportConfig) === null || _d === void 0 ? void 0 : _d.context);
335
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
+ }
336
411
  }
337
412
  exports.VariantAdapter = VariantAdapter;
338
413
  exports.default = VariantAdapter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentstack/cli-variants",
3
- "version": "1.3.3",
3
+ "version": "2.0.0-beta.1",
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.1",
30
+ "@contentstack/cli-utilities": "~1.15.0",
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
  }