@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
@@ -9,6 +9,8 @@ import {
9
9
  managementSDKClient,
10
10
  authenticationHandler,
11
11
  log,
12
+ CLIProgressManager,
13
+ configHandler,
12
14
  } from '@contentstack/cli-utilities';
13
15
 
14
16
  import {
@@ -39,23 +41,24 @@ export class VariantHttpClient<C> extends AdapterHelper<C, HttpClient> implement
39
41
  super(config, options);
40
42
  this.baseURL = config.baseURL?.includes('http') ? `${config.baseURL}/v3` : `https://${config.baseURL}/v3`;
41
43
  this.apiClient.baseUrl(this.baseURL);
42
- log.debug(`VariantHttpClient initialized with base URL: ${this.baseURL}`, this.exportConfig?.context );
44
+ log.debug(`VariantHttpClient initialized with base URL: ${this.baseURL}`, this.exportConfig?.context);
43
45
  }
44
46
 
45
47
  async init(): Promise<void> {
46
- log.debug('Initializing VariantHttpClient...', this.exportConfig?.context );
48
+ log.debug('Initializing VariantHttpClient...', this.exportConfig?.context);
47
49
  await authenticationHandler.getAuthDetails();
48
50
  const token = authenticationHandler.accessToken;
49
- log.debug(`Authentication type: ${authenticationHandler.isOauthEnabled ? 'OAuth' : 'Token'}`, this.exportConfig?.context );
50
-
51
+ log.debug(
52
+ `Authentication type: ${authenticationHandler.isOauthEnabled ? 'OAuth' : 'Token'}`,
53
+ this.exportConfig?.context,
54
+ );
51
55
  if (authenticationHandler.isOauthEnabled) {
52
- log.debug('Setting OAuth authorization header', this.exportConfig?.context );
56
+ log.debug('Setting OAuth authorization header', this.exportConfig?.context);
53
57
  this.apiClient.headers({ authorization: token });
54
58
  } else {
55
- log.debug('Setting authtoken header', this.exportConfig?.context );
59
+ log.debug('Setting authtoken header', this.exportConfig?.context);
56
60
  this.apiClient.headers({ authtoken: token });
57
61
  }
58
- log.debug('VariantHttpClient initialization completed', this.exportConfig?.context );
59
62
  }
60
63
 
61
64
  async variantEntry(options: VariantOptions) {
@@ -96,22 +99,28 @@ export class VariantHttpClient<C> extends AdapterHelper<C, HttpClient> implement
96
99
  include_publish_details = variantConfig.query.include_publish_details || true,
97
100
  } = options;
98
101
 
99
- log.debug(`Fetching variant entries for content type: ${content_type_uid}, entry: ${entry_uid}, locale: ${locale}`, this.exportConfig?.context );
100
- log.debug(`Query parameters - skip: ${skip}, limit: ${limit}, include_variant: ${include_variant}, include_count: ${include_count}, include_publish_details: ${include_publish_details}`, this.exportConfig?.context );
102
+ log.debug(
103
+ `Fetching variant entries for content type: ${content_type_uid}, entry: ${entry_uid}, locale: ${locale}`,
104
+ this.exportConfig?.context,
105
+ );
106
+ log.debug(
107
+ `Query parameters - skip: ${skip}, limit: ${limit}, include_variant: ${include_variant}, include_count: ${include_count}, include_publish_details: ${include_publish_details}`,
108
+ this.exportConfig?.context,
109
+ );
101
110
 
102
111
  if (variantConfig.serveMockData && callback) {
103
- log.debug('Using mock data for variant entries', this.exportConfig?.context );
112
+ log.debug('Using mock data for variant entries', this.exportConfig?.context);
104
113
  let data = [] as Record<string, any>[];
105
114
 
106
115
  if (existsSync(variantConfig.mockDataPath)) {
107
- log.debug(`Loading mock data from: ${variantConfig.mockDataPath}`, this.exportConfig?.context );
116
+ log.debug(`Loading mock data from: ${variantConfig.mockDataPath}`, this.exportConfig?.context);
108
117
  data = require(variantConfig.mockDataPath) as Record<string, any>[];
109
118
  }
110
119
  callback(data);
111
120
  return;
112
121
  }
113
122
  if (!locale) {
114
- log.debug('No locale provided, skipping variant entries fetch', this.exportConfig?.context );
123
+ log.debug('No locale provided, skipping variant entries fetch', this.exportConfig?.context);
115
124
  return;
116
125
  }
117
126
 
@@ -153,19 +162,22 @@ export class VariantHttpClient<C> extends AdapterHelper<C, HttpClient> implement
153
162
  endpoint = endpoint.concat(query);
154
163
  }
155
164
 
