@contentstack/cli-variants 1.2.1 → 1.3.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 (46) hide show
  1. package/lib/export/attributes.js +27 -10
  2. package/lib/export/audiences.js +28 -10
  3. package/lib/export/events.js +28 -10
  4. package/lib/export/experiences.js +48 -13
  5. package/lib/export/projects.js +24 -6
  6. package/lib/export/variant-entries.js +25 -4
  7. package/lib/import/attribute.d.ts +2 -3
  8. package/lib/import/attribute.js +16 -8
  9. package/lib/import/audiences.d.ts +2 -3
  10. package/lib/import/audiences.js +21 -8
  11. package/lib/import/events.d.ts +3 -4
  12. package/lib/import/events.js +16 -9
  13. package/lib/import/experiences.d.ts +2 -3
  14. package/lib/import/experiences.js +60 -17
  15. package/lib/import/project.d.ts +2 -3
  16. package/lib/import/project.js +11 -6
  17. package/lib/import/variant-entries.js +62 -25
  18. package/lib/types/export-config.d.ts +2 -1
  19. package/lib/types/utils.d.ts +11 -0
  20. package/lib/utils/attributes-helper.js +17 -1
  21. package/lib/utils/audiences-helper.js +37 -6
  22. package/lib/utils/events-helper.js +17 -4
  23. package/lib/utils/personalization-api-adapter.d.ts +2 -1
  24. package/lib/utils/personalization-api-adapter.js +119 -27
  25. package/lib/utils/variant-api-adapter.d.ts +4 -1
  26. package/lib/utils/variant-api-adapter.js +91 -17
  27. package/package.json +8 -5
  28. package/src/export/attributes.ts +34 -10
  29. package/src/export/audiences.ts +35 -7
  30. package/src/export/events.ts +35 -7
  31. package/src/export/experiences.ts +74 -24
  32. package/src/export/projects.ts +31 -7
  33. package/src/export/variant-entries.ts +47 -12
  34. package/src/import/attribute.ts +22 -9
  35. package/src/import/audiences.ts +28 -10
  36. package/src/import/events.ts +21 -10
  37. package/src/import/experiences.ts +74 -20
  38. package/src/import/project.ts +22 -8
  39. package/src/import/variant-entries.ts +116 -40
  40. package/src/types/export-config.ts +2 -1
  41. package/src/types/utils.ts +12 -0
  42. package/src/utils/attributes-helper.ts +21 -2
  43. package/src/utils/audiences-helper.ts +41 -1
  44. package/src/utils/events-helper.ts +19 -1
  45. package/src/utils/personalization-api-adapter.ts +95 -19
  46. package/src/utils/variant-api-adapter.ts +79 -8
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'path';
2
2
  import { existsSync } from 'fs';
