@contentstack/cli-variants 0.0.1-alpha → 1.1.0

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 (62) hide show
  1. package/lib/export/attributes.d.ts +2 -2
  2. package/lib/export/attributes.js +5 -4
  3. package/lib/export/audiences.d.ts +2 -2
  4. package/lib/export/audiences.js +5 -4
  5. package/lib/export/events.d.ts +2 -2
  6. package/lib/export/events.js +5 -4
  7. package/lib/export/experiences.d.ts +2 -2
  8. package/lib/export/experiences.js +20 -5
  9. package/lib/export/projects.d.ts +2 -2
  10. package/lib/export/projects.js +9 -6
  11. package/lib/export/variant-entries.js +1 -1
  12. package/lib/import/attribute.d.ts +1 -1
  13. package/lib/import/attribute.js +21 -10
  14. package/lib/import/audiences.d.ts +2 -2
  15. package/lib/import/audiences.js +21 -14
  16. package/lib/import/events.d.ts +1 -1
  17. package/lib/import/events.js +15 -8
  18. package/lib/import/experiences.d.ts +12 -5
  19. package/lib/import/experiences.js +86 -21
  20. package/lib/import/project.js +12 -11
  21. package/lib/import/variant-entries.d.ts +1 -1
  22. package/lib/import/variant-entries.js +28 -22
  23. package/lib/messages/index.d.ts +1 -1
  24. package/lib/messages/index.js +3 -2
  25. package/lib/types/export-config.d.ts +3 -3
  26. package/lib/types/import-config.d.ts +1 -1
  27. package/lib/types/personalization-api-adapter.d.ts +14 -1
  28. package/lib/types/variant-api-adapter.d.ts +3 -2
  29. package/lib/types/variant-entry.d.ts +2 -3
  30. package/lib/utils/attributes-helper.js +2 -2
  31. package/lib/utils/audiences-helper.js +14 -3
  32. package/lib/utils/error-helper.js +6 -6
  33. package/lib/utils/logger.js +5 -4
  34. package/lib/utils/personalization-api-adapter.d.ts +6 -2
  35. package/lib/utils/personalization-api-adapter.js +90 -23
  36. package/lib/utils/variant-api-adapter.d.ts +5 -4
  37. package/lib/utils/variant-api-adapter.js +29 -10
  38. package/package.json +2 -2
  39. package/src/export/attributes.ts +11 -7
  40. package/src/export/audiences.ts +7 -6
  41. package/src/export/events.ts +7 -6
  42. package/src/export/experiences.ts +24 -7
  43. package/src/export/projects.ts +11 -8
  44. package/src/export/variant-entries.ts +1 -2
  45. package/src/import/attribute.ts +31 -13
  46. package/src/import/audiences.ts +37 -19
  47. package/src/import/events.ts +25 -11
  48. package/src/import/experiences.ts +120 -30
  49. package/src/import/project.ts +13 -13
  50. package/src/import/variant-entries.ts +70 -37
  51. package/src/messages/index.ts +3 -2
  52. package/src/types/export-config.ts +3 -3
  53. package/src/types/import-config.ts +1 -1
  54. package/src/types/personalization-api-adapter.ts +14 -1
  55. package/src/types/variant-api-adapter.ts +3 -1
  56. package/src/types/variant-entry.ts +2 -3
  57. package/src/utils/attributes-helper.ts +2 -2
  58. package/src/utils/audiences-helper.ts +12 -2
  59. package/src/utils/error-helper.ts +6 -6
  60. package/src/utils/logger.ts +5 -4
  61. package/src/utils/personalization-api-adapter.ts +71 -18
  62. package/src/utils/variant-api-adapter.ts +21 -7
@@ -29,7 +29,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
29
29
  public entriesMapperPath: string;
30
30
  public variantEntryBasePath!: string;
31
31
  public variantIdList!: Record<string, unknown>;
32
- public personalizationConfig: ImportConfig['modules']['personalization'];
32
+ public personalizeConfig: ImportConfig['modules']['personalize'];
33
33
 
34
34
  public taxonomies!: Record<string, unknown>;
35
35
  public assetUrlMapper!: Record<string, any>;
@@ -49,15 +49,23 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
49
49
  headers: {
50
50
  api_key: config.apiKey,
51
51
  branch: config.branchName,
52
- authtoken: config.auth_token,
53
52
  organization_uid: config.org_uid,
54
- 'X-Project-Uid': config.modules.personalization.project_id,
53
+ 'X-Project-Uid': config.modules.personalize.project_id,
55
54
  },