156
- log.debug(`Making API call to: ${endpoint}`, this.exportConfig?.context );
165
+ log.debug(`Making API call to: ${endpoint}`, this.exportConfig?.context);
157
166
  const data = await this.apiClient.get(endpoint);
158
167
  const response = (await this.handleVariantAPIRes(data)) as { entries: VariantEntryStruct[]; count: number };
159
-
168
+
160
169
  if (response?.entries?.length) {
161
- log.debug(`Received ${response.entries?.length} variant entries out of total ${response.count}`, this.exportConfig?.context );
170
+ log.debug(
171
+ `Received ${response.entries?.length} variant entries out of total ${response.count}`,
172
+ this.exportConfig?.context,
173
+ );
162
174
  }
163
175
 
164
176
  if (callback) {
165
- log.debug('Executing callback with variant entries', this.exportConfig?.context );
177
+ log.debug('Executing callback with variant entries', this.exportConfig?.context);
166
178
  callback(response.entries);
167
179
  } else {
168
- log.debug('Adding variant entries to collection', this.exportConfig?.context );
180
+ log.debug('Adding variant entries to collection', this.exportConfig?.context);
169
181
  entries = entries.concat(response.entries);
170
182
  }
171
183
 
@@ -206,12 +218,15 @@ export class VariantHttpClient<C> extends AdapterHelper<C, HttpClient> implement
206
218
  options: CreateVariantEntryOptions,
207
219
  apiParams: Record<string, any>,
208
220
  ): Promise<VariantEntryStruct | string | void> {
209
- const { reject, resolve, variantUid, log } = apiParams;
221
+ const { reject, resolve, variantUid } = apiParams;
210
222
  const variantConfig = (this.config as ImportConfig).modules.variantEntry;
211
223
  const { locale = variantConfig.query.locale || 'en-us', variant_id, entry_uid, content_type_uid } = options;
212
-
213
- log.debug(`Creating variant entry for content type: ${content_type_uid}, entry: ${entry_uid}, variant: ${variant_id}`, this.exportConfig?.context );
214
-
224
+
225
+ log.debug(
226
+ `Creating variant entry for content type: ${content_type_uid}, entry: ${entry_uid}, variant: ${variant_id}`,
227
+ this.exportConfig?.context,
228
+ );
229
+
215
230
  let endpoint = `content_types/${content_type_uid}/entries/${entry_uid}/variants/${variant_id}?locale=${locale}`;
216
231
 
217
232
  const query = this.constructQuery(omit(variantConfig.query, ['locale']));
@@ -220,14 +235,12 @@ export class VariantHttpClient<C> extends AdapterHelper<C, HttpClient> implement
220
235
  endpoint = endpoint.concat(query);
221
236
  }
222
237
 
223
- log.debug(`Making API call to: ${endpoint}`, this.exportConfig?.context );
238
+ log.debug(`Making API call to: ${endpoint}`, this.exportConfig?.context);
224
239
 
225
240
  const onSuccess = (response: any) => {
226
- log.debug(`Variant entry created successfully: ${variantUid}`, this.exportConfig?.context );
227
241
  resolve({ response, apiData: { variantUid, entryUid: entry_uid }, log });
228
242
  };
