@contentstack/cli-variants 1.3.2 → 2.0.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/export/attributes.d.ts +2 -2
- package/lib/export/attributes.js +51 -23
- package/lib/export/audiences.d.ts +2 -2
- package/lib/export/audiences.js +50 -24
- package/lib/export/events.d.ts +2 -2
- package/lib/export/events.js +52 -24
- package/lib/export/experiences.js +87 -54
- package/lib/export/projects.d.ts +3 -2
- package/lib/export/projects.js +55 -63
- package/lib/export/variant-entries.d.ts +19 -0
- package/lib/export/variant-entries.js +76 -1
- package/lib/import/attribute.d.ts +2 -0
- package/lib/import/attribute.js +83 -37
- package/lib/import/audiences.d.ts +2 -0
- package/lib/import/audiences.js +85 -41
- package/lib/import/events.d.ts +3 -1
- package/lib/import/events.js +86 -30
- package/lib/import/experiences.d.ts +2 -0
- package/lib/import/experiences.js +93 -39
- package/lib/import/project.d.ts +2 -0
- package/lib/import/project.js +81 -22
- package/lib/import/variant-entries.d.ts +10 -0
- package/lib/import/variant-entries.js +139 -53
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/types/export-config.d.ts +0 -2
- package/lib/types/import-config.d.ts +0 -1
- package/lib/types/utils.d.ts +1 -1
- package/lib/utils/constants.d.ts +91 -0
- package/lib/utils/constants.js +93 -0
- package/lib/utils/personalization-api-adapter.d.ts +34 -1
- package/lib/utils/personalization-api-adapter.js +180 -53
- package/lib/utils/variant-api-adapter.d.ts +28 -1
- package/lib/utils/variant-api-adapter.js +89 -32
- package/package.json +2 -2
- package/src/export/attributes.ts +84 -34
- package/src/export/audiences.ts +87 -41
- package/src/export/events.ts +84 -41
- package/src/export/experiences.ts +155 -83
- package/src/export/projects.ts +71 -39
- package/src/export/variant-entries.ts +136 -12
- package/src/import/attribute.ts +105 -49
- package/src/import/audiences.ts +110 -54
- package/src/import/events.ts +104 -41
- package/src/import/experiences.ts +140 -62
- package/src/import/project.ts +108 -38
- package/src/import/variant-entries.ts +188 -75
- package/src/index.ts +2 -1
- package/src/types/export-config.ts +0 -2
- package/src/types/import-config.ts +0 -1
- package/src/types/utils.ts +1 -1
- package/src/utils/constants.ts +98 -0
- package/src/utils/personalization-api-adapter.ts +212 -76
- package/src/utils/variant-api-adapter.ts +137 -50
- package/tsconfig.json +1 -1
package/src/import/events.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { existsSync } from 'fs';
|
|
|
3
3
|
import { sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
|
|
4
4
|
import { PersonalizationAdapter, fsUtil } from '../utils';
|
|
5
5
|
import { APIConfig, EventStruct, ImportConfig } from '../types';
|
|
6
|
+
import { PROCESS_NAMES, MODULE_CONTEXTS, IMPORT_PROCESS_STATUS } from '../utils/constants';
|
|
6
7
|
|
|
7
8
|
export default class Events extends PersonalizationAdapter<ImportConfig> {
|
|
8
9
|
private mapperDirPath: string;
|
|
@@ -10,7 +11,8 @@ export default class Events extends PersonalizationAdapter<ImportConfig> {
|
|
|
10
11
|
private eventsUidMapperPath: string;
|
|
11
12
|
private eventsUidMapper: Record<string, unknown>;
|
|
12
13
|
private personalizeConfig: ImportConfig['modules']['personalize'];
|
|
13
|
-
private
|
|
14
|
+
private eventConfig: ImportConfig['modules']['personalize']['events'];
|
|
15
|
+
private events: EventStruct[];
|
|
14
16
|
|
|
15
17
|
constructor(public readonly config: ImportConfig) {
|
|
16
18
|
const conf: APIConfig = {
|
|
@@ -19,65 +21,126 @@ export default class Events extends PersonalizationAdapter<ImportConfig> {
|
|
|
19
21
|
headers: { 'X-Project-Uid': config.modules.personalize.project_id },
|
|
20
22
|
};
|
|
21
23
|
super(Object.assign(config, conf));
|
|
22
|
-
|
|
24
|
+
|
|
23
25
|
this.personalizeConfig = this.config.modules.personalize;
|
|
24
|
-
this.
|
|
26
|
+
this.eventConfig = this.personalizeConfig.events;
|
|
25
27
|
this.mapperDirPath = resolve(
|
|
26
28
|
sanitizePath(this.config.backupDir),
|
|
27
29
|
'mapper',
|
|
28
30
|
sanitizePath(this.personalizeConfig.dirName),
|
|
29
31
|
);
|
|
30
|
-
this.eventMapperDirPath = resolve(sanitizePath(this.mapperDirPath), sanitizePath(this.
|
|
32
|
+
this.eventMapperDirPath = resolve(sanitizePath(this.mapperDirPath), sanitizePath(this.eventConfig.dirName));
|
|
31
33
|
this.eventsUidMapperPath = resolve(sanitizePath(this.eventMapperDirPath), 'uid-mapping.json');
|
|
32
34
|
this.eventsUidMapper = {};
|
|
33
|
-
this.
|
|
35
|
+
this.events = [];
|
|
36
|
+
this.config.context.module = MODULE_CONTEXTS.EVENTS;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
/**
|
|
37
40
|
* The function asynchronously imports events from a JSON file and creates them in the system.
|
|
38
41
|
*/
|
|
39
42
|
async import() {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
try {
|
|
44
|
+
log.debug('Starting events import...', this.config.context);
|
|
45
|
+
|
|
46
|
+
const [canImport, eventsCount] = await this.analyzeEvents();
|
|
47
|
+
if (!canImport) {
|
|
48
|
+
log.info('No events found to import', this.config.context);
|
|
49
|
+
// Still need to mark as complete for parent progress
|
|
50
|
+
if (this.parentProgressManager) {
|
|
51
|
+
this.parentProgressManager.tick(true, 'events module (no data)', null, PROCESS_NAMES.EVENTS);
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Don't create own progress manager if we have a parent
|
|
57
|
+
let progress;
|
|
58
|
+
if (this.parentProgressManager) {
|
|
59
|
+
progress = this.parentProgressManager;
|
|
60
|
+
log.debug('Using parent progress manager for events import', this.config.context);
|
|
61
|
+
this.parentProgressManager.updateProcessTotal(PROCESS_NAMES.EVENTS, eventsCount);
|
|
62
|
+
} else {
|
|
63
|
+
progress = this.createSimpleProgress(PROCESS_NAMES.EVENTS, eventsCount);
|
|
64
|
+
log.debug('Created standalone progress manager for events import', this.config.context);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
await this.init();
|
|
68
|
+
await fsUtil.makeDirectory(this.eventMapperDirPath);
|
|
69
|
+
log.debug(`Created mapper directory: ${this.eventMapperDirPath}`, this.config.context);
|
|
70
|
+
|
|
71
|
+
log.info(`Processing ${eventsCount} events`, this.config.context);
|
|
72
|
+
|
|
73
|
+
for (const event of this.events) {
|
|
74
|
+
const { key, description, uid } = event;
|
|
75
|
+
if (!this.parentProgressManager) {
|
|
76
|
+
progress.updateStatus(IMPORT_PROCESS_STATUS[PROCESS_NAMES.EVENTS].CREATING);
|
|
77
|
+
}
|
|
78
|
+
log.debug(`Processing event: ${key} (${uid})`, this.config.context);
|
|
51
79
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
this.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
handleAndLogError(error, this.config.context, `Failed to create event: ${key} (${uid})`);
|
|
80
|
+
try {
|
|
81
|
+
log.debug(`Creating event: ${key}`, this.config.context);
|
|
82
|
+
const eventRes = await this.createEvents({ key, description });
|
|
83
|
+
this.eventsUidMapper[uid] = eventRes?.uid ?? '';
|
|
84
|
+
|
|
85
|
+
// For parent progress manager, we don't need to specify process name as it will be handled automatically
|
|
86
|
+
if (this.parentProgressManager) {
|
|
87
|
+
this.updateProgress(true, `event: ${key}`);
|
|
88
|
+
} else {
|
|
89
|
+
this.updateProgress(true, `event: ${key}`, undefined, PROCESS_NAMES.EVENTS);
|
|
90
|
+
}
|
|
91
|
+
log.debug(`Created event: ${uid} -> ${eventRes?.uid}`, this.config.context);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (this.parentProgressManager) {
|
|
94
|
+
this.updateProgress(false, `event: ${key}`, (error as any)?.message);
|
|
95
|
+
} else {
|
|
96
|
+
this.updateProgress(false, `event: ${key}`, (error as any)?.message, PROCESS_NAMES.EVENTS);
|
|
70
97
|
}
|
|
98
|
+
handleAndLogError(error, this.config.context, `Failed to create event: ${key} (${uid})`);
|
|
71
99
|
}
|
|
100
|
+
}
|
|
72
101
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
102
|
+
fsUtil.writeFile(this.eventsUidMapperPath, this.eventsUidMapper);
|
|
103
|
+
log.debug(`Saved ${Object.keys(this.eventsUidMapper).length} event mappings`, this.config.context);
|
|
104
|
+
|
|
105
|
+
// Only complete progress if we own the progress manager (no parent)
|
|
106
|
+
if (!this.parentProgressManager) {
|
|
107
|
+
this.completeProgress(true);
|
|
108
|
+
}
|
|
109
|
+
log.success(
|
|
110
|
+
`Events imported successfully! Total events: ${eventsCount} - personalization enabled`,
|
|
111
|
+
this.config.context,
|
|
112
|
+
);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (!this.parentProgressManager) {
|
|
115
|
+
this.completeProgress(false, (error as any)?.message || 'Events import failed');
|
|
78
116
|
}
|
|
79
|
-
|
|
80
|
-
|
|
117
|
+
handleAndLogError(error, this.config.context);
|
|
118
|
+
throw error;
|
|
81
119
|
}
|
|
82
120
|
}
|
|
121
|
+
|
|
122
|
+
private async analyzeEvents(): Promise<[boolean, number]> {
|
|
123
|
+
return this.withLoadingSpinner('EVENTS: Analyzing import data...', async () => {
|
|
124
|
+
const { dirName, fileName } = this.eventConfig;
|
|
125
|
+
const eventsPath = resolve(
|
|
126
|
+
sanitizePath(this.config.data),
|
|
127
|
+
sanitizePath(this.personalizeConfig.dirName),
|
|
128
|
+
sanitizePath(dirName),
|
|
129
|
+
sanitizePath(fileName),
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
log.debug(`Checking for events file: ${eventsPath}`, this.config.context);
|
|
133
|
+
|
|
134
|
+
if (!existsSync(eventsPath)) {
|
|
135
|
+
log.warn(`Events file not found: ${eventsPath}`, this.config.context);
|
|
136
|
+
return [false, 0];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.events = fsUtil.readFile(eventsPath, true) as EventStruct[];
|
|
140
|
+
const eventsCount = this.events?.length || 0;
|
|
141
|
+
|
|
142
|
+
log.debug(`Found ${eventsCount} events to import`, this.config.context);
|
|
143
|
+
return [eventsCount > 0, eventsCount];
|
|
144
|
+
});
|
|
145
|
+
}
|
|
83
146
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
CreateExperienceInput,
|
|
12
12
|
CreateExperienceVersionInput,
|
|
13
13
|
} from '../types';
|
|
14
|
+
import { PROCESS_NAMES, MODULE_CONTEXTS } from '../utils/constants';
|
|
14
15
|
|
|
15
16
|
export default class Experiences extends PersonalizationAdapter<ImportConfig> {
|
|
16
17
|
private createdCTs: string[];
|
|
@@ -40,9 +41,10 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
|
|
|
40
41
|
private personalizeConfig: ImportConfig['modules']['personalize'];
|
|
41
42
|
private audienceConfig: ImportConfig['modules']['personalize']['audiences'];
|
|
42
43
|
private experienceConfig: ImportConfig['modules']['personalize']['experiences'];
|
|
44
|
+
private experiences: ExperienceStruct[];
|
|
43
45
|
|
|
44
46
|
constructor(public readonly config: ImportConfig) {
|
|
45
|
-
|
|
47
|
+
const conf: APIConfig = {
|
|
46
48
|
config,
|
|
47
49
|
baseURL: config.modules.personalize.baseURL[config.region.name],
|
|
48
50
|
headers: { 'X-Project-Uid': config.modules.personalize.project_id },
|
|
@@ -52,7 +54,7 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
|
|
|
52
54
|
},
|
|
53
55
|
};
|
|
54
56
|
super(Object.assign(config, conf));
|
|
55
|
-
|
|
57
|
+
|
|
56
58
|
this.personalizeConfig = this.config.modules.personalize;
|
|
57
59
|
this.experiencesDirPath = resolve(
|
|
58
60
|
sanitizePath(this.config.data),
|
|
@@ -101,33 +103,55 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
|
|
|
101
103
|
this.createdCTs = [];
|
|
102
104
|
this.audiencesUid = (fsUtil.readFile(this.audiencesMapperPath, true) as Record<string, string>) || {};
|
|
103
105
|
this.eventsUid = (fsUtil.readFile(this.eventsMapperPath, true) as Record<string, string>) || {};
|
|
104
|
-
this.config.context.module =
|
|
106
|
+
this.config.context.module = MODULE_CONTEXTS.EXPERIENCES;
|
|
107
|
+
this.experiences = [];
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
/**
|
|
108
111
|
* The function asynchronously imports experiences from a JSON file and creates them in the system.
|
|
109
112
|
*/
|
|
110
|
-
async import() {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
113
|
+
async import() {
|
|
114
|
+
try {
|
|
115
|
+
log.debug('Starting experiences import...', this.config.context);
|
|
116
|
+
|
|
117
|
+
const [canImport, experiencesCount] = await this.analyzeExperiences();
|
|
118
|
+
if (!canImport) {
|
|
119
|
+
log.info('No experiences found to import', this.config.context);
|
|
120
|
+
// Still need to mark as complete for parent progress
|
|
121
|
+
if (this.parentProgressManager) {
|
|
122
|
+
this.parentProgressManager.tick(true, 'experiences module (no data)', null, PROCESS_NAMES.EXPERIENCES);
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// If we have a parent progress manager, use it as a sub-module
|
|
128
|
+
// Otherwise create our own simple progress manager
|
|
129
|
+
let progress;
|
|
130
|
+
if (this.parentProgressManager) {
|
|
131
|
+
progress = this.parentProgressManager;
|
|
132
|
+
log.debug('Using parent progress manager for experiences import', this.config.context);
|
|
133
|
+
this.parentProgressManager.updateProcessTotal(PROCESS_NAMES.EXPERIENCES, experiencesCount);
|
|
134
|
+
} else {
|
|
135
|
+
progress = this.createSimpleProgress(PROCESS_NAMES.EXPERIENCES, experiencesCount);
|
|
136
|
+
log.debug('Created standalone progress manager for experiences import', this.config.context);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
await this.init();
|
|
140
|
+
await fsUtil.makeDirectory(this.expMapperDirPath);
|
|
141
|
+
log.debug(`Created mapper directory: ${this.expMapperDirPath}`, this.config.context);
|
|
142
|
+
|
|
143
|
+
log.info(`Processing ${experiencesCount} experiences for import`, this.config.context);
|
|
130
144
|
|
|
145
|
+
for (const experience of this.experiences) {
|
|
146
|
+
const { uid, ...restExperienceData } = experience;
|
|
147
|
+
log.debug(`Processing experience: ${uid}`, this.config.context);
|
|
148
|
+
|
|
149
|
+
//check whether reference audience exists or not that referenced in variations having __type equal to AudienceBasedVariation & targeting
|
|
150
|
+
let experienceReqObj: CreateExperienceInput = lookUpAudiences(restExperienceData, this.audiencesUid);
|
|
151
|
+
//check whether events exists or not that referenced in metrics
|
|
152
|
+
experienceReqObj = lookUpEvents(experienceReqObj, this.eventsUid);
|
|
153
|
+
|
|
154
|
+
try {
|
|
131
155
|
const expRes = (await this.createExperience(experienceReqObj)) as ExperienceStruct;
|
|
132
156
|
//map old experience uid to new experience uid
|
|
133
157
|
this.experiencesUidMapper[uid] = expRes?.uid ?? '';
|
|
@@ -139,45 +163,90 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
|
|
|
139
163
|
} catch (error) {
|
|
140
164
|
handleAndLogError(error, this.config.context, `Failed to import experience versions for ${expRes.uid}`);
|
|
141
165
|
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
fsUtil.writeFile(this.experiencesUidMapperPath, this.experiencesUidMapper);
|
|
145
|
-
log.success('Experiences created successfully', this.config.context);
|
|
146
|
-
|
|
147
|
-
log.info('Validating variant and variant group creation',this.config.context);
|
|
148
|
-
this.pendingVariantAndVariantGrpForExperience = values(cloneDeep(this.experiencesUidMapper));
|
|
149
|
-
const jobRes = await this.validateVariantGroupAndVariantsCreated();
|
|
150
|
-
fsUtil.writeFile(this.cmsVariantPath, this.cmsVariants);
|
|
151
|
-
fsUtil.writeFile(this.cmsVariantGroupPath, this.cmsVariantGroups);
|
|
152
|
-
|
|
153
|
-
if (jobRes) {
|
|
154
|
-
log.success('Variant and variant groups created successfully', this.config.context);
|
|
155
|
-
} else {
|
|
156
|
-
log.error('Failed to create variants and variant groups', this.config.context);
|
|
157
|
-
this.personalizeConfig.importData = false;
|
|
158
|
-
}
|
|
159
166
|
|
|
160
|
-
|
|
161
|
-
log.
|
|
162
|
-
|
|
163
|
-
|
|
167
|
+
this.updateProgress(true, `experience: ${experience.name || uid}`, undefined, PROCESS_NAMES.EXPERIENCES);
|
|
168
|
+
log.debug(`Successfully processed experience: ${uid}`, this.config.context);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
this.updateProgress(
|
|
171
|
+
false,
|
|
172
|
+
`experience: ${experience.name || uid}`,
|
|
173
|
+
(error as any)?.message,
|
|
174
|
+
PROCESS_NAMES.EXPERIENCES,
|
|
175
|
+
);
|
|
176
|
+
handleAndLogError(error, this.config.context, `Failed to create experience: ${uid}`);
|
|
164
177
|
}
|
|
178
|
+
}
|
|
165
179
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
180
|
+
fsUtil.writeFile(this.experiencesUidMapperPath, this.experiencesUidMapper);
|
|
181
|
+
log.success('Experiences created successfully', this.config.context);
|
|
182
|
+
|
|
183
|
+
log.info('Validating variant and variant group creation', this.config.context);
|
|
184
|
+
this.pendingVariantAndVariantGrpForExperience = values(cloneDeep(this.experiencesUidMapper));
|
|
185
|
+
const jobRes = await this.validateVariantGroupAndVariantsCreated();
|
|
186
|
+
fsUtil.writeFile(this.cmsVariantPath, this.cmsVariants);
|
|
187
|
+
fsUtil.writeFile(this.cmsVariantGroupPath, this.cmsVariantGroups);
|
|
188
|
+
|
|
189
|
+
if (jobRes) {
|
|
190
|
+
log.success('Variant and variant groups created successfully', this.config.context);
|
|
191
|
+
} else {
|
|
192
|
+
log.error('Failed to create variants and variant groups', this.config.context);
|
|
193
|
+
this.personalizeConfig.importData = false;
|
|
169
194
|
}
|
|
170
|
-
|
|
171
|
-
|
|
195
|
+
|
|
196
|
+
if (this.personalizeConfig.importData) {
|
|
197
|
+
log.info('Attaching content types to experiences', this.config.context);
|
|
198
|
+
await this.attachCTsInExperience();
|
|
199
|
+
log.success('Content types attached to experiences successfully', this.config.context);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
await this.createVariantIdMapper();
|
|
203
|
+
|
|
204
|
+
// Only complete progress if we own the progress manager (no parent)
|
|
205
|
+
if (!this.parentProgressManager) {
|
|
206
|
+
this.completeProgress(true);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
log.success(
|
|
210
|
+
`Experiences imported successfully! Total experiences: ${experiencesCount} - personalization enabled`,
|
|
211
|
+
this.config.context,
|
|
212
|
+
);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
if (!this.parentProgressManager) {
|
|
215
|
+
this.completeProgress(false, (error as any)?.message || 'Experiences import failed');
|
|
216
|
+
}
|
|
217
|
+
handleAndLogError(error, this.config.context);
|
|
218
|
+
throw error;
|
|
172
219
|
}
|
|
173
220
|
}
|
|
174
221
|
|
|
222
|
+
private async analyzeExperiences(): Promise<[boolean, number]> {
|
|
223
|
+
return this.withLoadingSpinner('EXPERIENCES: Analyzing import data...', async () => {
|
|
224
|
+
log.debug(`Checking for experiences file: ${this.experiencesPath}`, this.config.context);
|
|
225
|
+
|
|
226
|
+
if (!existsSync(this.experiencesPath)) {
|
|
227
|
+
log.warn(`Experiences file not found: ${this.experiencesPath}`, this.config.context);
|
|
228
|
+
return [false, 0];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
this.experiences = fsUtil.readFile(this.experiencesPath, true) as ExperienceStruct[];
|
|
232
|
+
const experiencesCount = this.experiences?.length || 0;
|
|
233
|
+
|
|
234
|
+
if (experiencesCount < 1) {
|
|
235
|
+
log.warn('No experiences found in file', this.config.context);
|
|
236
|
+
return [false, 0];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
log.debug(`Found ${experiencesCount} experiences to import`, this.config.context);
|
|
240
|
+
return [true, experiencesCount];
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
175
244
|
/**
|
|
176
245
|
* function import experience versions from a JSON file and creates them in the project.
|
|
177
246
|
*/
|
|
178
247
|
async importExperienceVersions(experience: ExperienceStruct, oldExperienceUid: string) {
|
|
179
248
|
log.debug(`Importing versions for experience: ${oldExperienceUid}`, this.config.context);
|
|
180
|
-
|
|
249
|
+
|
|
181
250
|
const versionsPath = resolve(
|
|
182
251
|
sanitizePath(this.experiencesDirPath),
|
|
183
252
|
'versions',
|
|
@@ -191,7 +260,7 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
|
|
|
191
260
|
|
|
192
261
|
const versions = fsUtil.readFile(versionsPath, true) as ExperienceStruct[];
|
|
193
262
|
log.debug(`Found ${versions.length} versions for experience: ${oldExperienceUid}`, this.config.context);
|
|
194
|
-
|
|
263
|
+
|
|
195
264
|
const versionMap: Record<string, CreateExperienceVersionInput | undefined> = {
|
|
196
265
|
ACTIVE: undefined,
|
|
197
266
|
DRAFT: undefined,
|
|
@@ -258,8 +327,11 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
|
|
|
258
327
|
* @returns
|
|
259
328
|
*/
|
|
260
329
|
async validateVariantGroupAndVariantsCreated(retryCount = 0): Promise<any> {
|
|
261
|
-
log.debug(
|
|
262
|
-
|
|
330
|
+
log.debug(
|
|
331
|
+
`Validating variant groups and variants creation - attempt ${retryCount + 1}/${this.maxValidateRetry}`,
|
|
332
|
+
this.config.context,
|
|
333
|
+
);
|
|
334
|
+
|
|
263
335
|
try {
|
|
264
336
|
const promises = this.pendingVariantAndVariantGrpForExperience.map(async (expUid) => {
|
|
265
337
|
log.debug(`Checking experience: ${expUid}`, this.config.context);
|
|
@@ -289,7 +361,10 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
|
|
|
289
361
|
return this.validateVariantGroupAndVariantsCreated(retryCount);
|
|
290
362
|
} else {
|
|
291
363
|
log.error('Personalize job failed to create variants and variant groups', this.config.context);
|
|
292
|
-
log.error(
|
|
364
|
+
log.error(
|
|
365
|
+
`Failed experiences: ${this.pendingVariantAndVariantGrpForExperience.join(', ')}`,
|
|
366
|
+
this.config.context,
|
|
367
|
+
);
|
|
293
368
|
fsUtil.writeFile(this.failedCmsExpPath, this.pendingVariantAndVariantGrpForExperience);
|
|
294
369
|
return false;
|
|
295
370
|
}
|
|
@@ -305,7 +380,7 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
|
|
|
305
380
|
|
|
306
381
|
async attachCTsInExperience() {
|
|
307
382
|
log.debug('Attaching content types to experiences', this.config.context);
|
|
308
|
-
|
|
383
|
+
|
|
309
384
|
try {
|
|
310
385
|
// Read the created content types from the file
|
|
311
386
|
this.createdCTs = fsUtil.readFile(this.cTsSuccessPath, true) as any;
|
|
@@ -313,22 +388,25 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
|
|
|
313
388
|
log.debug('No Content types created, skipping following process', this.config.context);
|
|
314
389
|
return;
|
|
315
390
|
}
|
|
316
|
-
|
|
391
|
+
|
|
317
392
|
log.debug(`Found ${this.createdCTs.length} created content types`, this.config.context);
|
|
318
393
|
const experienceCTsMap = fsUtil.readFile(this.experienceCTsPath, true) as Record<string, string[]>;
|
|
319
|
-
|
|
394
|
+
|
|
320
395
|
return await Promise.allSettled(
|
|
321
396
|
Object.entries(this.experiencesUidMapper).map(async ([oldExpUid, newExpUid]) => {
|
|
322
397
|
if (experienceCTsMap[oldExpUid]?.length) {
|
|
323
398
|
log.debug(`Processing content types for experience: ${oldExpUid} -> ${newExpUid}`, this.config.context);
|
|
324
|
-
|
|
399
|
+
|
|
325
400
|
// Filter content types that were created
|
|
326
401
|
const updatedContentTypes = experienceCTsMap[oldExpUid].filter(
|
|
327
402
|
(ct: any) => this.createdCTs.includes(ct?.uid) && ct.status === 'linked',
|
|
328
403
|
);
|
|
329
|
-
|
|
404
|
+
|
|
330
405
|
if (updatedContentTypes?.length) {
|
|
331
|
-
log.debug(
|
|
406
|
+
log.debug(
|
|
407
|
+
`Attaching ${updatedContentTypes.length} content types to experience: ${newExpUid}`,
|
|
408
|
+
this.config.context,
|
|
409
|
+
);
|
|
332
410
|
const { variant_groups: [variantGroup] = [] } =
|
|
333
411
|
(await this.getVariantGroup({ experienceUid: newExpUid })) || {};
|
|
334
412
|
variantGroup.content_types = updatedContentTypes;
|
|
@@ -349,11 +427,11 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
|
|
|
349
427
|
|
|
350
428
|
async createVariantIdMapper() {
|
|
351
429
|
log.debug('Creating variant ID mapper', this.config.context);
|
|
352
|
-
|
|
430
|
+
|
|
353
431
|
try {
|
|
354
432
|
const experienceVariantIds: any = fsUtil.readFile(this.experienceVariantsIdsPath, true) || [];
|
|
355
433
|
log.debug(`Found ${experienceVariantIds.length} experience variant IDs to process`, this.config.context);
|
|
356
|
-
|
|
434
|
+
|
|
357
435
|
const variantUIDMapper: Record<string, string> = {};
|
|
358
436
|
for (let experienceVariantId of experienceVariantIds) {
|
|
359
437
|
const [experienceId, variantShortId, oldVariantId] = experienceVariantId.split('-');
|