@contentstack/cli-variants 0.0.1-alpha → 1.0.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 (53) hide show
  1. package/lib/export/attributes.d.ts +2 -2
  2. package/lib/export/attributes.js +3 -3
  3. package/lib/export/audiences.d.ts +2 -2
  4. package/lib/export/audiences.js +3 -3
  5. package/lib/export/events.d.ts +2 -2
  6. package/lib/export/events.js +3 -3
  7. package/lib/export/experiences.d.ts +2 -2
  8. package/lib/export/experiences.js +17 -3
  9. package/lib/export/projects.d.ts +2 -2
  10. package/lib/export/projects.js +7 -5
  11. package/lib/import/attribute.d.ts +1 -1
  12. package/lib/import/attribute.js +20 -10
  13. package/lib/import/audiences.d.ts +2 -2
  14. package/lib/import/audiences.js +20 -14
  15. package/lib/import/events.d.ts +1 -1
  16. package/lib/import/events.js +14 -8
  17. package/lib/import/experiences.d.ts +12 -5
  18. package/lib/import/experiences.js +84 -20
  19. package/lib/import/project.js +10 -10
  20. package/lib/import/variant-entries.d.ts +1 -1
  21. package/lib/import/variant-entries.js +13 -13
  22. package/lib/messages/index.d.ts +1 -1
  23. package/lib/messages/index.js +3 -2
  24. package/lib/types/export-config.d.ts +3 -3
  25. package/lib/types/import-config.d.ts +1 -1
  26. package/lib/types/personalization-api-adapter.d.ts +13 -0
  27. package/lib/types/variant-entry.d.ts +2 -2
  28. package/lib/utils/audiences-helper.js +14 -3
  29. package/lib/utils/error-helper.js +1 -1
  30. package/lib/utils/personalization-api-adapter.d.ts +4 -1
  31. package/lib/utils/personalization-api-adapter.js +40 -1
  32. package/lib/utils/variant-api-adapter.js +1 -0
  33. package/package.json +1 -1
  34. package/src/export/attributes.ts +5 -5
  35. package/src/export/audiences.ts +5 -5
  36. package/src/export/events.ts +5 -5
  37. package/src/export/experiences.ts +21 -5
  38. package/src/export/projects.ts +9 -7
  39. package/src/import/attribute.ts +30 -12
  40. package/src/import/audiences.ts +36 -18
  41. package/src/import/events.ts +24 -10
  42. package/src/import/experiences.ts +118 -28
  43. package/src/import/project.ts +10 -10
  44. package/src/import/variant-entries.ts +48 -25
  45. package/src/messages/index.ts +3 -2
  46. package/src/types/export-config.ts +3 -3
  47. package/src/types/import-config.ts +1 -1
  48. package/src/types/personalization-api-adapter.ts +13 -0
  49. package/src/types/variant-entry.ts +2 -2
  50. package/src/utils/audiences-helper.ts +12 -2
  51. package/src/utils/error-helper.ts +1 -1
  52. package/src/utils/personalization-api-adapter.ts +32 -2
  53. package/src/utils/variant-api-adapter.ts +2 -2
@@ -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>;
@@ -51,13 +51,22 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
51
51
  branch: config.branchName,
52
52
  authtoken: config.auth_token,
53
53
  organization_uid: config.org_uid,
54
- 'X-Project-Uid': config.modules.personalization.project_id,
54
+ 'X-Project-Uid': config.modules.personalize.project_id,
55
55
  },
56
- };
56
+ };
57
57
  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));
58
+ this.entriesMapperPath = resolve(
59
+ sanitizePath(config.backupDir),
60
+ sanitizePath(config.branchName || ''),
61
+ 'mapper',
62
+ 'entries',
63
+ );
64
+ this.personalizeConfig = this.config.modules.personalize;
65
+ this.entriesDirPath = resolve(
66
+ sanitizePath(config.backupDir),
67
+ sanitizePath(config.branchName || ''),
68
+ sanitizePath(config.modules.entries.dirName),
69
+ );
61
70
  this.failedVariantPath = resolve(sanitizePath(this.entriesMapperPath), 'failed-entry-variants.json');
62
71
  this.failedVariantEntries = new Map();
63
72
  }
@@ -75,8 +84,8 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
75
84
  const variantIdPath = resolve(
76
85
  sanitizePath(this.config.backupDir),
77
86
  'mapper',
78
- sanitizePath(this.personalizationConfig.dirName),
79
- sanitizePath(this.personalizationConfig.experiences.dirName),
87
+ sanitizePath(this.personalizeConfig.dirName),
88
+ sanitizePath(this.personalizeConfig.experiences.dirName),
80
89
  'variants-uid-mapping.json',
81
90
  );
82
91
 
@@ -107,7 +116,12 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
107
116
  'terms',
108
117
  'success.json',
109
118
  );
110
- const marketplaceAppMapperPath = resolve(sanitizePath(this.config.backupDir), 'mapper', 'marketplace_apps', 'uid-mapping.json');
119
+ const marketplaceAppMapperPath = resolve(
120
+ sanitizePath(this.config.backupDir),
121
+ 'mapper',
122
+ 'marketplace_apps',
123
+ 'uid-mapping.json',
124
+ );
111
125
  const envPath = resolve(sanitizePath(this.config.backupDir), 'environments', 'environments.json');
112
126
  // NOTE Read and store list of variant IDs
113
127
  this.variantIdList = (fsUtil.readFile(variantIdPath, true) || {}) as Record<string, unknown>;
@@ -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
  );