229
243
  const onReject = (error: any) => {
230
- log.debug(`Failed to create variant entry: ${variantUid}`, this.exportConfig?.context );
231
244
  reject({
232
245
  error,
233
246
  apiData: { variantUid, entryUid: entry_uid },
@@ -263,14 +276,17 @@ export class VariantHttpClient<C> extends AdapterHelper<C, HttpClient> implement
263
276
  options: PublishVariantEntryOptions,
264
277
  apiParams: Record<string, any>,
265
278
  ) {
266
- const { reject, resolve, log, variantUid } = apiParams;
279
+ const { reject, resolve, variantUid } = apiParams;
267
280
  const { entry_uid, content_type_uid } = options;
268
-
269
- log.debug(`Publishing variant entry for content type: ${content_type_uid}, entry: ${entry_uid}`, this.exportConfig?.context );
270
-
281
+
282
+ log.debug(
283
+ `Publishing variant entry for content type: ${content_type_uid}, entry: ${entry_uid}`,
284
+ this.exportConfig?.context,
285
+ );
286
+
271
287
  let endpoint = `content_types/${content_type_uid}/entries/${entry_uid}/publish`;
272
288
 
273
- log.debug(`Making API call to: ${endpoint}`, this.exportConfig?.context );
289
+ log.debug(`Making API call to: ${endpoint}`, this.exportConfig?.context);
274
290
 
275
291
  const onSuccess = (response: any) => {
276
292
  log.debug(`Variant entry published successfully: ${entry_uid}`, this.exportConfig?.context );
@@ -310,22 +326,20 @@ export class VariantHttpClient<C> extends AdapterHelper<C, HttpClient> implement
310
326
  res: APIResponse,
311
327
  ): Promise<VariantEntryStruct | { entries: VariantEntryStruct[]; count: number } | string | any> {
312
328
  const { status, data } = res;
313
- log.debug(`API response status: ${status}`, this.exportConfig?.context );
329
+ log.debug(`API response status: ${status}`, this.exportConfig?.context);
314
330
 
315
331
  if (status >= 200 && status < 300) {
316
- log.debug('API request successful', this.exportConfig?.context );
332
+ log.debug('API request successful', this.exportConfig?.context);
317
333
  return data;
318
334
  }
319
335
 
320
- log.debug(`API request failed with status: ${status}`, this.exportConfig?.context );
336
+ log.debug(`API request failed with status: ${status}`, this.exportConfig?.context);
321
337
  // Refresh the access token if the response status is 401
322
338
  await authenticationHandler.refreshAccessToken(res);
323
339
 
324
- const errorMsg = data?.errors
325
- ? formatErrors(data.errors)
326
- : data?.error_message || data?.message || data;
340
+ const errorMsg = data?.errors ? formatErrors(data.errors) : data?.error_message || data?.message || data;
327
341
 
328
- log.debug(`API error: ${errorMsg}`, this.exportConfig?.context );
342
+ log.debug(`API error: ${errorMsg}`, this.exportConfig?.context);
329
343
  throw errorMsg;
330
344
  }
331
345
  }
@@ -338,19 +352,15 @@ export class VariantManagementSDK<T>
338
352
  public exportConfig?: any;
339
353
 
340
354
  async init(): Promise<void> {
341
- log.debug('Initializing VariantManagementSDK...', this.exportConfig?.context );
342
355
  this.apiClient = await managementSDKClient(this.config);
343
- log.debug('VariantManagementSDK initialized successfully', this.exportConfig?.context );
344
356
  }
345
357
 
346
358
  async variantEntry(options: VariantOptions) {
347
- log.debug('VariantEntry method called (SDK placeholder implementation)', this.exportConfig?.context );
348
359
  // TODO SDK implementation
349
360
  return { entry: {} };
350
361
  }
351
362
 
352
363
  async variantEntries(options: VariantsOption) {
353
- log.debug('VariantEntries method called (SDK placeholder implementation)', this.exportConfig?.context );
354
364
  // TODO SDK implementation
355
365
  return { entries: [{}] };
356
366
  }
@@ -360,7 +370,6 @@ export class VariantManagementSDK<T>
360
370
  options: CreateVariantEntryOptions,
361
371
  apiParams: Record<string, any>,
362
372
  ): Promise<VariantEntryStruct | string | void> {
363
- log.debug('CreateVariantEntry method called (SDK placeholder implementation)', this.exportConfig?.context );
364
373
  // FIXME placeholder
365
374
  return Promise.resolve({} as VariantEntryStruct);
366
375
  }
@@ -368,16 +377,15 @@ export class VariantManagementSDK<T>
368
377
  async handleVariantAPIRes(
369
378
  res: APIResponse,
370
379
  ): Promise<VariantEntryStruct | { entries: VariantEntryStruct[]; count: number } | string> {
371
- log.debug('HandleVariantAPIRes method called (SDK implementation)', this.exportConfig?.context );
372
380
  return res.data;
373
381
  }
374
382
 
375
383
  constructQuery(query: Record<string, any>): string | void {
376
- log.debug('ConstructQuery method called (SDK placeholder implementation)', this.exportConfig?.context );
384
+ log.debug('ConstructQuery method called (SDK placeholder implementation)', this.exportConfig?.context);
377
385
  }
378
386
 
379
387
  async delay(ms: number): Promise<void> {
380
- log.debug(`Delay method called for ${ms}ms (SDK placeholder implementation)`, this.exportConfig?.context );
388
+ log.debug(`Delay method called for ${ms}ms (SDK placeholder implementation)`, this.exportConfig?.context);
381
389
  }
382
390
  }
383
391
 