3
- import { sanitizePath } from '@contentstack/cli-utilities';
3
+ import { sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
4
4
  import { PersonalizationAdapter, fsUtil } from '../utils';
5
5
  import { APIConfig, AttributeStruct, ImportConfig, LogType } from '../types';
6
6
 
@@ -12,13 +12,14 @@ export default class Attribute extends PersonalizationAdapter<ImportConfig> {
12
12
  private personalizeConfig: ImportConfig['modules']['personalize'];
13
13
  private attributeConfig: ImportConfig['modules']['personalize']['attributes'];
14
14
 
15
- constructor(public readonly config: ImportConfig, private readonly log: LogType = console.log) {
15
+ constructor(public readonly config: ImportConfig) {
16
16
  const conf: APIConfig = {
17
17
  config,
18
18
  baseURL: config.modules.personalize.baseURL[config.region.name],
19
19
  headers: { 'X-Project-Uid': config.modules.personalize.project_id },
20
20
  };
21
21
  super(Object.assign(config, conf));
22
+
22
23
  this.personalizeConfig = this.config.modules.personalize;
23
24
  this.attributeConfig = this.personalizeConfig.attributes;
24
25
  this.mapperDirPath = resolve(
@@ -29,15 +30,17 @@ export default class Attribute extends PersonalizationAdapter<ImportConfig> {
29
30
  this.attrMapperDirPath = resolve(sanitizePath(this.mapperDirPath), sanitizePath(this.attributeConfig.dirName));
30
31
  this.attributesUidMapperPath = resolve(sanitizePath(this.attrMapperDirPath), 'uid-mapping.json');
31
32
  this.attributesUidMapper = {};
33
+ this.config.context.module = 'attributes';
32
34
  }
33
35
 
34
36
  /**
35
37
  * The function asynchronously imports attributes from a JSON file and creates them in the system.
36
38
  */
37
- async import() {
38
- this.log(this.config, this.$t(this.messages.IMPORT_MSG, { module: 'Attributes' }), 'info');
39
+ async import() {
39
40
  await this.init();
40
41
  await fsUtil.makeDirectory(this.attrMapperDirPath);
42
+ log.debug(`Created mapper directory: ${this.attrMapperDirPath}`, this.config.context);
43
+
41
44
  const { dirName, fileName } = this.attributeConfig;
42
45
  const attributesPath = resolve(
43
46
  sanitizePath(this.config.data),
@@ -46,33 +49,43 @@ export default class Attribute extends PersonalizationAdapter<ImportConfig> {
46
49
  sanitizePath(fileName),
47
50
  );
48
51
 
52
+ log.debug(`Checking for attributes file: ${attributesPath}`, this.config.context);
53
+
49
54
  if (existsSync(attributesPath)) {
50
55
  try {
51
56
  const attributes = fsUtil.readFile(attributesPath, true) as AttributeStruct[];
57
+ log.info(`Found ${attributes.length} attributes to import`, this.config.context);
52
58
 
53
59
  for (const attribute of attributes) {
54
60
  const { key, name, description, uid } = attribute;
61
+ log.debug(`Processing attribute: ${name} - ${attribute.__type}`, this.config.context);
62
+
55
63
  // skip creating preset attributes, as they are already present in the system
56
64
  if (attribute.__type === 'PRESET') {
65
+ log.debug(`Skipping preset attribute: ${name}`, this.config.context);
57
66
  continue;
58
67
  }
68
+
59
69
  try {
70
+ log.debug(`Creating custom attribute: ${name}`, this.config.context);
60
71
  const attributeRes = await this.createAttribute({ key, name, description });
61
72
  //map old attribute uid to new attribute uid
62
73
  //mapper file is used to check whether attribute created or not before creating audience
63
74
  this.attributesUidMapper[uid] = attributeRes?.uid ?? '';
75
+ log.debug(`Created attribute: ${uid} -> ${attributeRes?.uid}`, this.config.context);
64
76
  } catch (error) {
65
- this.log(this.config, `Failed to create attribute ${name}!`, 'error');
66
- this.log(this.config, error, 'error');
77
+ handleAndLogError(error, this.config.context, `Failed to create attribute: ${name}`);
67
78
  }
68
79
  }
69
80
 
70
81
  fsUtil.writeFile(this.attributesUidMapperPath, this.attributesUidMapper);
71
- this.log(this.config, this.$t(this.messages.CREATE_SUCCESS, { module: 'Attributes' }), 'info');
82
+ log.debug(`Saved ${Object.keys(this.attributesUidMapper).length} attribute mappings to: ${this.attributesUidMapperPath}`, this.config.context);
83
+ log.success('Attributes imported successfully', this.config.context);
72
84
  } catch (error) {
73
- this.log(this.config, this.$t(this.messages.CREATE_FAILURE, { module: 'Attributes' }), 'error');
74
- this.log(this.config, error, 'error');
85
+ handleAndLogError(error, this.config.context);
75
86
  }
87
+ } else {
88
+ log.warn(`Attributes file not found: ${attributesPath}`, this.config.context);
76
89
  }
77
90
  }
78
91
  }
@@ -1,7 +1,7 @@
1
1
  import { resolve } from 'path';
2
2
  import { existsSync } from 'fs';
3
- import { sanitizePath } from '@contentstack/cli-utilities';
4
- import { APIConfig, AudienceStruct, ImportConfig, LogType } from '../types';
3
+ import { sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
4
+ import { APIConfig, AudienceStruct, ImportConfig } from '../types';
5
5
  import { PersonalizationAdapter, fsUtil, lookUpAttributes } from '../utils';
6
6
 
7
7
  export default class Audiences extends PersonalizationAdapter<ImportConfig> {
@@ -14,13 +14,14 @@ export default class Audiences extends PersonalizationAdapter<ImportConfig> {
14
14
  private audienceConfig: ImportConfig['modules']['personalize']['audiences'];
15
15
  public attributeConfig: ImportConfig['modules']['personalize']['attributes'];
16
16
 
17
- constructor(public readonly config: ImportConfig, private readonly log: LogType = console.log) {
17
+ constructor(public readonly config: ImportConfig ) {
18
18
  const conf: APIConfig = {
19
19
  config,
20
20
  baseURL: config.modules.personalize.baseURL[config.region.name],
21
21
  headers: { 'X-Project-Uid': config.modules.personalize.project_id },
22
22
  };
23
23
  super(Object.assign(config, conf));
24
+
24
25
  this.personalizeConfig = this.config.modules.personalize;
25
26
  this.audienceConfig = this.personalizeConfig.audiences;
26
27
  this.attributeConfig = this.personalizeConfig.attributes;
@@ -37,15 +38,17 @@ export default class Audiences extends PersonalizationAdapter<ImportConfig> {
37
38
  'uid-mapping.json',
38
39
  );
39
40
  this.audiencesUidMapper = {};
41
+ this.config.context.module = 'audiences';
40
42
  }
41
43
 
42
44
  /**
43
45
  * The function asynchronously imports audiences from a JSON file and creates them in the system.
44
46
  */
45
- async import() {
46
- this.log(this.config, this.$t(this.messages.IMPORT_MSG, { module: 'Audiences' }), 'info');
47
+ async import() {
47
48
  await this.init();
48
49
  await fsUtil.makeDirectory(this.audienceMapperDirPath);
50
+ log.debug(`Created mapper directory: ${this.audienceMapperDirPath}`, this.config.context);
51
+
49
52
  const { dirName, fileName } = this.audienceConfig;
50
53
  const audiencesPath = resolve(
51
54
  sanitizePath(this.config.data),
@@ -54,34 +57,49 @@ export default class Audiences extends PersonalizationAdapter<ImportConfig> {
54
57
  sanitizePath(fileName),
55
58
  );
56
59
 
60
+ log.debug(`Checking for audiences file: ${audiencesPath}`, this.config.context);
61
+
57
62
  if (existsSync(audiencesPath)) {
58
63
  try {
59
64
  const audiences = fsUtil.readFile(audiencesPath, true) as AudienceStruct[];
65
+ log.info(`Found ${audiences.length} audiences to import`, this.config.context);
66
+
60
67
  const attributesUid = (fsUtil.readFile(this.attributesMapperPath, true) as Record<string, string>) || {};
68
+ log.debug(`Loaded ${Object.keys(attributesUid).length} attribute mappings for audience processing`, this.config.context);
61
69
 
62
70
  for (const audience of audiences) {
63
71
  let { name, definition, description, uid } = audience;
72
+ log.debug(`Processing audience: ${name} (${uid})`, this.config.context);
73
+
64
74
  try {
65
75
  //check whether reference attributes exists or not
66
76
  if (definition.rules?.length) {
77
+ log.debug(`Processing ${definition.rules.length} definition rules for audience: ${name}`, this.config.context);
67
78
  definition.rules = lookUpAttributes(definition.rules, attributesUid);
79
+ log.debug(`Processed definition rules, remaining rules: ${definition.rules.length}`, this.config.context);
80
+ } else {
81
+ log.debug(`No definition rules found for audience: ${name}`, this.config.context);
68
82
  }
83
+
84
+ log.debug(`Creating audience: ${name}`, this.config.context);
69
85
  const audienceRes = await this.createAudience({ definition, name, description });
70
86
  //map old audience uid to new audience uid
71
87
  //mapper file is used to check whether audience created or not before creating experience
72
88
  this.audiencesUidMapper[uid] = audienceRes?.uid ?? '';
89
+ log.debug(`Created audience: ${uid} -> ${audienceRes?.uid}`, this.config.context);
73
90
  } catch (error) {
74
- this.log(this.config, `Failed to create audience uid: ${uid}, name: ${name}`, 'error');
75
- this.log(this.config, error, 'error');
91
+ handleAndLogError(error, this.config.context, `Failed to create audience: ${name} (${uid})`);
76
92
  }
77
93
  }
78
94
 
79
95
  fsUtil.writeFile(this.audiencesUidMapperPath, this.audiencesUidMapper);
80
- this.log(this.config, this.$t(this.messages.CREATE_SUCCESS, { module: 'Audiences' }), 'info');
96
+ log.debug(`Saved ${Object.keys(this.audiencesUidMapper).length} audience mappings to: ${this.audiencesUidMapperPath}`, this.config.context);
97
+ log.success('Audiences imported successfully', this.config.context);
81
98
  } catch (error) {
82
- this.log(this.config, this.$t(this.messages.CREATE_FAILURE, { module: 'Audiences' }), 'error');
83
- this.log(this.config, error, 'error');
99
+ handleAndLogError(error, this.config.context);
84
100
  }
101
+ } else {
102
+ log.warn(`Audiences file not found: ${audiencesPath}`, this.config.context);
85
103
  }
86
104
  }
87
105
  }
@@ -1,8 +1,8 @@
1
1
  import { resolve } from 'path';
2
2
  import { existsSync } from 'fs';
3
- import { sanitizePath } from '@contentstack/cli-utilities';
3
+ import { sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
4
4
  import { PersonalizationAdapter, fsUtil } from '../utils';
5
- import { APIConfig, EventStruct, ImportConfig, LogType } from '../types';
5
+ import { APIConfig, EventStruct, ImportConfig } from '../types';
6
6
 
7
7
  export default class Events extends PersonalizationAdapter<ImportConfig> {
8
8
  private mapperDirPath: string;
@@ -12,13 +12,14 @@ export default class Events extends PersonalizationAdapter<ImportConfig> {
12
12
  private personalizeConfig: ImportConfig['modules']['personalize'];
13
13
  private eventsConfig: ImportConfig['modules']['personalize']['events'];
14
14
 
15
- constructor(public readonly config: ImportConfig, private readonly log: LogType = console.log) {
15
+ constructor(public readonly config: ImportConfig) {
16
16
  const conf: APIConfig = {
17
17
  config,
18
18
  baseURL: config.modules.personalize.baseURL[config.region.name],
19
19
  headers: { 'X-Project-Uid': config.modules.personalize.project_id },
20
20
  };
21
21
  super(Object.assign(config, conf));
22
+
22
23
  this.personalizeConfig = this.config.modules.personalize;
23
24
  this.eventsConfig = this.personalizeConfig.events;
24
25
  this.mapperDirPath = resolve(
@@ -29,15 +30,17 @@ export default class Events extends PersonalizationAdapter<ImportConfig> {
29
30
  this.eventMapperDirPath = resolve(sanitizePath(this.mapperDirPath), sanitizePath(this.eventsConfig.dirName));
30
31
  this.eventsUidMapperPath = resolve(sanitizePath(this.eventMapperDirPath), 'uid-mapping.json');
31
32
  this.eventsUidMapper = {};
33
+ this.config.context.module = 'events';
32
34
  }
33
35
 
34
36
  /**
35
- * The function asynchronously imports attributes from a JSON file and creates them in the system.
37
+ * The function asynchronously imports events from a JSON file and creates them in the system.
36
38
  */
37
39
  async import() {
38
- this.log(this.config, this.$t(this.messages.IMPORT_MSG, { module: 'Events' }), 'info');
39
40
  await this.init();
40
41
  await fsUtil.makeDirectory(this.eventMapperDirPath);
42
+ log.debug(`Created mapper directory: ${this.eventMapperDirPath}`, this.config.context);
43
+
41
44
  const { dirName, fileName } = this.eventsConfig;
42
45
  const eventsPath = resolve(
43
46
  sanitizePath(this.config.data),
@@ -46,27 +49,35 @@ export default class Events extends PersonalizationAdapter<ImportConfig> {
46
49
  sanitizePath(fileName),
47
50
  );
48
51
 
52
+ log.debug(`Checking for events file: ${eventsPath}`, this.config.context);
53
+
49
54
  if (existsSync(eventsPath)) {
50
55
  try {
51
56
  const events = fsUtil.readFile(eventsPath, true) as EventStruct[];
57
+ log.info(`Found ${events.length} events to import`, this.config.context);
52
58
 
53
59
  for (const event of events) {
54
60
  const { key, description, uid } = event;
61
+ log.debug(`Processing event: ${key} (${uid})`, this.config.context);
62
+
55
63
  try {
64
+ log.debug(`Creating event: ${key}`, this.config.context);
56
65
  const eventsResponse = await this.createEvents({ key, description });
57
66
  this.eventsUidMapper[uid] = eventsResponse?.uid ?? '';
67
+ log.debug(`Created event: ${uid} -> ${eventsResponse?.uid}`, this.config.context);
58
68
  } catch (error) {
59
- this.log(this.config, `failed to create event uid: ${uid}`, 'error');
60
- this.log(this.config, error, 'error');
69
+ handleAndLogError(error, this.config.context, `Failed to create event: ${key} (${uid})`);
61
70
  }
62
71
  }
63
72
 
64
73
  fsUtil.writeFile(this.eventsUidMapperPath, this.eventsUidMapper);
65
- this.log(this.config, this.$t(this.messages.CREATE_SUCCESS, { module: 'Events' }), 'info');
74
+ log.debug(`Saved ${Object.keys(this.eventsUidMapper).length} event mappings to: ${this.eventsUidMapperPath}`, this.config.context);
75
+ log.success('Events imported successfully', this.config.context);
66
76
  } catch (error) {
67
- this.log(this.config, this.$t(this.messages.CREATE_FAILURE, { module: 'Events' }), 'error');
68
- this.log(this.config, error, 'error');
77
+ handleAndLogError(error, this.config.context);
69
78
  }
79
+ } else {
80
+ log.warn(`Events file not found: ${eventsPath}`, this.config.context);
70
81
  }
71
82
  }
72
83
  }
@@ -2,16 +2,16 @@ import { join, resolve } from 'path';
2
2
  import { existsSync } from 'fs';
3
3
  import values from 'lodash/values';
4
4
  import cloneDeep from 'lodash/cloneDeep';
5
- import { sanitizePath } from '@contentstack/cli-utilities';
5
+ import { sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
6
6
  import { PersonalizationAdapter, fsUtil, lookUpAudiences, lookUpEvents } from '../utils';
7
7
  import {
8
8
  APIConfig,
9
9
  ImportConfig,
10
10
  ExperienceStruct,
11
11
  CreateExperienceInput,
12
- LogType,
13
12
  CreateExperienceVersionInput,
14
13
  } from '../types';
14
+
15
15
  export default class Experiences extends PersonalizationAdapter<ImportConfig> {
16
16
  private createdCTs: string[];
17
17
  private mapperDirPath: string;
@@ -41,8 +41,8 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
41
41
  private audienceConfig: ImportConfig['modules']['personalize']['audiences'];
42
42
  private experienceConfig: ImportConfig['modules']['personalize']['experiences'];
43
43
 
44
- constructor(public readonly config: ImportConfig, private readonly log: LogType = console.log) {
45
- const conf: APIConfig = {
44
+ constructor(public readonly config: ImportConfig) {
45
+ const conf: APIConfig = {
46
46
  config,
47
47
  baseURL: config.modules.personalize.baseURL[config.region.name],
48
48
  headers: { 'X-Project-Uid': config.modules.personalize.project_id },
@@ -52,6 +52,7 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
52
52
  },
53
53
  };
54
54
  super(Object.assign(config, conf));
55
+
55
56
  this.personalizeConfig = this.config.modules.personalize;
56
57
  this.experiencesDirPath = resolve(
57
58
  sanitizePath(this.config.data),
@@ -100,22 +101,28 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
100
101
  this.createdCTs = [];
101
102
  this.audiencesUid = (fsUtil.readFile(this.audiencesMapperPath, true) as Record<string, string>) || {};
102
103
  this.eventsUid = (fsUtil.readFile(this.eventsMapperPath, true) as Record<string, string>) || {};
104
+ this.config.context.module = 'experiences';
103
105
  }
104
106
 
105
107
  /**
106
108
  * The function asynchronously imports experiences from a JSON file and creates them in the system.
107
109
  */
108
- async import() {
109
- this.log(this.config, this.$t(this.messages.IMPORT_MSG, { module: 'Experiences' }), 'info');
110
+ async import() {
110
111
  await this.init();
111
112
  await fsUtil.makeDirectory(this.expMapperDirPath);
113
+ log.debug(`Created mapper directory: ${this.expMapperDirPath}`, this.config.context);
112
114
 
113
115
  if (existsSync(this.experiencesPath)) {
116
+ log.debug(`Loading experiences from: ${this.experiencesPath}`, this.config.context);
117
+
114
118
  try {
115
119
  const experiences = fsUtil.readFile(this.experiencesPath, true) as ExperienceStruct[];
120
+ log.info(`Found ${experiences.length} experiences to import`, this.config.context);
116
121
 
117
122
  for (const experience of experiences) {
118
123
  const { uid, ...restExperienceData } = experience;
124
+ log.debug(`Processing experience: ${uid}`, this.config.context);
125
+
119
126
  //check whether reference audience exists or not that referenced in variations having __type equal to AudienceBasedVariation & targeting
120
127
  let experienceReqObj: CreateExperienceInput = lookUpAudiences(restExperienceData, this.audiencesUid);
121
128
  //check whether events exists or not that referenced in metrics
@@ -124,40 +131,44 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
124
131
  const expRes = (await this.createExperience(experienceReqObj)) as ExperienceStruct;
125
132
  //map old experience uid to new experience uid
126
133
  this.experiencesUidMapper[uid] = expRes?.uid ?? '';
134
+ log.debug(`Created experience: ${uid} -> ${expRes?.uid}`, this.config.context);
127
135
 
128
136
  try {
129
137
  // import versions of experience
130
138
  await this.importExperienceVersions(expRes, uid);
131
139
  } catch (error) {
132
- this.log(this.config, `Error while importing experience versions of ${expRes.uid}`, 'error');
133
- this.log(this.config, error, 'error');
140
+ handleAndLogError(error, this.config.context, `Failed to import experience versions for ${expRes.uid}`);
134
141
  }
135
142
  }
143
+
136
144
  fsUtil.writeFile(this.experiencesUidMapperPath, this.experiencesUidMapper);
137
- this.log(this.config, this.$t(this.messages.CREATE_SUCCESS, { module: 'Experiences' }), 'info');
145
+ log.success('Experiences created successfully', this.config.context);
138
146
 
139
- this.log(this.config, this.messages.VALIDATE_VARIANT_AND_VARIANT_GRP, 'info');
147
+ log.info('Validating variant and variant group creation',this.config.context);
140
148
  this.pendingVariantAndVariantGrpForExperience = values(cloneDeep(this.experiencesUidMapper));
141
149
  const jobRes = await this.validateVariantGroupAndVariantsCreated();
142
150
  fsUtil.writeFile(this.cmsVariantPath, this.cmsVariants);
143
151
  fsUtil.writeFile(this.cmsVariantGroupPath, this.cmsVariantGroups);
152
+
144
153
  if (jobRes) {
145
- this.log(this.config, this.$t(this.messages.CREATE_SUCCESS, { module: 'Variant & Variant groups' }), 'info');
154
+ log.success('Variant and variant groups created successfully', this.config.context);
146
155
  } else {
156
+ log.error('Failed to create variants and variant groups', this.config.context);
147
157
  this.personalizeConfig.importData = false;
148
158
  }
149
159
 
150
160
  if (this.personalizeConfig.importData) {
151
- this.log(this.config, this.messages.UPDATING_CT_IN_EXP, 'info');
161
+ log.info('Attaching content types to experiences', this.config.context);
152
162
  await this.attachCTsInExperience();
153
- this.log(this.config, this.messages.UPDATED_CT_IN_EXP, 'info');
163
+ log.success('Content types attached to experiences successfully', this.config.context);
154
164
  }
155
165
 
156
166
  await this.createVariantIdMapper();
157
167
  } catch (error) {
158
- this.log(this.config, this.$t(this.messages.CREATE_FAILURE, { module: 'Experiences' }), 'error');
159
- this.log(this.config, error, 'error');
168
+ handleAndLogError(error, this.config.context);
160
169
  }
170
+ } else {
171
+ log.warn(`Experiences file not found: ${this.experiencesPath}`, this.config.context);
161
172
  }
162
173
  }
163
174
 
@@ -165,6 +176,8 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
165
176
  * function import experience versions from a JSON file and creates them in the project.
166
177
  */
167
178
  async importExperienceVersions(experience: ExperienceStruct, oldExperienceUid: string) {
179
+ log.debug(`Importing versions for experience: ${oldExperienceUid}`, this.config.context);
180
+
168
181
  const versionsPath = resolve(
169
182
  sanitizePath(this.experiencesDirPath),
170
183
  'versions',
@@ -172,10 +185,13 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
172
185
  );
173
186
 
174
187
  if (!existsSync(versionsPath)) {
188
+ log.debug(`No versions file found for experience: ${oldExperienceUid}`, this.config.context);
175
189
  return;
176
190
  }
177
191
 
178
192
  const versions = fsUtil.readFile(versionsPath, true) as ExperienceStruct[];
193
+ log.debug(`Found ${versions.length} versions for experience: ${oldExperienceUid}`, this.config.context);
194
+
179
195
  const versionMap: Record<string, CreateExperienceVersionInput | undefined> = {
180
196
  ACTIVE: undefined,
181
197
  DRAFT: undefined,
@@ -189,6 +205,7 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
189
205
 
190
206
  if (versionReqObj && versionReqObj.status) {
191
207
  versionMap[versionReqObj.status] = versionReqObj;
208
+ log.debug(`Mapped version with status: ${versionReqObj.status}`, this.config.context);
192
209
  }
193
210
  });
194
211
 
@@ -201,18 +218,22 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
201
218
  experience: ExperienceStruct,
202
219
  versionMap: Record<string, CreateExperienceVersionInput | undefined>,
203
220
  ) {
221
+ log.debug(`Handling version update/create for experience: ${experience.uid}`, this.config.context);
204
222
  const { ACTIVE, DRAFT, PAUSE } = versionMap;
205
223
  let latestVersionUsed = false;
206
224
 
207
225
  if (ACTIVE) {
226
+ log.debug(`Updating experience version to ACTIVE for: ${experience.uid}`, this.config.context);
208
227
  await this.updateExperienceVersion(experience.uid, experience.latestVersion, ACTIVE);
209
228
  latestVersionUsed = true;
210
229
  }
211
230
 
212
231
  if (DRAFT) {
213
232
  if (latestVersionUsed) {
233
+ log.debug(`Creating new DRAFT version for: ${experience.uid}`, this.config.context);
214
234
  await this.createExperienceVersion(experience.uid, DRAFT);
215
235
  } else {
236
+ log.debug(`Updating experience version to DRAFT for: ${experience.uid}`, this.config.context);
216
237
  await this.updateExperienceVersion(experience.uid, experience.latestVersion, DRAFT);
217
238
  latestVersionUsed = true;
218
239
  }
@@ -220,8 +241,10 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
220
241
 
221
242
  if (PAUSE) {
222
243
  if (latestVersionUsed) {
244
+ log.debug(`Creating new PAUSE version for: ${experience.uid}`, this.config.context);
223
245
  await this.createExperienceVersion(experience.uid, PAUSE);
224
246
  } else {
247
+ log.debug(`Updating experience version to PAUSE for: ${experience.uid}`, this.config.context);
225
248
  await this.updateExperienceVersion(experience.uid, experience.latestVersion, PAUSE);
226
249
  }
227
250
  }
@@ -235,14 +258,20 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
235
258
  * @returns
236
259
  */
237
260
  async validateVariantGroupAndVariantsCreated(retryCount = 0): Promise<any> {
261
+ log.debug(`Validating variant groups and variants creation - attempt ${retryCount + 1}/${this.maxValidateRetry}`, this.config.context);
262
+
238
263
  try {
239
264
  const promises = this.pendingVariantAndVariantGrpForExperience.map(async (expUid) => {
265
+ log.debug(`Checking experience: ${expUid}`, this.config.context);
240
266
  const expRes = await this.getExperience(expUid);
241
267
  const variants = expRes?._cms?.variants ?? {};
242
268
  if (expRes?._cms && expRes?._cms?.variantGroup && Object.keys(variants).length > 0) {
269
+ log.debug(`Found variants and variant group for experience: ${expUid}`, this.config.context);
243
270
  this.cmsVariants[expUid] = expRes._cms?.variants ?? {};
244
271
  this.cmsVariantGroups[expUid] = expRes._cms?.variantGroup ?? {};
245
272
  return expUid; // Return the expUid for filtering later
273
+ } else {
274
+ log.debug(`Variants/variant group not ready for experience: ${expUid}`, this.config.context);
246
275
  }
247
276
  });
248
277
 
@@ -251,6 +280,7 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
251
280
 
252
281
  if (this.pendingVariantAndVariantGrpForExperience?.length) {
253
282
  if (retryCount !== this.maxValidateRetry) {
283
+ log.debug(`Waiting ${this.expCheckIntervalDuration}ms before retry`, this.config.context);
254
284
  await this.delay(this.expCheckIntervalDuration);
255
285
  // Filter out the processed elements
256
286
  this.pendingVariantAndVariantGrpForExperience = this.pendingVariantAndVariantGrpForExperience.filter(
@@ -258,65 +288,89 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
258
288
  );
259
289
  return this.validateVariantGroupAndVariantsCreated(retryCount);
260
290
  } else {
261
- this.log(this.config, this.messages.PERSONALIZE_JOB_FAILURE, 'error');
291
+ log.error('Personalize job failed to create variants and variant groups', this.config.context);
292
+ log.error(`Failed experiences: ${this.pendingVariantAndVariantGrpForExperience.join(', ')}`, this.config.context);
262
293
  fsUtil.writeFile(this.failedCmsExpPath, this.pendingVariantAndVariantGrpForExperience);
263
294
  return false;
264
295
  }
265
296
  } else {
297
+ log.debug('All variant groups and variants created successfully', this.config.context);
266
298
  return true;
267
299
  }
268
300
  } catch (error) {
269
- this.log(this.config, `Error while validating variant group and variants creation:`, 'error');
301
+ handleAndLogError(error, this.config.context);
270
302
  throw error;
271
303
  }
272
304
  }
273
305
 
274
306
  async attachCTsInExperience() {
307
+ log.debug('Attaching content types to experiences', this.config.context);
308
+
275
309
  try {
276
310
  // Read the created content types from the file
277
311
  this.createdCTs = fsUtil.readFile(this.cTsSuccessPath, true) as any;
278
312
  if (!this.createdCTs) {
279
- this.log(this.config, 'No Content types created, skipping following process', 'error');
313
+ log.debug('No Content types created, skipping following process', this.config.context);
280
314
  return;
281
315
  }
316
+
317
+ log.debug(`Found ${this.createdCTs.length} created content types`, this.config.context);
282
318
  const experienceCTsMap = fsUtil.readFile(this.experienceCTsPath, true) as Record<string, string[]>;
319
+
283
320
  return await Promise.allSettled(
284
321
  Object.entries(this.experiencesUidMapper).map(async ([oldExpUid, newExpUid]) => {
285
322
  if (experienceCTsMap[oldExpUid]?.length) {
323
+ log.debug(`Processing content types for experience: ${oldExpUid} -> ${newExpUid}`, this.config.context);
324
+
286
325
  // Filter content types that were created
287
326
  const updatedContentTypes = experienceCTsMap[oldExpUid].filter(
288
327
  (ct: any) => this.createdCTs.includes(ct?.uid) && ct.status === 'linked',
289
328
  );
329
+
290
330
  if (updatedContentTypes?.length) {
331
+ log.debug(`Attaching ${updatedContentTypes.length} content types to experience: ${newExpUid}`, this.config.context);
291
332
  const { variant_groups: [variantGroup] = [] } =
292
333
  (await this.getVariantGroup({ experienceUid: newExpUid })) || {};
293
334
  variantGroup.content_types = updatedContentTypes;
294
335
  // Update content types detail in the new experience asynchronously
295
336
  return await this.updateVariantGroup(variantGroup);
337
+ } else {
338
+ log.debug(`No valid content types found for experience: ${newExpUid}`, this.config.context);
296
339
  }
340
+ } else {
341
+ log.debug(`No content types mapped for experience: ${oldExpUid}`, this.config.context);
297
342
  }
298
343
  }),
299
344
  );
300
345
  } catch (error) {
301
- this.log(this.config, `Error while attaching content type with experience`, 'error');
302
- this.log(this.config, error, 'error');
346
+ handleAndLogError(error, this.config.context, 'Failed to attach content type with experience');
303
347
  }
304
348
  }
305
349
 
306
350
  async createVariantIdMapper() {
351
+ log.debug('Creating variant ID mapper', this.config.context);
352
+
307
353
  try {
308
354
  const experienceVariantIds: any = fsUtil.readFile(this.experienceVariantsIdsPath, true) || [];
355
+ log.debug(`Found ${experienceVariantIds.length} experience variant IDs to process`, this.config.context);
356
+
309
357
  const variantUIDMapper: Record<string, string> = {};
310
358
  for (let experienceVariantId of experienceVariantIds) {
311
359
  const [experienceId, variantShortId, oldVariantId] = experienceVariantId.split('-');
312
360
  const latestVariantId = this.cmsVariants[this.experiencesUidMapper[experienceId]]?.[variantShortId];
313
361
  if (latestVariantId) {
314
362
  variantUIDMapper[oldVariantId] = latestVariantId;
363
+ log.debug(`Mapped variant ID: ${oldVariantId} -> ${latestVariantId}`, this.config.context);
364
+ } else {
365
+ log.warn(`Could not find variant ID mapping for: ${experienceVariantId}`, this.config.context);
315
366
  }
316
367
  }
317
368
 
369
+ log.debug(`Created ${Object.keys(variantUIDMapper).length} variant ID mappings`, this.config.context);
318
370
  fsUtil.writeFile(this.variantUidMapperFilePath, variantUIDMapper);
371
+ log.debug(`Variant ID mapper saved to: ${this.variantUidMapperFilePath}`, this.config.context);
319
372
  } catch (error) {
373
+ handleAndLogError(error, this.config.context, 'Failed to create variant ID mapper');
320
374
  throw error;
321
375
  }
322
376
  }