@@ -380,23 +407,19 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
380
407
  async publishVariantEntries(batch: VariantEntryStruct[], entryUid: string, content_type: string) {
381
408
  const allPromise = [];
382
409
  for (let [, variantEntry] of entries(batch)) {
383
- const variantUid = variantEntry.uid;
384
- const oldVariantUid = variantEntry.variant_id || '';
410
+ const variantEntryUID = variantEntry.uid;
411
+ const oldVariantUid = variantEntry._variant._uid || '';
385
412
  const newVariantUid = this.variantIdList[oldVariantUid] as string;
386
413
 
387
414
  if (!newVariantUid) {
388
- log(
389
- this.config,
390
- `${this.messages.VARIANT_ID_NOT_FOUND}. Skipping entry variant publish for ${variantUid}`,
391
- 'info',
392
- );
415
+ log(this.config, `${this.messages.VARIANT_ID_NOT_FOUND}. Skipping entry variant publish`, 'info');
393
416
  continue;
394
417
  }
395
418
 
396
- if (this.failedVariantEntries.has(variantUid)) {
419
+ if (this.failedVariantEntries.has(variantEntryUID)) {
397
420
  log(
398
421
  this.config,
399
- `${this.messages.VARIANT_UID_NOT_FOUND}. Skipping entry variant publish for ${variantUid}`,
422
+ `${this.messages.VARIANT_UID_NOT_FOUND}. Skipping entry variant publish for ${variantEntryUID}`,
400
423
  'info',
401
424
  );
402
425
  continue;
@@ -440,7 +463,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
440
463
  reject: onReject.bind(this),
441
464
  resolve: onSuccess.bind(this),
442
465
  log: log,
443
- variantUid,
466
+ variantUid: newVariantUid,
444
467
  },
445
468
  );
446
469
 
@@ -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 with personalize background job! Failed to fetch some variant & 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 {
@@ -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
  };
@@ -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 = {};
@@ -22,5 +22,5 @@ function formatError(error: any): string {
22
22
  if (typeof error === 'object') {
23
23
  return Object.values(error).join(' ');
24
24
  }
25
- return String(error);
25
+ return error;
26
26
  }
@@ -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
 
@@ -77,6 +78,35 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
77
78
  return this.handleVariantAPIRes(data) as ExperienceStruct;
78
79
  }
79
80
 
81
+ async getExperienceVersions(experienceUid: string): Promise<ExperienceStruct | void> {
82
+ const getExperiencesVersionsEndPoint = `/experiences/${experienceUid}/versions`;
83
+ const data = await this.apiClient.get(getExperiencesVersionsEndPoint);
84
+ return this.handleVariantAPIRes(data) as ExperienceStruct;
85
+ }
86
+
87
+ async createExperienceVersion(
88
+ experienceUid: string,
89
+ input: CreateExperienceVersionInput,
90
+ ): Promise<ExperienceStruct | void> {
91
+ const createExperiencesVersionsEndPoint = `/experiences/${experienceUid}/versions`;
92
+ const data = await this.apiClient.post(createExperiencesVersionsEndPoint, input);
93
+ return this.handleVariantAPIRes(data) as ExperienceStruct;
94
+ }
95
+
96
+ async updateExperienceVersion(
97
+ experienceUid: string,
98
+ versionId: string,
99
+ input: CreateExperienceVersionInput,
100
+ ): Promise<ExperienceStruct | void> {
101
+ // loop through input and remove shortId from variant
102
+ if (input?.variants) {
103
+ input.variants = input.variants.map(({ shortUid, ...rest }) => rest);
104
+ }
105
+ const updateExperiencesVersionsEndPoint = `/experiences/${experienceUid}/versions/${versionId}`;
106
+ const data = await this.apiClient.put(updateExperiencesVersionsEndPoint, input);
107
+ return this.handleVariantAPIRes(data) as ExperienceStruct;
108
+ }
109
+
80
110
  async getVariantGroup(input: GetVariantGroupInput): Promise<VariantGroupStruct | void> {
81
111
  if (this.cmaAPIClient) {
82
112
  const getVariantGroupEndPoint = `/variant_groups`;
@@ -178,10 +208,10 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
178
208
  if (status >= 200 && status < 300) {
179
209
  return data;
180
210
  }
181
-
211
+
182
212
  const errorMsg = data?.errors
183
213
  ? formatErrors(data.errors)
184
- : data?.error_message || data?.message || 'Something went wrong while processing variant entries request!';
214
+ : data?.error || data?.error_message || data?.message || 'Something went wrong while processing variant entries request!';
185
215
 
186
216
  throw errorMsg;
187
217
  }
@@ -184,6 +184,7 @@ export class VariantHttpClient<C> extends AdapterHelper<C, HttpClient> implement
184
184
  });
185
185
 
186
186
  try {
187
+ this.apiClient.headers({ api_version: undefined });
187
188
  const res = await this.apiClient.put<VariantEntryStruct>(endpoint, { entry: input });
188
189
  const data = this.handleVariantAPIRes(res);
189
190
 
@@ -214,8 +215,7 @@ export class VariantHttpClient<C> extends AdapterHelper<C, HttpClient> implement
214
215
  const { entry_uid, content_type_uid } = options;
215
216
  let endpoint = `content_types/${content_type_uid}/entries/${entry_uid}/publish`;
216
217
 
217
- const onSuccess = (response: any) =>
218
- resolve({ response, apiData: { entryUid: entry_uid, variantUid }, log });
218
+ const onSuccess = (response: any) => resolve({ response, apiData: { entryUid: entry_uid, variantUid }, log });
219
219
  const onReject = (error: any) =>
220
220
  reject({
221
221
  error,