@@ -385,6 +393,9 @@ export class VariantAdapter<T> {
385
393
  protected variantInstance;
386
394
  public readonly messages: typeof messages;
387
395
  public exportConfig?: any;
396
+ protected progressManager: CLIProgressManager | null = null;
397
+ protected parentProgressManager: CLIProgressManager | null = null;
398
+ protected currentModuleName: string = '';
388
399
 
389
400
  constructor(config: ContentstackConfig & AnyProperty & AdapterType<T, ContentstackConfig>);
390
401
  constructor(config: APIConfig & AdapterType<T, APIConfig & AnyProperty>, options?: HttpClientOptions);
@@ -392,21 +403,97 @@ export class VariantAdapter<T> {
392
403
  config: APIConfig & AdapterType<T, (APIConfig & AnyProperty) | ContentstackConfig>,
393
404
  options?: HttpClientOptions,
394
405
  ) {
395
- log.debug('Initializing VariantAdapter...', this.exportConfig?.context );
396
-
406
+ log.debug('Initializing VariantAdapter...', this.exportConfig?.context);
407
+
397
408
  if (config.httpClient) {
398
- log.debug('Using HTTP client variant instance', this.exportConfig?.context );
409
+ log.debug('Using HTTP client variant instance', this.exportConfig?.context);
399
410
  const { httpClient, Adapter, ...restConfig } = config;
400
411
  this.variantInstance = new Adapter(restConfig, options);
401
412
  } else {
402
- log.debug('Using SDK variant instance', this.exportConfig?.context );
413
+ log.debug('Using SDK variant instance', this.exportConfig?.context);
403
414
  const { Adapter, ...restConfig } = config;
404
415
  this.variantInstance = new Adapter(restConfig);
405
416
  }
406
417
 
407
418
  this.messages = messages;
408
- log.debug('VariantAdapter initialized successfully', this.exportConfig?.context );
419
+ log.debug('VariantAdapter initialized successfully', this.exportConfig?.context);
420
+ }
421
+
422
+ /**
423
+ * Set parent progress manager for sub-module integration
424
+ */
425
+ public setParentProgressManager(parentProgress: CLIProgressManager): void {
426
+ this.parentProgressManager = parentProgress;
427
+ this.progressManager = parentProgress;
428
+ }
429
+
430
+ /**
431
+ * Create simple progress manager for single process tracking
432
+ */
433
+ protected createSimpleProgress(moduleName: string, total?: number): CLIProgressManager {
434
+ this.currentModuleName = moduleName;
435
+
436
+ // If we have a parent progress manager, use it instead of creating a new one
437
+ if (this.parentProgressManager) {
438
+ this.progressManager = this.parentProgressManager;
439
+ return this.progressManager;
440
+ }
441
+
442
+ const logConfig = configHandler.get('log') || {};
443
+ const showConsoleLogs = logConfig.showConsoleLogs ?? false;
444
+ this.progressManager = CLIProgressManager.createSimple(moduleName, total, showConsoleLogs);
445
+ return this.progressManager;
446
+ }
447
+
448
+ /**
449
+ * Create nested progress manager for multi-process tracking
450
+ */
451
+ protected createNestedProgress(moduleName: string): CLIProgressManager {
452
+ this.currentModuleName = moduleName;
453
+
454
+ // If we have a parent progress manager, use it instead of creating a new one
455
+ if (this.parentProgressManager) {
456
+ this.progressManager = this.parentProgressManager;
457
+ return this.progressManager;
458
+ }
459
+
460
+ const logConfig = configHandler.get('log') || {};
461
+ const showConsoleLogs = logConfig.showConsoleLogs ?? false;
462
+ this.progressManager = CLIProgressManager.createNested(moduleName, showConsoleLogs);
463
+ return this.progressManager;
464
+ }
465
+
466
+ /**
467
+ * Complete progress manager
468
+ */
469
+ protected completeProgress(success: boolean = true, error?: string): void {
470
+ // Only complete progress if we own the progress manager (no parent)
471
+ if (!this.parentProgressManager) {
472
+ this.progressManager?.complete(success, error);
473
+ }
474
+ this.progressManager = null;
475
+ }
476
+
477
+ /**
478
+ * Execute action with loading spinner for initial setup tasks
479
+ */
480
+ protected async withLoadingSpinner<T>(message: string, action: () => Promise<T>): Promise<T> {
481
+ const logConfig = configHandler.get('log') || {};
482
+ const showConsoleLogs = logConfig.showConsoleLogs ?? false;
483
+
484
+ if (showConsoleLogs) {
485
+ // If console logs are enabled, don't show spinner, just execute the action
486
+ return await action();
487
+ }
488
+ return await CLIProgressManager.withLoadingSpinner(message, action);
489
+ }
490
+
491
+ /**
492
+ * Update progress for a specific item
493
+ */
494
+ protected updateProgress(success: boolean, itemName: string, error?: string, processName?: string): void {
495
+ this.progressManager?.tick(success, itemName, error, processName);
409
496
  }
410
497
  }
411
498
 
412
- export default VariantAdapter;
499
+ export default VariantAdapter;
package/tsconfig.json CHANGED
@@ -15,5 +15,5 @@
15
15
  "es2020.promise"
16
16
  ],
17
17
  },
18
- "include": ["src/*"]
18
+ "include": ["src/*", "src/utils/constants.ts"]
19
19
  }