56
- };
55
+ };
57
56
  super(Object.assign(omit(config, ['helpers']), conf));
58
- this.entriesMapperPath = resolve(sanitizePath(config.backupDir), sanitizePath(config.branchName || ''), 'mapper', 'entries');
59
- this.personalizationConfig = this.config.modules.personalization;
60
- this.entriesDirPath = resolve(sanitizePath(config.backupDir), sanitizePath(config.branchName || ''), sanitizePath(config.modules.entries.dirName));
57
+ this.entriesMapperPath = resolve(
58
+ sanitizePath(config.backupDir),
59
+ sanitizePath(config.branchName || ''),
60
+ 'mapper',
61
+ 'entries',
62
+ );
63
+ this.personalizeConfig = this.config.modules.personalize;
64
+ this.entriesDirPath = resolve(
65
+ sanitizePath(config.backupDir),
66
+ sanitizePath(config.branchName || ''),
67
+ sanitizePath(config.modules.entries.dirName),
68
+ );
61
69
  this.failedVariantPath = resolve(sanitizePath(this.entriesMapperPath), 'failed-entry-variants.json');
62
70
  this.failedVariantEntries = new Map();
63
71
  }
@@ -75,8 +83,8 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
75
83
  const variantIdPath = resolve(
76
84
  sanitizePath(this.config.backupDir),
77
85
  'mapper',
78
- sanitizePath(this.personalizationConfig.dirName),
79
- sanitizePath(this.personalizationConfig.experiences.dirName),
86
+ sanitizePath(this.personalizeConfig.dirName),
87
+ sanitizePath(this.personalizeConfig.experiences.dirName),
80
88
  'variants-uid-mapping.json',
81
89
  );
82
90
 
@@ -107,7 +115,12 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
107
115
  'terms',
108
116
  'success.json',
109
117
  );
110
- const marketplaceAppMapperPath = resolve(sanitizePath(this.config.backupDir), 'mapper', 'marketplace_apps', 'uid-mapping.json');
118
+ const marketplaceAppMapperPath = resolve(
119
+ sanitizePath(this.config.backupDir),
120
+ 'mapper',
121
+ 'marketplace_apps',
122
+ 'uid-mapping.json',
123
+ );
111
124
  const envPath = resolve(sanitizePath(this.config.backupDir), 'environments', 'environments.json');
112
125
  // NOTE Read and store list of variant IDs
113
126
  this.variantIdList = (fsUtil.readFile(variantIdPath, true) || {}) as Record<string, unknown>;
@@ -124,7 +137,8 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
124
137
  this.assetUidMapper = (fsUtil.readFile(assetUidMapperPath, true) || {}) as Record<string, any>;
125
138
  this.assetUrlMapper = (fsUtil.readFile(assetUrlMapperPath, true) || {}) as Record<string, any>;
126
139
  this.environments = (fsUtil.readFile(envPath, true) || {}) as Record<string, any>;
127
-
140
+ // set the token
141
+ await this.variantInstance.init();
128
142
  for (const entriesForVariant of entriesForVariants) {
129
143
  await this.importVariantEntries(entriesForVariant);
130
144
  }
@@ -141,9 +155,22 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
141
155
  const { content_type, locale, entry_uid } = entriesForVariant;
142
156
  const ctConfig = this.config.modules['content-types'];
143
157
  const contentType: ContentTypeStruct = JSON.parse(
144
- readFileSync(resolve(sanitizePath(this.config.backupDir), sanitizePath(ctConfig.dirName), `${sanitizePath(content_type)}.json`), 'utf8'),
158
+ readFileSync(
159
+ resolve(
160
+ sanitizePath(this.config.backupDir),
161
+ sanitizePath(ctConfig.dirName),
162
+ `${sanitizePath(content_type)}.json`,
163
+ ),
164
+ 'utf8',
165
+ ),
166
+ );
167
+ const variantEntryBasePath = join(
168
+ sanitizePath(this.entriesDirPath),
169
+ sanitizePath(content_type),
170
+ sanitizePath(locale),
171
+ sanitizePath(variantEntry.dirName),
172
+ sanitizePath(entry_uid),
145
173
  );
146
- const variantEntryBasePath = join(sanitizePath(this.entriesDirPath), sanitizePath(content_type), sanitizePath(locale), sanitizePath(variantEntry.dirName), sanitizePath(entry_uid));
147
174
  const fs = new FsUtility({ basePath: variantEntryBasePath });
148
175
 
149
176
  for (const _ in fs.indexFileContent) {
@@ -202,7 +229,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
202
229
  log(this.config, error, 'error');
203
230
  };
