@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
@@ -1,24 +1,24 @@
1
1
  import * as path from 'path';
2
2
  import { sanitizePath } from '@contentstack/cli-utilities';
3
- import { ExportConfig, PersonalizationConfig } from '../types';
3
+ import { ExportConfig, PersonalizeConfig } from '../types';
4
4
  import { PersonalizationAdapter, log, fsUtil, formatError } from '../utils';
5
5
 
6
6
  export default class ExportProjects extends PersonalizationAdapter<ExportConfig> {
7
7
  private projectFolderPath: string;
8
8
  public exportConfig: ExportConfig;
9
- public personalizationConfig: PersonalizationConfig;
9
+ public personalizeConfig: PersonalizeConfig;
10
10
  constructor(exportConfig: ExportConfig) {
11
11
  super({
12
12
  config: exportConfig,
13
- baseURL: exportConfig.modules.personalization.baseURL[exportConfig.region.name],
14
- headers: { authtoken: exportConfig.auth_token, organization_uid: exportConfig.org_uid },
13
+ baseURL: exportConfig.modules.personalize.baseURL[exportConfig.region.name],
14
+ headers: { organization_uid: exportConfig.org_uid },
15
15
  });
16
16
  this.exportConfig = exportConfig;
17
- this.personalizationConfig = exportConfig.modules.personalization;
17
+ this.personalizeConfig = exportConfig.modules.personalize;
18
18
  this.projectFolderPath = path.resolve(
19
19
  sanitizePath(exportConfig.data),
20
20
  sanitizePath(exportConfig.branchName || ''),
21
- sanitizePath(this.personalizationConfig.dirName),
21
+ sanitizePath(this.personalizeConfig.dirName),
22
22
  'projects',
23
23
  );
24
24
  }
@@ -26,10 +26,11 @@ export default class ExportProjects extends PersonalizationAdapter<ExportConfig>
26
26
  async start() {
27
27
  try {
28
28
  log(this.exportConfig, 'Starting projects export', 'info');
29
+ await this.init();
29
30
  await fsUtil.makeDirectory(this.projectFolderPath);
30
31
  const project = await this.projects({ connectedStackApiKey: this.exportConfig.apiKey });
31
32
  if (!project || project?.length < 1) {
32
- log(this.exportConfig, 'No Personalization Project connected with the given stack', 'info');
33
+ log(this.exportConfig, 'No Personalize Project connected with the given stack', 'info');
33
34
  this.exportConfig.personalizationEnabled = false;
34
35
  return;
35
36
  }
@@ -38,7 +39,9 @@ export default class ExportProjects extends PersonalizationAdapter<ExportConfig>
38
39
  fsUtil.writeFile(path.resolve(sanitizePath(this.projectFolderPath), 'projects.json'), project);
39
40
  log(this.exportConfig, 'Project exported successfully!', 'success');
40
41
  } catch (error) {
41
- log(this.exportConfig, `Failed to export projects!`, 'error');
42
+ if (error !== 'Forbidden') {
43
+ log(this.exportConfig, `Failed to export projects!`, 'error');
44
+ }
42
45
  throw error;
43
46
  }
44
47
  }
@@ -19,7 +19,6 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Exp
19
19
  headers: {
20
20
  api_key: config.apiKey,
21
21
  branch: config.branchName,
22
- authtoken: config.auth_token,
23
22
  organization_uid: config.org_uid,
24
23
  'X-Project-Uid': config.project_id,
25
24
  },
@@ -36,7 +35,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Exp
36
35
  async exportVariantEntry(options: { locale: string; contentTypeUid: string; entries: Record<string, any>[] }) {
37
36
  const variantEntry = this.config.modules.variantEntry;
38
37
  const { entries, locale, contentTypeUid: content_type_uid } = options;
39
-
38
+ await this.variantInstance.init();
40
39
  for (let index = 0; index < entries.length; index++) {
41
40
  const entry = entries[index];
42
41
  const variantEntryBasePath = join(sanitizePath(this.entriesDirPath), sanitizePath(content_type_uid), sanitizePath(locale), sanitizePath(variantEntry.dirName), sanitizePath(entry.uid));
@@ -9,19 +9,23 @@ export default class Attribute extends PersonalizationAdapter<ImportConfig> {
9
9
  private attrMapperDirPath: string;
10
10
  private attributesUidMapperPath: string;
11
11
  private attributesUidMapper: Record<string, unknown>;
12
- private personalizationConfig: ImportConfig['modules']['personalization'];
13
- private attributeConfig: ImportConfig['modules']['personalization']['attributes'];
12
+ private personalizeConfig: ImportConfig['modules']['personalize'];
13
+ private attributeConfig: ImportConfig['modules']['personalize']['attributes'];
14
14
 
15
15
  constructor(public readonly config: ImportConfig, private readonly log: LogType = console.log) {
16
16
  const conf: APIConfig = {
17
17
  config,
18
- baseURL: config.modules.personalization.baseURL[config.region.name],
19
- headers: { 'X-Project-Uid': config.modules.personalization.project_id, authtoken: config.auth_token },
18
+ baseURL: config.modules.personalize.baseURL[config.region.name],
19
+ headers: { 'X-Project-Uid': config.modules.personalize.project_id },
20
20
  };
21
21
  super(Object.assign(config, conf));
22
- this.personalizationConfig = this.config.modules.personalization;
23
- this.attributeConfig = this.personalizationConfig.attributes;
24
- this.mapperDirPath = resolve(sanitizePath(this.config.backupDir), 'mapper', sanitizePath(this.personalizationConfig.dirName));
22
+ this.personalizeConfig = this.config.modules.personalize;
23
+ this.attributeConfig = this.personalizeConfig.attributes;
24
+ this.mapperDirPath = resolve(
25
+ sanitizePath(this.config.backupDir),
26
+ 'mapper',
27
+ sanitizePath(this.personalizeConfig.dirName),
28
+ );
25
29
  this.attrMapperDirPath = resolve(sanitizePath(this.mapperDirPath), sanitizePath(this.attributeConfig.dirName));
26
30
  this.attributesUidMapperPath = resolve(sanitizePath(this.attrMapperDirPath), 'uid-mapping.json');
27
31
  this.attributesUidMapper = {};
@@ -32,10 +36,15 @@ export default class Attribute extends PersonalizationAdapter<ImportConfig> {
32
36
  */
33
37
  async import() {
34
38
  this.log(this.config, this.$t(this.messages.IMPORT_MSG, { module: 'Attributes' }), 'info');
35
-
39
+ await this.init();
36
40
  await fsUtil.makeDirectory(this.attrMapperDirPath);
37
41
  const { dirName, fileName } = this.attributeConfig;
38
- const attributesPath = resolve(sanitizePath(this.config.data), sanitizePath(this.personalizationConfig.dirName), sanitizePath(dirName), sanitizePath(fileName));
42
+ const attributesPath = resolve(
43
+ sanitizePath(this.config.data),
44
+ sanitizePath(this.personalizeConfig.dirName),
45
+ sanitizePath(dirName),
46
+ sanitizePath(fileName),
47
+ );
39
48
 
40
49
  if (existsSync(attributesPath)) {
41
50
  try {
@@ -43,10 +52,19 @@ export default class Attribute extends PersonalizationAdapter<ImportConfig> {
43
52
 
44
53
  for (const attribute of attributes) {
45
54
  const { key, name, description, uid } = attribute;
46
- const attributeRes = await this.createAttribute({ key, name, description });
47
- //map old attribute uid to new attribute uid
48
- //mapper file is used to check whether attribute created or not before creating audience
49
- this.attributesUidMapper[uid] = attributeRes?.uid ?? '';
55
+ // skip creating preset attributes, as they are already present in the system
56
+ if (attribute.__type === 'PRESET') {
57
+ continue;
58
+ }
59
+ try {
60
+ const attributeRes = await this.createAttribute({ key, name, description });
61
+ //map old attribute uid to new attribute uid
62
+ //mapper file is used to check whether attribute created or not before creating audience
63
+ this.attributesUidMapper[uid] = attributeRes?.uid ?? '';
64
+ } catch (error) {
65
+ this.log(this.config, `Failed to create attribute ${name}!`, 'error');
66
+ this.log(this.config, error, 'error');
67
+ }
50
68
  }
51
69
 
52
70
  fsUtil.writeFile(this.attributesUidMapperPath, this.attributesUidMapper);
@@ -10,24 +10,32 @@ export default class Audiences extends PersonalizationAdapter<ImportConfig> {
10
10
  private attributesMapperPath: string;
11
11
  private audiencesUidMapperPath: string;
12
12
  private audiencesUidMapper: Record<string, unknown>;
13
- private personalizationConfig: ImportConfig['modules']['personalization'];
14
- private audienceConfig: ImportConfig['modules']['personalization']['audiences'];
15
- public attributeConfig: ImportConfig['modules']['personalization']['attributes'];
13
+ private personalizeConfig: ImportConfig['modules']['personalize'];
14
+ private audienceConfig: ImportConfig['modules']['personalize']['audiences'];
15
+ public attributeConfig: ImportConfig['modules']['personalize']['attributes'];
16
16
 
17
17
  constructor(public readonly config: ImportConfig, private readonly log: LogType = console.log) {
18
18
  const conf: APIConfig = {
19
19
  config,
20
- baseURL: config.modules.personalization.baseURL[config.region.name],
21
- headers: { 'X-Project-Uid': config.modules.personalization.project_id, authtoken: config.auth_token },
20
+ baseURL: config.modules.personalize.baseURL[config.region.name],
21
+ headers: { 'X-Project-Uid': config.modules.personalize.project_id },
22
22
  };
23
23
  super(Object.assign(config, conf));
24
- this.personalizationConfig = this.config.modules.personalization;
25
- this.audienceConfig = this.personalizationConfig.audiences;
26
- this.attributeConfig = this.personalizationConfig.attributes;
27
- this.mapperDirPath = resolve(sanitizePath(this.config.backupDir), 'mapper', sanitizePath(this.personalizationConfig.dirName));
24
+ this.personalizeConfig = this.config.modules.personalize;
25
+ this.audienceConfig = this.personalizeConfig.audiences;
26
+ this.attributeConfig = this.personalizeConfig.attributes;
27
+ this.mapperDirPath = resolve(
28
+ sanitizePath(this.config.backupDir),
29
+ 'mapper',
30
+ sanitizePath(this.personalizeConfig.dirName),
31
+ );
28
32
  this.audienceMapperDirPath = resolve(sanitizePath(this.mapperDirPath), sanitizePath(this.audienceConfig.dirName));
29
33
  this.audiencesUidMapperPath = resolve(sanitizePath(this.audienceMapperDirPath), 'uid-mapping.json');
30
- this.attributesMapperPath = resolve(sanitizePath(this.mapperDirPath), sanitizePath(this.attributeConfig.dirName), 'uid-mapping.json');
34
+ this.attributesMapperPath = resolve(
35
+ sanitizePath(this.mapperDirPath),
36
+ sanitizePath(this.attributeConfig.dirName),
37
+ 'uid-mapping.json',
38
+ );
31
39
  this.audiencesUidMapper = {};
32
40
  }
33
41
 
@@ -36,10 +44,15 @@ export default class Audiences extends PersonalizationAdapter<ImportConfig> {
36
44
  */
37
45
  async import() {
38
46
  this.log(this.config, this.$t(this.messages.IMPORT_MSG, { module: 'Audiences' }), 'info');
39
-
47
+ await this.init();
40
48
  await fsUtil.makeDirectory(this.audienceMapperDirPath);
41
49
  const { dirName, fileName } = this.audienceConfig;
42
- const audiencesPath = resolve(sanitizePath(this.config.data), sanitizePath(this.personalizationConfig.dirName), sanitizePath(dirName), sanitizePath(fileName));
50
+ const audiencesPath = resolve(
51
+ sanitizePath(this.config.data),
52
+ sanitizePath(this.personalizeConfig.dirName),
53
+ sanitizePath(dirName),
54
+ sanitizePath(fileName),
55
+ );
43
56
 
44
57
  if (existsSync(audiencesPath)) {
45
58
  try {
@@ -48,14 +61,19 @@ export default class Audiences extends PersonalizationAdapter<ImportConfig> {
48
61
 
49
62
  for (const audience of audiences) {
50
63
  let { name, definition, description, uid } = audience;
51
- //check whether reference attributes exists or not
52
- if (definition.rules?.length) {
53
- definition.rules = lookUpAttributes(definition.rules, attributesUid);
64
+ try {
65
+ //check whether reference attributes exists or not
66
+ if (definition.rules?.length) {
67
+ definition.rules = lookUpAttributes(definition.rules, attributesUid);
68
+ }
69
+ const audienceRes = await this.createAudience({ definition, name, description });
70
+ //map old audience uid to new audience uid
71
+ //mapper file is used to check whether audience created or not before creating experience
72
+ this.audiencesUidMapper[uid] = audienceRes?.uid ?? '';
73
+ } catch (error) {
74
+ this.log(this.config, `Failed to create audience uid: ${uid}, name: ${name}`, 'error');
75
+ this.log(this.config, error, 'error');
54
76
  }
55
- const audienceRes = await this.createAudience({ definition, name, description });
56
- //map old audience uid to new audience uid
57
- //mapper file is used to check whether audience created or not before creating experience
58
- this.audiencesUidMapper[uid] = audienceRes?.uid ?? '';
59
77
  }
60
78
 
61
79
  fsUtil.writeFile(this.audiencesUidMapperPath, this.audiencesUidMapper);
@@ -9,19 +9,23 @@ export default class Events extends PersonalizationAdapter<ImportConfig> {
9
9
  private eventMapperDirPath: string;
10
10
  private eventsUidMapperPath: string;
11
11
  private eventsUidMapper: Record<string, unknown>;
12
- private personalizationConfig: ImportConfig['modules']['personalization'];
13
- private eventsConfig: ImportConfig['modules']['personalization']['events'];
12
+ private personalizeConfig: ImportConfig['modules']['personalize'];
13
+ private eventsConfig: ImportConfig['modules']['personalize']['events'];
14
14
 
15
15
  constructor(public readonly config: ImportConfig, private readonly log: LogType = console.log) {
16
16
  const conf: APIConfig = {
17
17
  config,
18
- baseURL: config.modules.personalization.baseURL[config.region.name],
19
- headers: { 'X-Project-Uid': config.modules.personalization.project_id, authtoken: config.auth_token },
18
+ baseURL: config.modules.personalize.baseURL[config.region.name],
19
+ headers: { 'X-Project-Uid': config.modules.personalize.project_id },
20
20
  };
21
21
  super(Object.assign(config, conf));
22
- this.personalizationConfig = this.config.modules.personalization;
23
- this.eventsConfig = this.personalizationConfig.events;
24
- this.mapperDirPath = resolve(sanitizePath(this.config.backupDir), 'mapper', sanitizePath(this.personalizationConfig.dirName));
22
+ this.personalizeConfig = this.config.modules.personalize;
23
+ this.eventsConfig = this.personalizeConfig.events;
24
+ this.mapperDirPath = resolve(
25
+ sanitizePath(this.config.backupDir),
26
+ 'mapper',
27
+ sanitizePath(this.personalizeConfig.dirName),
28
+ );
25
29
  this.eventMapperDirPath = resolve(sanitizePath(this.mapperDirPath), sanitizePath(this.eventsConfig.dirName));
26
30
  this.eventsUidMapperPath = resolve(sanitizePath(this.eventMapperDirPath), 'uid-mapping.json');
27
31
  this.eventsUidMapper = {};
@@ -32,10 +36,15 @@ export default class Events extends PersonalizationAdapter<ImportConfig> {
32
36
  */
33
37
  async import() {
34
38
  this.log(this.config, this.$t(this.messages.IMPORT_MSG, { module: 'Events' }), 'info');
35
-
39
+ await this.init();
36
40
  await fsUtil.makeDirectory(this.eventMapperDirPath);
37
41
  const { dirName, fileName } = this.eventsConfig;
38
- const eventsPath = resolve(sanitizePath(this.config.data), sanitizePath(this.personalizationConfig.dirName), sanitizePath(dirName), sanitizePath(fileName));
42
+ const eventsPath = resolve(
43
+ sanitizePath(this.config.data),
44
+ sanitizePath(this.personalizeConfig.dirName),
45
+ sanitizePath(dirName),
46
+ sanitizePath(fileName),
47
+ );
39
48
 
40
49
  if (existsSync(eventsPath)) {
41
50
  try {
@@ -43,8 +52,13 @@ export default class Events extends PersonalizationAdapter<ImportConfig> {
43
52
 
44
53
  for (const event of events) {
45
54
  const { key, description, uid } = event;
46
- const eventsResponse = await this.createEvents({ key, description });
47
- this.eventsUidMapper[uid] = eventsResponse?.uid ?? '';
55
+ try {
56
+ const eventsResponse = await this.createEvents({ key, description });
57
+ this.eventsUidMapper[uid] = eventsResponse?.uid ?? '';
58
+ } catch (error) {
59
+ this.log(this.config, `failed to create event uid: ${uid}`, 'error');
60
+ this.log(this.config, error, 'error');
61
+ }
48
62
  }
49
63
 
50
64
  fsUtil.writeFile(this.eventsUidMapperPath, this.eventsUidMapper);
@@ -4,8 +4,14 @@ import values from 'lodash/values';
4
4
  import cloneDeep from 'lodash/cloneDeep';
5
5
  import { sanitizePath } from '@contentstack/cli-utilities';
6
6
  import { PersonalizationAdapter, fsUtil, lookUpAudiences, lookUpEvents } from '../utils';
7
- import { APIConfig, ImportConfig, ExperienceStruct, CreateExperienceInput, LogType } from '../types';
8
-
7
+ import {
8
+ APIConfig,
9
+ ImportConfig,
10
+ ExperienceStruct,
11
+ CreateExperienceInput,
12
+ LogType,
13
+ CreateExperienceVersionInput,
14
+ } from '../types';
9
15
  export default class Experiences extends PersonalizationAdapter<ImportConfig> {
10
16
  private createdCTs: string[];
11
17
  private mapperDirPath: string;
@@ -29,43 +35,56 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
29
35
  private cmsVariantGroups: Record<string, unknown>;
30
36
  private experiencesUidMapper: Record<string, string>;
31
37
  private pendingVariantAndVariantGrpForExperience: string[];
32
- private personalizationConfig: ImportConfig['modules']['personalization'];
33
- private audienceConfig: ImportConfig['modules']['personalization']['audiences'];
34
- private experienceConfig: ImportConfig['modules']['personalization']['experiences'];
38
+ private audiencesUid: Record<string, string>;
39
+ private eventsUid: Record<string, string>;
40
+ private personalizeConfig: ImportConfig['modules']['personalize'];
41
+ private audienceConfig: ImportConfig['modules']['personalize']['audiences'];
42
+ private experienceConfig: ImportConfig['modules']['personalize']['experiences'];
35
43
 
36
44
  constructor(public readonly config: ImportConfig, private readonly log: LogType = console.log) {
37
45
  const conf: APIConfig = {
38
46
  config,
39
- baseURL: config.modules.personalization.baseURL[config.region.name],
40
- headers: { 'X-Project-Uid': config.modules.personalization.project_id, authtoken: config.auth_token },
47
+ baseURL: config.modules.personalize.baseURL[config.region.name],
48
+ headers: { 'X-Project-Uid': config.modules.personalize.project_id},
41
49
  cmaConfig: {
42
50
  baseURL: config.region.cma + `/v3`,
43
- headers: { authtoken: config.auth_token, api_key: config.apiKey },
51
+ headers: { api_key: config.apiKey },
44
52
  },
45
53
  };
46
54
  super(Object.assign(config, conf));
47
- this.personalizationConfig = this.config.modules.personalization;
55
+ this.personalizeConfig = this.config.modules.personalize;
48
56
  this.experiencesDirPath = resolve(
49
57
  sanitizePath(this.config.data),
50
- sanitizePath(this.personalizationConfig.dirName),
51
- sanitizePath(this.personalizationConfig.experiences.dirName),
58
+ sanitizePath(this.personalizeConfig.dirName),
59
+ sanitizePath(this.personalizeConfig.experiences.dirName),
60
+ );
61
+ this.experiencesPath = join(
62
+ sanitizePath(this.experiencesDirPath),
63
+ sanitizePath(this.personalizeConfig.experiences.fileName),
64
+ );
65
+ this.experienceConfig = this.personalizeConfig.experiences;
66
+ this.audienceConfig = this.personalizeConfig.audiences;
67
+ this.mapperDirPath = resolve(
68
+ sanitizePath(this.config.backupDir),
69
+ 'mapper',
70
+ sanitizePath(this.personalizeConfig.dirName),
52
71
  );
53
- this.experiencesPath = join(sanitizePath(this.experiencesDirPath), sanitizePath(this.personalizationConfig.experiences.fileName));
54
- this.experienceConfig = this.personalizationConfig.experiences;
55
- this.audienceConfig = this.personalizationConfig.audiences;
56
- this.mapperDirPath = resolve(sanitizePath(this.config.backupDir), 'mapper', sanitizePath(this.personalizationConfig.dirName));
57
72
  this.expMapperDirPath = resolve(sanitizePath(this.mapperDirPath), sanitizePath(this.experienceConfig.dirName));
58
73
  this.experiencesUidMapperPath = resolve(sanitizePath(this.expMapperDirPath), 'uid-mapping.json');
59
74
  this.cmsVariantGroupPath = resolve(sanitizePath(this.expMapperDirPath), 'cms-variant-groups.json');
60
75
  this.cmsVariantPath = resolve(sanitizePath(this.expMapperDirPath), 'cms-variants.json');
61
- this.audiencesMapperPath = resolve(sanitizePath(this.mapperDirPath), sanitizePath(this.audienceConfig.dirName), 'uid-mapping.json');
76
+ this.audiencesMapperPath = resolve(
77
+ sanitizePath(this.mapperDirPath),
78
+ sanitizePath(this.audienceConfig.dirName),
79
+ 'uid-mapping.json',
80
+ );
62
81
  this.eventsMapperPath = resolve(sanitizePath(this.mapperDirPath), 'events', 'uid-mapping.json');
63
82
  this.failedCmsExpPath = resolve(sanitizePath(this.expMapperDirPath), 'failed-cms-experience.json');
64
83
  this.failedCmsExpPath = resolve(sanitizePath(this.expMapperDirPath), 'failed-cms-experience.json');
65
84
  this.experienceCTsPath = resolve(sanitizePath(this.experiencesDirPath), 'experiences-content-types.json');
66
85
  this.experienceVariantsIdsPath = resolve(
67
86
  sanitizePath(this.config.data),
68
- sanitizePath(this.personalizationConfig.dirName),
87
+ sanitizePath(this.personalizeConfig.dirName),
69
88
  sanitizePath(this.experienceConfig.dirName),
70
89
  'experiences-variants-ids.json',
71
90
  );
@@ -79,6 +98,8 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
79
98
  this.pendingVariantAndVariantGrpForExperience = [];
80
99
  this.cTsSuccessPath = resolve(sanitizePath(this.config.backupDir), 'mapper', 'content_types', 'success.json');
81
100
  this.createdCTs = [];
101
+ this.audiencesUid = (fsUtil.readFile(this.audiencesMapperPath, true) as Record<string, string>) || {};
102
+ this.eventsUid = (fsUtil.readFile(this.eventsMapperPath, true) as Record<string, string>) || {};
82
103
  }
83
104
 
84
105
  /**
@@ -86,25 +107,31 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
86
107
  */
87
108
  async import() {
88
109
  this.log(this.config, this.$t(this.messages.IMPORT_MSG, { module: 'Experiences' }), 'info');
89
-
110
+ await this.init();
90
111
  await fsUtil.makeDirectory(this.expMapperDirPath);
91
112
 
92
113
  if (existsSync(this.experiencesPath)) {
93
114
  try {
94
115
  const experiences = fsUtil.readFile(this.experiencesPath, true) as ExperienceStruct[];
95
- const audiencesUid = (fsUtil.readFile(this.audiencesMapperPath, true) as Record<string, string>) || {};
96
- const eventsUid = (fsUtil.readFile(this.eventsMapperPath, true) as Record<string, string>) || {};
97
116
 
98
117
  for (const experience of experiences) {
99
118
  const { uid, ...restExperienceData } = experience;
100
119
  //check whether reference audience exists or not that referenced in variations having __type equal to AudienceBasedVariation & targeting
101
- let experienceReqObj: CreateExperienceInput = lookUpAudiences(restExperienceData, audiencesUid);
120
+ let experienceReqObj: CreateExperienceInput = lookUpAudiences(restExperienceData, this.audiencesUid);
102
121
  //check whether events exists or not that referenced in metrics
103
- experienceReqObj = lookUpEvents(experienceReqObj, eventsUid);
122
+ experienceReqObj = lookUpEvents(experienceReqObj, this.eventsUid);
104
123
 
105
- const expRes = await this.createExperience(experienceReqObj);
124
+ const expRes = (await this.createExperience(experienceReqObj)) as ExperienceStruct;
106
125
  //map old experience uid to new experience uid
107
126
  this.experiencesUidMapper[uid] = expRes?.uid ?? '';
127
+
128
+ try {
129
+ // import versions of experience
130
+ await this.importExperienceVersions(expRes, uid);
131
+ } catch (error) {
132
+ this.log(this.config, `Error while importing experience versions of ${expRes.uid}`, 'error');
133
+ this.log(this.config, error, 'error');
134
+ }
108
135
  }
109
136
  fsUtil.writeFile(this.experiencesUidMapperPath, this.experiencesUidMapper);
110
137
  this.log(this.config, this.$t(this.messages.CREATE_SUCCESS, { module: 'Experiences' }), 'info');
@@ -117,9 +144,10 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
117
144
  if (jobRes)
118
145
  this.log(this.config, this.$t(this.messages.CREATE_SUCCESS, { module: 'Variant & Variant groups' }), 'info');
119
146
 
120
- if (this.personalizationConfig.importData) {
147
+ if (this.personalizeConfig.importData) {
121
148
  this.log(this.config, this.messages.UPDATING_CT_IN_EXP, 'info');
122
149
  await this.attachCTsInExperience();
150
+ this.log(this.config, this.messages.UPDATED_CT_IN_EXP, 'info');
123
151
  }
124
152
 
125
153
  await this.createVariantIdMapper();
@@ -131,9 +159,71 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
131
159
  }
132
160
 
133
161
  /**
134
- * function to validate if all variant groups and variants have been created using personalization background job
135
- * store the variant groups data in mapper/personalization/experiences/cms-variant-groups.json and the variants data
136
- * in mapper/personalization/experiences/cms-variants.json. If not, invoke validateVariantGroupAndVariantsCreated after some delay.
162
+ * function import experience versions from a JSON file and creates them in the project.
163
+ */
164
+ async importExperienceVersions(experience: ExperienceStruct, oldExperienceUid: string) {
165
+ const versionsPath = resolve(sanitizePath(this.experiencesDirPath), 'versions', `${sanitizePath(oldExperienceUid)}.json`);
166
+
167
+ if (!existsSync(versionsPath)) {
168
+ return;
169
+ }
170
+
171
+ const versions = fsUtil.readFile(versionsPath, true) as ExperienceStruct[];
172
+ const versionMap: Record<string, CreateExperienceVersionInput | undefined> = {
173
+ ACTIVE: undefined,
174
+ DRAFT: undefined,
175
+ PAUSE: undefined,
176
+ };
177
+
178
+ // Process each version and map them by status
179
+ versions.forEach((version) => {
180
+ let versionReqObj = lookUpAudiences(version, this.audiencesUid) as CreateExperienceVersionInput;
181
+ versionReqObj = lookUpEvents(version, this.eventsUid) as CreateExperienceVersionInput;
182
+
183
+ if (versionReqObj && versionReqObj.status) {
184
+ versionMap[versionReqObj.status] = versionReqObj;
185
+ }
186
+ });
187
+
188
+ // Prioritize updating or creating versions based on the order: ACTIVE -> DRAFT -> PAUSE
189
+ return await this.handleVersionUpdateOrCreate(experience, versionMap);
190
+ }
191
+
192
+ // Helper method to handle version update or creation logic
193
+ private async handleVersionUpdateOrCreate(
194
+ experience: ExperienceStruct,
195
+ versionMap: Record<string, CreateExperienceVersionInput | undefined>,
196
+ ) {
197
+ const { ACTIVE, DRAFT, PAUSE } = versionMap;
198
+ let latestVersionUsed = false;
199
+
200
+ if (ACTIVE) {
201
+ await this.updateExperienceVersion(experience.uid, experience.latestVersion, ACTIVE);
202
+ latestVersionUsed = true;
203
+ }
204
+
205
+ if (DRAFT) {
206
+ if (latestVersionUsed) {
207
+ await this.createExperienceVersion(experience.uid, DRAFT);
208
+ } else {
209
+ await this.updateExperienceVersion(experience.uid, experience.latestVersion, DRAFT);
210
+ latestVersionUsed = true;
211
+ }
212
+ }
213
+
214
+ if (PAUSE) {
215
+ if (latestVersionUsed) {
216
+ await this.createExperienceVersion(experience.uid, PAUSE);
217
+ } else {
218
+ await this.updateExperienceVersion(experience.uid, experience.latestVersion, PAUSE);
219
+ }
220
+ }
221
+ }
222
+
223
+ /**
224
+ * function to validate if all variant groups and variants have been created using personalize background job
225
+ * store the variant groups data in mapper/personalize/experiences/cms-variant-groups.json and the variants data
226
+ * in mapper/personalize/experiences/cms-variants.json. If not, invoke validateVariantGroupAndVariantsCreated after some delay.
137
227
  * @param retryCount Counter to track the number of times the function has been called
138
228
  * @returns
139
229
  */
@@ -160,7 +250,7 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
160
250
  );
161
251
  return this.validateVariantGroupAndVariantsCreated(retryCount);
162
252
  } else {
163
- this.log(this.config, this.messages.PERSONALIZATION_JOB_FAILURE, 'error');
253
+ this.log(this.config, this.messages.PERSONALIZE_JOB_FAILURE, 'error');
164
254
  fsUtil.writeFile(this.failedCmsExpPath, this.pendingVariantAndVariantGrpForExperience);
165
255
  return false;
166
256
  }
@@ -185,8 +275,8 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
185
275
  Object.entries(this.experiencesUidMapper).map(async ([oldExpUid, newExpUid]) => {
186
276
  if (experienceCTsMap[oldExpUid]?.length) {
187
277
  // Filter content types that were created
188
- const updatedContentTypes = experienceCTsMap[oldExpUid].filter((ct: any) =>
189
- this.createdCTs.includes(ct?.uid),
278
+ const updatedContentTypes = experienceCTsMap[oldExpUid].filter(
279
+ (ct: any) => this.createdCTs.includes(ct?.uid) && ct.status === 'linked',
190
280
  );
191
281
  if (updatedContentTypes?.length) {
192
282
  const { variant_groups: [variantGroup] = [] } =
@@ -9,14 +9,14 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
9
9
  constructor(public readonly config: ImportConfig, private readonly log: LogType = console.log) {
10
10
  const conf: APIConfig = {
11
11
  config,
12
- baseURL: config.modules.personalization.baseURL[config.region.name],
13
- headers: { organization_uid: config.org_uid, authtoken: config.auth_token },
12
+ baseURL: config.modules.personalize.baseURL[config.region.name],
13
+ headers: { organization_uid: config.org_uid },
14
14
  };
15
15
  super(Object.assign(config, conf));
16
16
  this.projectMapperFolderPath = pResolve(
17
17
  sanitizePath(this.config.backupDir),
18
18
  'mapper',
19
- sanitizePath(this.config.modules.personalization.dirName),
19
+ sanitizePath(this.config.modules.personalize.dirName),
20
20
  'projects',
21
21
  );
22
22
  }
@@ -26,19 +26,19 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
26
26
  * data.
27
27
  */
28
28
  async import() {
29
- const personalization = this.config.modules.personalization;
30
- const { dirName, fileName } = personalization.projects;
31
- const projectPath = join(sanitizePath(this.config.data), sanitizePath(personalization.dirName), sanitizePath(dirName), sanitizePath(fileName));
32
-
29
+ const personalize = this.config.modules.personalize;
30
+ const { dirName, fileName } = personalize.projects;
31
+ const projectPath = join(sanitizePath(this.config.data), sanitizePath(personalize.dirName), sanitizePath(dirName), sanitizePath(fileName));
32
+
33
33
  if (existsSync(projectPath)) {
34
34
  const projects = JSON.parse(readFileSync(projectPath, 'utf8')) as CreateProjectInput[];
35
35
 
36
36
  if (!projects || projects.length < 1) {
37
- this.config.modules.personalization.importData = false; // Stop personalization import if stack not connected to any project
37
+ this.config.modules.personalize.importData = false; // Stop personalize import if stack not connected to any project
38
38
  this.log(this.config, 'No project found!', 'info');
39
39
  return;
40
40
  }
41
-
41
+ await this.init();
42
42
  for (const project of projects) {
43
43
  const createProject = async (newName: void | string): Promise<ProjectStruct> => {
44
44
  return await this.createProject({
@@ -46,7 +46,7 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
46
46
  description: project.description,
47
47
  connectedStackApiKey: this.config.apiKey,
48
48
  }).catch(async (error) => {
49
- if (error === 'personalization.PROJECTS.DUPLICATE_NAME') {
49
+ if (error.includes('personalization.PROJECTS.DUPLICATE_NAME') || error.includes('personalize.PROJECTS.DUPLICATE_NAME')) {
50
50
  const projectName = await askProjectName('Copy Of ' + (newName || project.name));
51
51
  return await createProject(projectName);
52
52
  }
@@ -56,15 +56,15 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
56
56
  };
57
57
 
58
58
  const projectRes = await createProject(this.config.personalizeProjectName);
59
- this.config.modules.personalization.project_id = projectRes.uid;
60
- this.config.modules.personalization.importData = true;
59
+ this.config.modules.personalize.project_id = projectRes.uid;
60
+ this.config.modules.personalize.importData = true;
61
61
 
62
62
  await fsUtil.makeDirectory(this.projectMapperFolderPath);
63
63
  fsUtil.writeFile(pResolve(sanitizePath(this.projectMapperFolderPath), 'projects.json'), projectRes);
64
64
  this.log(this.config, `Project Created Successfully: ${projectRes.uid}`, 'info');
65
65
  }
66
66
  } else {
67
- this.config.modules.personalization.importData = false; // Stop personalization import if stack not connected to any project
67
+ this.config.modules.personalize.importData = false; // Stop personalize import if stack not connected to any project
68
68
  this.log(this.config, 'No project found!', 'info');
69
69
  }
70
70
  }