204
231
  // NOTE Find new variant Id by old Id
205
- const variant_id = this.variantIdList[variantEntry.variant_id] as string;
232
+ const variantId = this.variantIdList[variantEntry._variant._uid] as string;
206
233
  // NOTE Replace all the relation data UID's
207
234
  variantEntry = this.handleVariantEntryRelationalData(contentType, variantEntry);
208
235
  const changeSet = this.serializeChangeSet(variantEntry);
@@ -211,19 +238,19 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
211
238
  ...changeSet,
212
239
  };
213
240
 
214
- if (variant_id) {
241
+ if (variantId) {
215
242
  const promise = this.variantInstance.createVariantEntry(
216
243
  createVariantReq,
217
244
  {
218
245
  locale,
219
246
  entry_uid: entryUid,
220
- variant_id,
247
+ variant_id: variantId,
221
248
  content_type_uid: content_type,
222
249
  },
223
250
  {
224
251
  reject: onReject.bind(this),
225
252
  resolve: onSuccess.bind(this),
226
- variantUid: variantEntry.uid,
253
+ variantUid: variantId,
227
254
  log: log,
228
255
  },
229
256
  );
@@ -341,7 +368,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
341
368
  * @param variantEntry - The entry variant to update.
342
369
  */
343
370
  updateFileFields(variantEntry: VariantEntryStruct) {
344
- const setValue = (currentObj: VariantEntryStruct, keys: Array<string>) => {
371
+ const setValue = (currentObj: VariantEntryStruct, keys: string[]) => {
345
372
  if (!currentObj || keys.length === 0) return;
346
373
 
347
374
  const [firstKey, ...restKeys] = keys;
@@ -353,7 +380,14 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
353
380
  } else if (currentObj && typeof currentObj === 'object') {
354
381
  if (firstKey in currentObj) {
355
382
  if (keys.length === 1) {
356
- currentObj[firstKey] = { uid: currentObj[firstKey], filename: 'dummy.jpeg' };
383
+ // Check if the current property is already an object with uid and filename
384
+ const existingValue = currentObj[firstKey];
385
+
386
+ if (existingValue && typeof existingValue === 'object' && existingValue.uid) {
387
+ currentObj[firstKey] = { uid: existingValue.uid, filename: 'dummy.jpeg' };
388
+ } else {
389
+ currentObj[firstKey] = { uid: currentObj[firstKey], filename: 'dummy.jpeg' };
390
+ }
357
391
  } else {
358
392
  setValue(currentObj[firstKey], restKeys);
359
393
  }
@@ -361,13 +395,12 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
361
395
  }
362
396
  };
363
397
 
364
- const pathsToUpdate = variantEntry?._metadata?.references
365
- .filter((ref: any) => ref._content_type_uid === 'sys_assets')
366
- .map((ref: any) => ref.path);
398
+ const pathsToUpdate =
399
+ variantEntry?._metadata?.references
400
+ ?.filter((ref: any) => ref._content_type_uid === 'sys_assets')
401
+ .map((ref: any) => ref.path) || [];
367
402
 
368
- if (pathsToUpdate) {
369
- pathsToUpdate.forEach((path: string) => setValue(variantEntry, path.split('.')));
370
- }
403
+ pathsToUpdate.forEach((path: string) => setValue(variantEntry, path.split('.')));
371
404
  }
372
405
 
373
406
  /**
@@ -379,24 +412,25 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
379
412
  */
380
413
  async publishVariantEntries(batch: VariantEntryStruct[], entryUid: string, content_type: string) {
381
414
  const allPromise = [];
415
+ log(
416
+ this.config,
417
+ `Publishing variant entries for entry uid '${entryUid}' of Content Type '${content_type}'`,
418
+ 'info',
419
+ );
382
420
  for (let [, variantEntry] of entries(batch)) {
383
- const variantUid = variantEntry.uid;
384
- const oldVariantUid = variantEntry.variant_id || '';
421
+ const variantEntryUID = variantEntry.uid;
422
+ const oldVariantUid = variantEntry._variant._uid || '';
385
423
  const newVariantUid = this.variantIdList[oldVariantUid] as string;
386
424
 
387
425
  if (!newVariantUid) {
388
- log(
389
- this.config,
390
- `${this.messages.VARIANT_ID_NOT_FOUND}. Skipping entry variant publish for ${variantUid}`,
391
- 'info',
392
- );
426
+ log(this.config, `${this.messages.VARIANT_ID_NOT_FOUND}. Skipping entry variant publish`, 'info');
393
427
  continue;
394
428
  }
395
429
 
396
- if (this.failedVariantEntries.has(variantUid)) {
430
+ if (this.failedVariantEntries.has(variantEntryUID)) {
397
431
  log(
398
432
  this.config,
399
- `${this.messages.VARIANT_UID_NOT_FOUND}. Skipping entry variant publish for ${variantUid}`,
433
+ `${this.messages.VARIANT_UID_NOT_FOUND}. Skipping entry variant publish for ${variantEntryUID}`,
400
434
  'info',
401
435
  );
402
436
  continue;
@@ -423,11 +457,9 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
423
457
  entry: {
424
458
  environments,
425
459
  locales,
426
- publish_with_base_entry: false,
427
460
  variants: [{ uid: newVariantUid, version: 1 }],
428
461
  },
429
462
  locale: variantEntry.locale,
430
- version: 1,
431
463
  };
432
464
 
433
465
  const promise = this.variantInstance.publishVariantEntry(
@@ -440,13 +472,14 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
440
472
  reject: onReject.bind(this),
441
473
  resolve: onSuccess.bind(this),
442
474
  log: log,
443
- variantUid,
475
+ variantUid: newVariantUid,
444
476
  },
445
477
  );
446
478
 
447
479
  allPromise.push(promise);
448
480
  }
449
481
  await Promise.allSettled(allPromise);
482
+ log(this.config, `Published variant entries for entry uid '${entryUid}' of Content Type '${content_type}'`, 'info');
450
483
  }
451
484
 
452
485
  /**
@@ -23,7 +23,7 @@ const expImportMsg = {
23
23
  UPDATING_CT_IN_EXP: 'Updating content types in experiences...',
24
24
  UPDATED_CT_IN_EXP: 'Successfully updated content types in experiences!',
25
25
  VALIDATE_VARIANT_AND_VARIANT_GRP: 'Validating variant group and variants creation...',
26
- PERSONALIZATION_JOB_FAILURE: 'Something went wrong with personalization background job! Failed to fetch some variant & variant groups',
26
+ PERSONALIZE_JOB_FAILURE: 'Something went wrong! Failed to fetch some variant and variant groups.',
27
27
  };
28
28
 
29
29
  const messages: typeof errors & typeof commonMsg & typeof migrationMsg & typeof variantEntry & typeof expImportMsg = {
@@ -50,7 +50,8 @@ function $t(msg: string, args: Record<string, string>): string {
50
50
 
51
51
  for (const key of Object.keys(args)) {
52
52
  const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
53
- msg = msg.replace(new RegExp(`{${escapedKey}}`, 'g'), args[key] || escapedKey);
53
+ const placeholder = `{${escapedKey}}`;
54
+ msg = msg.split(placeholder).join(args[key]);
54
55
  }
55
56
 
56
57
  return msg;
@@ -20,7 +20,7 @@ export type Modules =
20
20
  | 'labels'
21
21
  | 'marketplace-apps'
22
22
  | 'taxonomies'
23
- | 'personalization';
23
+ | 'personalize';
24
24
 
25
25
  export type branch = {
26
26
  uid: string;
@@ -155,7 +155,7 @@ export interface DefaultConfig {
155
155
  include_publish_details: boolean;
156
156
  } & AnyProperty;
157
157
  } & AnyProperty;
158
- personalization: {
158
+ personalize: {
159
159
  dirName: string;
160
160
  baseURL: Record<string, string>;
161
161
  } & AnyProperty;
@@ -269,7 +269,7 @@ export interface ExportConfig extends DefaultConfig {
269
269
  region: any;
270
270
  }
271
271
 
272
- export interface PersonalizationConfig {
272
+ export interface PersonalizeConfig {
273
273
  dirName: string;
274
274
  baseURL: Record<string, string>;
275
275
  }
@@ -12,7 +12,7 @@ export interface ImportDefaultConfig extends AnyProperty {
12
12
  dirName: string;
13
13
  fileName: string;
14
14
  };
15
- personalization: {
15
+ personalize: {
16
16
  baseURL: Record<string, string>;
17
17
  dirName: string;
18
18
  importData: boolean;
@@ -130,6 +130,18 @@ export type ExperienceStruct = {
130
130
  content_types?: string[];
131
131
  } & AnyProperty;
132
132
 
133
+ export interface CreateExperienceVersionInput {
134
+ name: string;
135
+ __type: string;
136
+ description: string;
137
+ targeting?: ExpTargeting;
138
+ variations: ExpVariations[];
139
+ variationSplit?: string;
140
+ metrics?: ExpMetric[];
141
+ status: string;
142
+ metadata?: object;
143
+ variants: Array<ExpVariations>;
144
+ }
133
145
  export interface CreateExperienceInput {
134
146
  name: string;
135
147
  __type: string;
@@ -140,6 +152,7 @@ export interface CreateExperienceInput {
140
152
  metrics?: ExpMetric[];
141
153
  status: string;
142
154
  metadata?: object;
155
+ variants?: Array<ExpVariations>;
143
156
  }
144
157
 
145
158
  export interface UpdateExperienceInput {
@@ -193,5 +206,5 @@ export interface Personalization<T> extends AdapterHelperInterface<T, HttpClient
193
206
 
194
207
  updateCTsInExperience(experience: UpdateExperienceInput, experienceUid: string): Promise<CMSExperienceStruct | void>;
195
208
 
196
- handleVariantAPIRes(res: any): VariantAPIRes;
209
+ handleVariantAPIRes(res: any): Promise<VariantAPIRes>;
197
210
  }
@@ -42,6 +42,8 @@ export type VariantOptions = VariantsOption & {
42
42
  };
43
43
 
44
44
  export interface VariantInterface<T, ApiClient> extends AdapterHelperInterface<T, ApiClient> {
45
+ init(): Promise<void>;
46
+
45
47
  variantEntry(options: VariantOptions): Promise<{ entry: Record<string, any> }>;
46
48
 
47
49
  variantEntries(options: VariantsOption): Promise<{ entries?: Record<string, any>[] | unknown[] } | void>;
@@ -52,5 +54,5 @@ export interface VariantInterface<T, ApiClient> extends AdapterHelperInterface<T
52
54
  apiParams: Record<string, any>,
53
55
  ): Promise<VariantEntryStruct | string | void>;
54
56
 
55
- handleVariantAPIRes(res: APIResponse): VariantEntryStruct | { entries: VariantEntryStruct[]; count: number } | string;
57
+ handleVariantAPIRes(res: APIResponse): Promise<VariantEntryStruct | { entries: VariantEntryStruct[]; count: number } | string>;
56
58
  }
@@ -3,11 +3,11 @@ import { AnyProperty } from './utils';
3
3
  export type VariantEntryStruct = {
4
4
  uid: string;
5
5
  title: string;
6
- variant_id: string;
7
6
  locale: string;
8
7
  _version: number;
9
8
  _variant: {
10
- uid: string;
9
+ _uid: string;
10
+ _instance_uid: string;
11
11
  _change_set: string[];
12
12
  _base_entry_version: number;
13
13
  };
@@ -50,7 +50,6 @@ export type PublishVariantEntryDto = {
50
50
  entry: {
51
51
  environments: string[];
52
52
  locales: string[];
53
- publish_with_base_entry: boolean;
54
53
  variants: {
55
54
  uid: string;
56
55
  version?: number;
@@ -12,8 +12,8 @@ export const lookUpAttributes = (attributeRules: Record<string, any>[], attribut
12
12
  // Check if attribute reference exists in attributesUid
13
13
  const attributeRef = rule.attribute?.ref;
14
14
  const attributeType = rule.attribute['__type'];
15
- // check if type is UserAttributeReference
16
- if (attributeType === 'UserAttributeReference') {
15
+ // check if type is CustomAttributeReference
16
+ if (attributeType === 'CustomAttributeReference') {
17
17
  if (attributeRef && attributesUid.hasOwnProperty(attributeRef) && attributesUid[attributeRef]) {
18
18
  rule.attribute.ref = attributesUid[attributeRef];
19
19
  } else {
@@ -1,4 +1,4 @@
1
- import { CreateExperienceInput } from '../types';
1
+ import { CreateExperienceInput, CreateExperienceVersionInput } from '../types';
2
2
 
3
3
  /**
4
4
  * function for substituting an old audience UID with a new one or deleting the audience information if it does not exist
@@ -37,10 +37,20 @@ export const lookUpAudiences = (
37
37
  }
38
38
  }
39
39
  }
40
+ } else if (experience.variants) {
41
+ for (let index = experience.variants.length - 1; index >= 0; index--) {
42
+ const expVariations = experience.variants[index];
43
+ if (expVariations['__type'] === 'SegmentedVariant' && expVariations?.audiences?.length) {
44
+ updateAudiences(expVariations.audiences, audiencesUid);
45
+ if (!expVariations.audiences.length) {
46
+ experience.variants.splice(index, 1);
47
+ }
48
+ }
49
+ }
40
50
  }
41
51
 
42
- // Update targeting audiences
43
52
  if (experience?.targeting?.hasOwnProperty('audience') && experience?.targeting?.audience?.audiences?.length) {
53
+ // Update targeting audiences
44
54
  updateAudiences(experience.targeting.audience.audiences, audiencesUid);
45
55
  if (!experience.targeting.audience.audiences.length) {
46
56
  experience.targeting = {};
@@ -9,18 +9,18 @@ export function formatErrors(errors: any): string {
9
9
  for (const errorKey in errors) {
10
10
  const errorValue = errors[errorKey];
11
11
  if (Array.isArray(errorValue)) {
12
- errorMessages.push(...errorValue.map((error: any) => formatError(error)));
12
+ errorMessages.push(...errorValue.map((error: any) => formatError(errorKey, error)));
13
13
  } else {
14
- errorMessages.push(formatError(errorValue));
14
+ errorMessages.push(formatError(errorKey, errorValue));
15
15
  }
16
16
  }
17
17
 
18
18
  return errorMessages.join(' ');
19
19
  }
20
20
 
21
- function formatError(error: any): string {
22
- if (typeof error === 'object') {
23
- return Object.values(error).join(' ');
21
+ function formatError(errorKey: string, error: any): string {
22
+ if (typeof error === 'object' && error !== null) {
23
+ return `${errorKey}: ${Object.values(error).join(' ')}`;
24
24
  }
25
- return String(error);
25
+ return `${errorKey}: ${error}`;
26
26
  }
@@ -51,9 +51,9 @@ let errorLogger: winston.Logger;
51
51
  let successTransport;
52
52
  let errorTransport;
53
53
 
54
- function init(_logPath: string) {
54
+ function init(_logPath: string, module: string) {
55
55
  if (!logger || !errorLogger) {
56
- const logsDir = path.resolve(sanitizePath(_logPath), 'logs', 'export');
56
+ const logsDir = path.resolve(sanitizePath(_logPath), 'logs', sanitizePath(module));
57
57
  // Create dir if doesn't already exist
58
58
  mkdirp.sync(logsDir);
59
59
 
@@ -131,11 +131,12 @@ function init(_logPath: string) {
131
131
  export const log = (config: ExportConfig | ImportConfig, message: any, type: 'info' | 'error' | 'success') => {
132
132
  const logsPath = config.data;
133
133
  // ignoring the type argument, as we are not using it to create a logfile anymore
134
+ const module = (config as ImportConfig)['backupDir'] ? 'import' : 'export';
134
135
  if (type !== 'error') {
135
136
  // removed type argument from init method
136
- init(logsPath).log(message);
137
+ init(logsPath, module).log(message);
137
138
  } else {
138
- init(logsPath).error(message);
139
+ init(logsPath, module).error(message);
139
140
  }
140
141
  };
141
142
 
@@ -1,5 +1,5 @@
1
1
  import { AdapterHelper } from './adapter-helper';
2
- import { HttpClient } from '@contentstack/cli-utilities';
2
+ import { HttpClient, authenticationHandler } from '@contentstack/cli-utilities';
3
3
 
4
4
  import {
5
5
  ProjectStruct,
@@ -22,6 +22,7 @@ import {
22
22
  APIResponse,
23
23
  VariantGroupStruct,
24
24
  VariantGroup,
25
+ CreateExperienceVersionInput,
25
26
  } from '../types';
26
27
  import { formatErrors } from './error-helper';
27
28
 
@@ -30,10 +31,27 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
30
31
  super(options);
31
32
  }
32
33
 
34
+ async init(): Promise<void> {
35
+ await authenticationHandler.getAuthDetails();
36
+ const token = authenticationHandler.accessToken;
37
+ if (authenticationHandler.isOauthEnabled) {
38
+ this.apiClient.headers({ authorization: token });
39
+ if(this.adapterConfig.cmaConfig) {
40
+ this.cmaAPIClient?.headers({ authorization: token });
41
+ }
42
+ } else {
43
+ this.apiClient.headers({ authtoken: token });
44
+ if(this.adapterConfig.cmaConfig) {
45
+ this.cmaAPIClient?.headers({ authtoken: token });
46
+ }
47
+ }
48
+ }
49
+
33
50
  async projects(options: GetProjectsParams): Promise<ProjectStruct[]> {
51
+ await this.init();
34
52
  const getProjectEndPoint = `/projects?connectedStackApiKey=${options.connectedStackApiKey}`;
35
53
  const data = await this.apiClient.get(getProjectEndPoint);
36
- return this.handleVariantAPIRes(data) as ProjectStruct[];
54
+ return (await this.handleVariantAPIRes(data)) as ProjectStruct[];
37
55
  }
38
56
 
39
57
  /**
@@ -48,7 +66,7 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
48
66
  */
49
67
  async createProject(project: CreateProjectInput): Promise<ProjectStruct> {
50
68
  const data = await this.apiClient.post<ProjectStruct>('/projects', project);
51
- return this.handleVariantAPIRes(data) as ProjectStruct;
69
+ return (await this.handleVariantAPIRes(data)) as ProjectStruct;
52
70
  }
53
71
 
54
72
  /**
@@ -62,19 +80,48 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
62
80
  */
63
81
  async createAttribute(attribute: CreateAttributeInput): Promise<AttributeStruct> {
64
82
  const data = await this.apiClient.post<AttributeStruct>('/attributes', attribute);
65
- return this.handleVariantAPIRes(data) as AttributeStruct;
83
+ return (await this.handleVariantAPIRes(data)) as AttributeStruct;
66
84
  }
67
85
 
68
86
  async getExperiences(): Promise<ExperienceStruct[]> {
69
87
  const getExperiencesEndPoint = `/experiences`;
70
88
  const data = await this.apiClient.get(getExperiencesEndPoint);
71
- return this.handleVariantAPIRes(data) as ExperienceStruct[];
89
+ return (await this.handleVariantAPIRes(data)) as ExperienceStruct[];
72
90
  }
73
91
 
74
92
  async getExperience(experienceUid: string): Promise<ExperienceStruct | void> {
75
93
  const getExperiencesEndPoint = `/experiences/${experienceUid}`;
76
94
  const data = await this.apiClient.get(getExperiencesEndPoint);
77
- return this.handleVariantAPIRes(data) as ExperienceStruct;
95
+ return (await this.handleVariantAPIRes(data)) as ExperienceStruct;
96
+ }
97
+
98
+ async getExperienceVersions(experienceUid: string): Promise<ExperienceStruct | void> {
99
+ const getExperiencesVersionsEndPoint = `/experiences/${experienceUid}/versions`;
100
+ const data = await this.apiClient.get(getExperiencesVersionsEndPoint);
101
+ return (await this.handleVariantAPIRes(data)) as ExperienceStruct;
102
+ }
103
+
104
+ async createExperienceVersion(
105
+ experienceUid: string,
106
+ input: CreateExperienceVersionInput,
107
+ ): Promise<ExperienceStruct | void> {
108
+ const createExperiencesVersionsEndPoint = `/experiences/${experienceUid}/versions`;
109
+ const data = await this.apiClient.post(createExperiencesVersionsEndPoint, input);
110
+ return (await this.handleVariantAPIRes(data)) as ExperienceStruct;
111
+ }
112
+
113
+ async updateExperienceVersion(
114
+ experienceUid: string,
115
+ versionId: string,
116
+ input: CreateExperienceVersionInput,
117
+ ): Promise<ExperienceStruct | void> {
118
+ // loop through input and remove shortId from variant
119
+ if (input?.variants) {
120
+ input.variants = input.variants.map(({ shortUid, ...rest }) => rest);
121
+ }
122
+ const updateExperiencesVersionsEndPoint = `/experiences/${experienceUid}/versions/${versionId}`;
123
+ const data = await this.apiClient.put(updateExperiencesVersionsEndPoint, input);
124
+ return (await this.handleVariantAPIRes(data)) as ExperienceStruct;
78
125
  }
79
126
 
80
127
  async getVariantGroup(input: GetVariantGroupInput): Promise<VariantGroupStruct | void> {
@@ -83,7 +130,7 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
83
130
  const data = await this.cmaAPIClient
84
131
  .queryParams({ experience_uid: input.experienceUid })
85
132
  .get(getVariantGroupEndPoint);
86
- return this.handleVariantAPIRes(data) as VariantGroupStruct;
133
+ return (await this.handleVariantAPIRes(data)) as VariantGroupStruct;
87
134
  }
88
135
  }
89
136
 
@@ -91,28 +138,28 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
91
138
  if (this.cmaAPIClient) {
92
139
  const updateVariantGroupEndPoint = `/variant_groups/${input.uid}`;
93
140
  const data = await this.cmaAPIClient.put(updateVariantGroupEndPoint, input);
94
- return this.handleVariantAPIRes(data) as VariantGroup;
141
+ return (await this.handleVariantAPIRes(data)) as VariantGroup;
95
142
  }
96
143
  }
97
144
 
98
145
  async getEvents(): Promise<EventStruct[] | void> {
99
146
  const data = await this.apiClient.get<EventStruct>('/events');
100
- return this.handleVariantAPIRes(data) as EventStruct[];
147
+ return (await this.handleVariantAPIRes(data)) as EventStruct[];
101
148
  }
102
149
 
103
150
  async createEvents(event: CreateEventInput): Promise<void | EventStruct> {
104
151
  const data = await this.apiClient.post<EventStruct>('/events', event);
105
- return this.handleVariantAPIRes(data) as EventStruct;
152
+ return (await this.handleVariantAPIRes(data)) as EventStruct;
106
153
  }
107
154
 
108
155
  async getAudiences(): Promise<AudienceStruct[] | void> {
109
156
  const data = await this.apiClient.get<AudienceStruct>('/audiences');
110
- return this.handleVariantAPIRes(data) as AudienceStruct[];
157
+ return (await this.handleVariantAPIRes(data)) as AudienceStruct[];
111
158
  }
112
159
 
113
160
  async getAttributes(): Promise<AttributeStruct[] | void> {
114
161
  const data = await this.apiClient.get<AttributeStruct>('/attributes');
115
- return this.handleVariantAPIRes(data) as AttributeStruct[];
162
+ return (await this.handleVariantAPIRes(data)) as AttributeStruct[];
116
163
  }
117
164
 
118
165
  /**
@@ -125,7 +172,7 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
125
172
  */
126
173
  async createAudience(audience: CreateAudienceInput): Promise<void | AudienceStruct> {
127
174
  const data = await this.apiClient.post<AudienceStruct>('/audiences', audience);
128
- return this.handleVariantAPIRes(data) as AudienceStruct;
175
+ return (await this.handleVariantAPIRes(data)) as AudienceStruct;
129
176
  }
130
177
 
131
178
  /**
@@ -138,7 +185,7 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
138
185
  */
139
186
  async createExperience(experience: CreateExperienceInput): Promise<void | ExperienceStruct> {
140
187
  const data = await this.apiClient.post<ExperienceStruct>('/experiences', experience);
141
- return this.handleVariantAPIRes(data) as ExperienceStruct;
188
+ return (await this.handleVariantAPIRes(data)) as ExperienceStruct;
142
189
  }
143
190
 
144
191
  /**
@@ -152,7 +199,7 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
152
199
  ): Promise<void | CMSExperienceStruct> {
153
200
  const updateCTInExpEndPoint = `/experiences/${experienceUid}/cms-integration/variant-group`;
154
201
  const data = await this.apiClient.post<CMSExperienceStruct>(updateCTInExpEndPoint, experience);
155
- return this.handleVariantAPIRes(data) as CMSExperienceStruct;
202
+ return (await this.handleVariantAPIRes(data)) as CMSExperienceStruct;
156
203
  }
157
204
 
158
205
  /**
@@ -163,7 +210,7 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
163
210
  async getCTsFromExperience(experienceUid: string): Promise<void | CMSExperienceStruct> {
164
211
  const getCTFromExpEndPoint = `/experiences/${experienceUid}/cms-integration/variant-group`;
165
212
  const data = await this.apiClient.get<CMSExperienceStruct>(getCTFromExpEndPoint);
166
- return this.handleVariantAPIRes(data) as CMSExperienceStruct;
213
+ return (await this.handleVariantAPIRes(data)) as CMSExperienceStruct;
167
214
  }
168
215
 
169
216
  /**
@@ -172,16 +219,22 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
172
219
  * @returns The variant API response data.
173
220
  * @throws If the API response status is not within the success range, an error message is thrown.
174
221
  */
175
- handleVariantAPIRes(res: APIResponse): VariantAPIRes {
222
+ async handleVariantAPIRes(res: APIResponse): Promise<VariantAPIRes> {
176
223
  const { status, data } = res;
177
224
 
178
225
  if (status >= 200 && status < 300) {
179
226
  return data;
180
227
  }
181
228
 
229
+ // Refresh the access token if it has expired
230
+ await authenticationHandler.refreshAccessToken(res);
231
+
182
232
  const errorMsg = data?.errors
183
233
  ? formatErrors(data.errors)
184
- : data?.error_message || data?.message || 'Something went wrong while processing variant entries request!';
234
+ : data?.error ||
235
+ data?.error_message ||
236
+ data?.message ||
237
+ 'Something went wrong while processing variant entries request!';
185
238
 
186
239
  throw errorMsg;
187
240
  }