@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/export/events.ts
CHANGED
|
@@ -1,73 +1,97 @@
|
|
|
1
1
|
import omit from 'lodash/omit';
|
|
2
2
|
import { resolve as pResolve } from 'node:path';
|
|
3
|
-
import { log, handleAndLogError } from '@contentstack/cli-utilities';
|
|
4
|
-
|
|
3
|
+
import { sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
|
|
4
|
+
import { PersonalizeConfig, ExportConfig, EventsConfig, EventStruct } from '../types';
|
|
5
5
|
import { fsUtil, PersonalizationAdapter } from '../utils';
|
|
6
|
-
import {
|
|
6
|
+
import { PROCESS_NAMES, MODULE_CONTEXTS, EXPORT_PROCESS_STATUS } from '../utils/constants';
|
|
7
7
|
|
|
8
8
|
export default class ExportEvents extends PersonalizationAdapter<ExportConfig> {
|
|
9
9
|
private eventsConfig: EventsConfig;
|
|
10
10
|
private eventsFolderPath: string;
|
|
11
11
|
private events: Record<string, unknown>[];
|
|
12
|
+
public exportConfig: ExportConfig;
|
|
12
13
|
public personalizeConfig: PersonalizeConfig;
|
|
13
14
|
|
|
14
|
-
constructor(
|
|
15
|
+
constructor(exportConfig: ExportConfig) {
|
|
15
16
|
super({
|
|
16
17
|
config: exportConfig,
|
|
17
18
|
baseURL: exportConfig.modules.personalize.baseURL[exportConfig.region.name],
|
|
18
19
|
headers: { 'X-Project-Uid': exportConfig.project_id },
|
|
19
20
|
});
|
|
21
|
+
this.exportConfig = exportConfig;
|
|
20
22
|
this.personalizeConfig = exportConfig.modules.personalize;
|
|
21
23
|
this.eventsConfig = exportConfig.modules.events;
|
|
22
24
|
this.eventsFolderPath = pResolve(
|
|
23
|
-
exportConfig.data,
|
|
24
|
-
exportConfig.branchName || '',
|
|
25
|
-
this.personalizeConfig.dirName,
|
|
26
|
-
this.eventsConfig.dirName,
|
|
25
|
+
sanitizePath(exportConfig.data),
|
|
26
|
+
sanitizePath(exportConfig.branchName || ''),
|
|
27
|
+
sanitizePath(this.personalizeConfig.dirName),
|
|
28
|
+
sanitizePath(this.eventsConfig.dirName),
|
|
27
29
|
);
|
|
28
30
|
this.events = [];
|
|
29
|
-
this.exportConfig.context.module =
|
|
31
|
+
this.exportConfig.context.module = MODULE_CONTEXTS.EVENTS;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
async start() {
|
|
33
35
|
try {
|
|
34
36
|
log.debug('Starting events export process...', this.exportConfig.context);
|
|
35
37
|
log.info('Starting events export', this.exportConfig.context);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
|
|
39
|
+
await this.withLoadingSpinner('EVENTS: Initializing export and fetching data...', async () => {
|
|
40
|
+
log.debug('Initializing personalization adapter...', this.exportConfig.context);
|
|
41
|
+
await this.init();
|
|
42
|
+
log.debug('Personalization adapter initialized successfully', this.exportConfig.context);
|
|
43
|
+
|
|
44
|
+
log.debug(`Creating events directory at: ${this.eventsFolderPath}`, this.exportConfig.context);
|
|
45
|
+
await fsUtil.makeDirectory(this.eventsFolderPath);
|
|
46
|
+
log.debug('Events directory created successfully', this.exportConfig.context);
|
|
47
|
+
|
|
48
|
+
log.debug('Fetching events from personalization API...', this.exportConfig.context);
|
|
49
|
+
this.events = (await this.getEvents()) as EventStruct[];
|
|
50
|
+
log.debug(`Fetched ${this.events?.length || 0} events`, this.exportConfig.context);
|
|
51
|
+
});
|
|
48
52
|
|
|
49
53
|
if (!this.events?.length) {
|
|
50
54
|
log.debug('No events found, completing export', this.exportConfig.context);
|
|
51
55
|
log.info('No Events found with the given project!', this.exportConfig.context);
|
|
52
56
|
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let progress: any;
|
|
60
|
+
|
|
61
|
+
if (this.parentProgressManager) {
|
|
62
|
+
progress = this.parentProgressManager;
|
|
63
|
+
this.progressManager = this.parentProgressManager;
|
|
64
|
+
progress.updateProcessTotal(PROCESS_NAMES.EVENTS, this.events.length);
|
|
53
65
|
} else {
|
|
54
|
-
|
|
55
|
-
this.sanitizeAttribs();
|
|
56
|
-
log.debug('Events sanitization completed', this.exportConfig.context);
|
|
57
|
-
|
|
58
|
-
const eventsFilePath = pResolve(this.eventsFolderPath, this.eventsConfig.fileName);
|
|
59
|
-
log.debug(`Writing events to: ${eventsFilePath}`, this.exportConfig.context);
|
|
60
|
-
fsUtil.writeFile(eventsFilePath, this.events);
|
|
61
|
-
|
|
62
|
-
log.debug('Events export completed successfully', this.exportConfig.context);
|
|
63
|
-
log.success(
|
|
64
|
-
`Events exported successfully! Total events: ${this.events.length}`,
|
|
65
|
-
this.exportConfig.context,
|
|
66
|
-
);
|
|
67
|
-
return;
|
|
66
|
+
progress = this.createSimpleProgress(PROCESS_NAMES.EVENTS, this.events.length);
|
|
68
67
|
}
|
|
69
|
-
|
|
68
|
+
|
|
69
|
+
log.debug(`Processing ${this.events.length} events`, this.exportConfig.context);
|
|
70
|
+
progress.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.EVENTS].EXPORTING, PROCESS_NAMES.EVENTS);
|
|
71
|
+
|
|
72
|
+
this.sanitizeAttribs();
|
|
73
|
+
log.debug('Events sanitization completed', this.exportConfig.context);
|
|
74
|
+
|
|
75
|
+
progress.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.EVENTS].EXPORTING, PROCESS_NAMES.EVENTS);
|
|
76
|
+
const eventsFilePath = pResolve(sanitizePath(this.eventsFolderPath), sanitizePath(this.eventsConfig.fileName));
|
|
77
|
+
log.debug(`Writing events to: ${eventsFilePath}`, this.exportConfig.context);
|
|
78
|
+
fsUtil.writeFile(eventsFilePath, this.events);
|
|
79
|
+
|
|
80
|
+
// Final progress update
|
|
81
|
+
if (this.progressManager) {
|
|
82
|
+
//this.updateProgress(true, `${this.events.length} events exported`, undefined, processName);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Complete progress only if we're managing our own progress
|
|
86
|
+
if (!this.parentProgressManager) {
|
|
87
|
+
this.completeProgress(true);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
log.debug('Events export completed successfully', this.exportConfig.context);
|
|
91
|
+
log.success(`Events exported successfully! Total events: ${this.events.length}`, this.exportConfig.context);
|
|
92
|
+
} catch (error: any) {
|
|
70
93
|
log.debug(`Error occurred during events export: ${error}`, this.exportConfig.context);
|
|
94
|
+
this.completeProgress(false, error?.message || 'Events export failed');
|
|
71
95
|
handleAndLogError(error, { ...this.exportConfig.context });
|
|
72
96
|
}
|
|
73
97
|
}
|
|
@@ -77,10 +101,29 @@ export default class ExportEvents extends PersonalizationAdapter<ExportConfig> {
|
|
|
77
101
|
*/
|
|
78
102
|
sanitizeAttribs() {
|
|
79
103
|
log.debug(`Sanitizing ${this.events?.length || 0} events`, this.exportConfig.context);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
104
|
+
|
|
105
|
+
this.events =
|
|
106
|
+
this.events?.map((event, index) => {
|
|
107
|
+
const sanitizedEvent = omit(event, this.eventsConfig.invalidKeys);
|
|
108
|
+
|
|
109
|
+
if (this.progressManager) {
|
|
110
|
+
const processName = this.parentProgressManager ? PROCESS_NAMES.EVENTS : undefined;
|
|
111
|
+
this.updateProgress(
|
|
112
|
+
true,
|
|
113
|
+
`event ${index + 1}/${this.events.length}: ${
|
|
114
|
+
(event as any).key || (event as any).name || (event as any).uid || 'unknown'
|
|
115
|
+
}`,
|
|
116
|
+
undefined,
|
|
117
|
+
processName,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return sanitizedEvent;
|
|
122
|
+
}) || [];
|
|
123
|
+
|
|
124
|
+
log.debug(
|
|
125
|
+
`Sanitization complete. Total events after sanitization: ${this.events.length}`,
|
|
126
|
+
this.exportConfig.context,
|
|
127
|
+
);
|
|
85
128
|
}
|
|
86
129
|
}
|
|
@@ -2,11 +2,13 @@ import * as path from 'path';
|
|
|
2
2
|
import { sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
|
|
3
3
|
import { PersonalizeConfig, ExportConfig, ExperienceStruct } from '../types';
|
|
4
4
|
import { fsUtil, PersonalizationAdapter } from '../utils';
|
|
5
|
+
import { PROCESS_NAMES, MODULE_CONTEXTS, EXPORT_PROCESS_STATUS } from '../utils/constants';
|
|
5
6
|
|
|
6
7
|
export default class ExportExperiences extends PersonalizationAdapter<ExportConfig> {
|
|
7
8
|
private experiencesFolderPath: string;
|
|
8
9
|
public exportConfig: ExportConfig;
|
|
9
10
|
public personalizeConfig: PersonalizeConfig;
|
|
11
|
+
|
|
10
12
|
constructor(exportConfig: ExportConfig) {
|
|
11
13
|
super({
|
|
12
14
|
config: exportConfig,
|
|
@@ -25,123 +27,193 @@ export default class ExportExperiences extends PersonalizationAdapter<ExportConf
|
|
|
25
27
|
sanitizePath(this.personalizeConfig.dirName),
|
|
26
28
|
'experiences',
|
|
27
29
|
);
|
|
28
|
-
this.exportConfig.context.module =
|
|
30
|
+
this.exportConfig.context.module = MODULE_CONTEXTS.EXPERIENCES;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
async start() {
|
|
32
34
|
try {
|
|
33
|
-
// get all experiences
|
|
34
|
-
// loop through experiences and get content types attached to it
|
|
35
|
-
// write experiences in to a file
|
|
36
35
|
log.debug('Starting experiences export process...', this.exportConfig.context);
|
|
37
36
|
log.info('Starting experiences export', this.exportConfig.context);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
37
|
+
|
|
38
|
+
const { experiences } = await this.withLoadingSpinner(
|
|
39
|
+
'EXPERIENCES: Initializing export and fetching data...',
|
|
40
|
+
async () => {
|
|
41
|
+
log.debug('Initializing personalization adapter...', this.exportConfig.context);
|
|
42
|
+
await this.init();
|
|
43
|
+
log.debug('Personalization adapter initialized successfully', this.exportConfig.context);
|
|
44
|
+
|
|
45
|
+
log.debug(`Creating experiences directory at: ${this.experiencesFolderPath}`, this.exportConfig.context);
|
|
46
|
+
await fsUtil.makeDirectory(this.experiencesFolderPath);
|
|
47
|
+
log.debug('Experiences directory created successfully', this.exportConfig.context);
|
|
48
|
+
|
|
49
|
+
const versionsDirPath = path.resolve(sanitizePath(this.experiencesFolderPath), 'versions');
|
|
50
|
+
log.debug(`Creating versions directory at: ${versionsDirPath}`, this.exportConfig.context);
|
|
51
|
+
await fsUtil.makeDirectory(versionsDirPath);
|
|
52
|
+
log.debug('Versions directory created successfully', this.exportConfig.context);
|
|
53
|
+
|
|
54
|
+
log.debug('Fetching experiences from personalization API...', this.exportConfig.context);
|
|
55
|
+
const experiences: Array<ExperienceStruct> = (await this.getExperiences()) || [];
|
|
56
|
+
log.debug(`Fetched ${experiences?.length || 0} experiences`, this.exportConfig.context);
|
|
57
|
+
|
|
58
|
+
return { experiences };
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
56
62
|
if (!experiences || experiences?.length < 1) {
|
|
57
63
|
log.debug('No experiences found, completing export', this.exportConfig.context);
|
|
58
64
|
log.info('No Experiences found with the given project!', this.exportConfig.context);
|
|
59
65
|
return;
|
|
60
66
|
}
|
|
61
|
-
|
|
67
|
+
|
|
68
|
+
let progress: any;
|
|
69
|
+
const processName = PROCESS_NAMES.EXPERIENCES;
|
|
70
|
+
|
|
71
|
+
if (this.parentProgressManager) {
|
|
72
|
+
progress = this.parentProgressManager;
|
|
73
|
+
this.progressManager = this.parentProgressManager;
|
|
74
|
+
progress.updateProcessTotal(processName, experiences.length);
|
|
75
|
+
} else {
|
|
76
|
+
// Create our own progress for standalone execution
|
|
77
|
+
progress = this.createSimpleProgress(PROCESS_NAMES.EXPERIENCES, experiences.length);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
log.debug(`Processing ${experiences.length} experiences`, this.exportConfig.context);
|
|
81
|
+
progress.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.EXPERIENCES].EXPORTING, processName);
|
|
82
|
+
|
|
62
83
|
const experiencesFilePath = path.resolve(sanitizePath(this.experiencesFolderPath), 'experiences.json');
|
|
63
84
|
log.debug(`Writing experiences to: ${experiencesFilePath}`, this.exportConfig.context);
|
|
64
85
|
fsUtil.writeFile(experiencesFilePath, experiences);
|
|
65
86
|
|
|
66
87
|
const experienceToVariantsStrList: Array<string> = [];
|
|
67
88
|
const experienceToContentTypesMap: Record<string, string[]> = {};
|
|
68
|
-
|
|
69
|
-
log.debug(`Processing ${experiences.length} experiences for variants and content types`, this.exportConfig.context);
|
|
70
|
-
|
|
71
|
-
for (let experience of experiences) {
|
|
72
|
-
log.debug(`Processing experience: ${experience.name} (${experience.uid})`, this.exportConfig.context);
|
|
73
|
-
|
|
74
|
-
// create id mapper for experience to variants
|
|
75
|
-
let variants = experience?._cms?.variants ?? {};
|
|
76
|
-
log.debug(`Found ${Object.keys(variants).length} variants for experience: ${experience.name}`, this.exportConfig.context);
|
|
77
|
-
|
|
78
|
-
Object.keys(variants).forEach((variantShortId: string) => {
|
|
79
|
-
const experienceToVariantsStr = `${experience.uid}-${variantShortId}-${variants[variantShortId]}`;
|
|
80
|
-
experienceToVariantsStrList.push(experienceToVariantsStr);
|
|
81
|
-
log.debug(`Added variant mapping: ${experienceToVariantsStr}`, this.exportConfig.context);
|
|
82
|
-
});
|
|
83
89
|
|
|
90
|
+
log.debug(
|
|
91
|
+
`Processing ${experiences.length} experiences for variants and content types`,
|
|
92
|
+
this.exportConfig.context,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
progress.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.EXPERIENCES].EXPORTING, processName);
|
|
96
|
+
|
|
97
|
+
for (let experienceIndex = 0; experienceIndex < experiences.length; experienceIndex++) {
|
|
98
|
+
const experience = experiences[experienceIndex];
|
|
84
99
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
log.debug(
|
|
101
|
+
`Processing experience: ${experience.name} (${experience.uid}) - ${experienceIndex + 1}/${
|
|
102
|
+
experiences.length
|
|
103
|
+
}`,
|
|
104
|
+
this.exportConfig.context,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// create id mapper for experience to variants
|
|
108
|
+
let variants = experience?._cms?.variants ?? {};
|
|
109
|
+
// talisman-ignore-start
|
|
110
|
+
log.debug(
|
|
111
|
+
`Found ${Object.keys(variants).length} variants for experience: ${experience.name}`,
|
|
112
|
+
this.exportConfig.context,
|
|
113
|
+
);
|
|
114
|
+
// talisman-ignore-end
|
|
115
|
+
|
|
116
|
+
// talisman-ignore-start
|
|
117
|
+
Object.keys(variants).forEach((variantShortId: string) => {
|
|
118
|
+
const experienceToVariantsStr = `${experience.uid}-${variantShortId}-${variants[variantShortId]}`;
|
|
119
|
+
experienceToVariantsStrList.push(experienceToVariantsStr);
|
|
120
|
+
log.debug(`Added variant mapping: ${experienceToVariantsStr}`, this.exportConfig.context);
|
|
121
|
+
});
|
|
122
|
+
// talisman-ignore-end
|
|
123
|
+
|
|
124
|
+
// Fetch versions of experience
|
|
125
|
+
try {
|
|
126
|
+
log.debug(`Fetching versions for experience: ${experience.name}`, this.exportConfig.context);
|
|
127
|
+
const experienceVersions = (await this.getExperienceVersions(experience.uid)) || [];
|
|
128
|
+
log.debug(
|
|
129
|
+
`Fetched ${experienceVersions.length} versions for experience: ${experience.name}`,
|
|
98
130
|
this.exportConfig.context,
|
|
99
131
|
);
|
|
132
|
+
|
|
133
|
+
if (experienceVersions.length > 0) {
|
|
134
|
+
const versionsFilePath = path.resolve(
|
|
135
|
+
sanitizePath(this.experiencesFolderPath),
|
|
136
|
+
'versions',
|
|
137
|
+
`${experience.uid}.json`,
|
|
138
|
+
);
|
|
139
|
+
log.debug(`Writing experience versions to: ${versionsFilePath}`, this.exportConfig.context);
|
|
140
|
+
fsUtil.writeFile(versionsFilePath, experienceVersions);
|
|
141
|
+
} else {
|
|
142
|
+
log.debug(`No versions found for experience: ${experience.name}`, this.exportConfig.context);
|
|
143
|
+
log.info(`No versions found for experience '${experience.name}'`, this.exportConfig.context);
|
|
144
|
+
}
|
|
145
|
+
} catch (error: any) {
|
|
146
|
+
log.debug(
|
|
147
|
+
`Error occurred while fetching versions for experience: ${experience.name}`,
|
|
148
|
+
this.exportConfig.context,
|
|
149
|
+
);
|
|
150
|
+
handleAndLogError(
|
|
151
|
+
error,
|
|
152
|
+
{ ...this.exportConfig.context },
|
|
153
|
+
`Failed to fetch versions of experience ${experience.name}`,
|
|
154
|
+
);
|
|
100
155
|
}
|
|
101
|
-
} catch (error) {
|
|
102
|
-
log.debug(`Error occurred while fetching versions for experience: ${experience.name}`, this.exportConfig.context);
|
|
103
|
-
handleAndLogError(
|
|
104
|
-
error,
|
|
105
|
-
{...this.exportConfig.context},
|
|
106
|
-
`Failed to fetch versions of experience ${experience.name}`
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
156
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
157
|
+
// Fetch content types of experience
|
|
158
|
+
try {
|
|
159
|
+
log.debug(`Fetching variant group for experience: ${experience.name}`, this.exportConfig.context);
|
|
160
|
+
const { variant_groups: [variantGroup] = [] } =
|
|
161
|
+
(await this.getVariantGroup({ experienceUid: experience.uid })) || {};
|
|
162
|
+
|
|
163
|
+
if (variantGroup?.content_types?.length) {
|
|
164
|
+
log.debug(
|
|
165
|
+
`Found ${variantGroup.content_types.length} content types for experience: ${experience.name}`,
|
|
166
|
+
this.exportConfig.context,
|
|
167
|
+
);
|
|
168
|
+
experienceToContentTypesMap[experience.uid] = variantGroup.content_types;
|
|
169
|
+
} else {
|
|
170
|
+
log.debug(`No content types found for experience: ${experience.name}`, this.exportConfig.context);
|
|
171
|
+
}
|
|
172
|
+
} catch (error: any) {
|
|
173
|
+
log.debug(
|
|
174
|
+
`Error occurred while fetching content types for experience: ${experience.name}`,
|
|
175
|
+
this.exportConfig.context,
|
|
176
|
+
);
|
|
177
|
+
handleAndLogError(
|
|
178
|
+
error,
|
|
179
|
+
{ ...this.exportConfig.context },
|
|
180
|
+
`Failed to fetch content types of experience ${experience.name}`,
|
|
181
|
+
);
|
|
121
182
|
}
|
|
122
|
-
} catch (error) {
|
|
123
|
-
log.debug(`Error occurred while
|
|
124
|
-
handleAndLogError(
|
|
125
|
-
error,
|
|
126
|
-
{...this.exportConfig.context},
|
|
127
|
-
`Failed to fetch content types of experience ${experience.name}`
|
|
128
|
-
);
|
|
183
|
+
} catch (error: any) {
|
|
184
|
+
log.debug(`Error occurred while processing experience: ${experience.name}`, this.exportConfig.context);
|
|
185
|
+
handleAndLogError(error, { ...this.exportConfig.context }, `Failed to process experience ${experience.name}`);
|
|
129
186
|
}
|
|
130
187
|
}
|
|
131
|
-
|
|
132
|
-
|
|
188
|
+
|
|
189
|
+
progress.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.EXPERIENCES].EXPORTING, processName);
|
|
190
|
+
|
|
191
|
+
// Write final mapping files
|
|
192
|
+
const variantsIdsFilePath = path.resolve(
|
|
193
|
+
sanitizePath(this.experiencesFolderPath),
|
|
194
|
+
'experiences-variants-ids.json',
|
|
195
|
+
);
|
|
133
196
|
log.debug(`Writing experience variants mapping to: ${variantsIdsFilePath}`, this.exportConfig.context);
|
|
134
197
|
fsUtil.writeFile(variantsIdsFilePath, experienceToVariantsStrList);
|
|
135
198
|
|
|
136
|
-
const contentTypesFilePath = path.resolve(
|
|
199
|
+
const contentTypesFilePath = path.resolve(
|
|
200
|
+
sanitizePath(this.experiencesFolderPath),
|
|
201
|
+
'experiences-content-types.json',
|
|
202
|
+
);
|
|
137
203
|
log.debug(`Writing experience content types mapping to: ${contentTypesFilePath}`, this.exportConfig.context);
|
|
138
204
|
fsUtil.writeFile(contentTypesFilePath, experienceToContentTypesMap);
|
|
139
|
-
|
|
205
|
+
|
|
206
|
+
// Complete progress only if we're managing our own progress
|
|
207
|
+
if (!this.parentProgressManager) {
|
|
208
|
+
this.completeProgress(true);
|
|
209
|
+
}
|
|
210
|
+
|
|
140
211
|
log.debug('Experiences export completed successfully', this.exportConfig.context);
|
|
141
212
|
log.success('Experiences exported successfully!', this.exportConfig.context);
|
|
142
|
-
} catch (error) {
|
|
213
|
+
} catch (error: any) {
|
|
143
214
|
log.debug(`Error occurred during experiences export: ${error}`, this.exportConfig.context);
|
|
144
|
-
|
|
215
|
+
this.completeProgress(false, error?.message || 'Experiences export failed');
|
|
216
|
+
handleAndLogError(error, { ...this.exportConfig.context });
|
|
145
217
|
}
|
|
146
218
|
}
|
|
147
219
|
}
|
package/src/export/projects.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { sanitizePath, log } from '@contentstack/cli-utilities';
|
|
3
|
-
import { ExportConfig,
|
|
4
|
-
import {
|
|
1
|
+
import { resolve as pResolve } from 'node:path';
|
|
2
|
+
import { sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
|
|
3
|
+
import { PersonalizeConfig, ExportConfig, ProjectStruct } from '../types';
|
|
4
|
+
import { fsUtil, PersonalizationAdapter } from '../utils';
|
|
5
|
+
import { PROCESS_NAMES, MODULE_CONTEXTS, EXPORT_PROCESS_STATUS } from '../utils/constants';
|
|
5
6
|
|
|
6
7
|
export default class ExportProjects extends PersonalizationAdapter<ExportConfig> {
|
|
7
|
-
private
|
|
8
|
+
private projectsFolderPath: string;
|
|
9
|
+
private projectsData: ProjectStruct[];
|
|
8
10
|
public exportConfig: ExportConfig;
|
|
9
11
|
public personalizeConfig: PersonalizeConfig;
|
|
12
|
+
|
|
10
13
|
constructor(exportConfig: ExportConfig) {
|
|
11
14
|
super({
|
|
12
15
|
config: exportConfig,
|
|
@@ -15,57 +18,86 @@ export default class ExportProjects extends PersonalizationAdapter<ExportConfig>
|
|
|
15
18
|
});
|
|
16
19
|
this.exportConfig = exportConfig;
|
|
17
20
|
this.personalizeConfig = exportConfig.modules.personalize;
|
|
18
|
-
this.
|
|
21
|
+
this.projectsFolderPath = pResolve(
|
|
19
22
|
sanitizePath(exportConfig.data),
|
|
20
23
|
sanitizePath(exportConfig.branchName || ''),
|
|
21
24
|
sanitizePath(this.personalizeConfig.dirName),
|
|
22
25
|
'projects',
|
|
23
26
|
);
|
|
24
|
-
this.
|
|
27
|
+
this.projectsData = [];
|
|
28
|
+
this.exportConfig.context.module = MODULE_CONTEXTS.PROJECTS;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
async start() {
|
|
28
32
|
try {
|
|
29
33
|
log.debug('Starting projects export process...', this.exportConfig.context);
|
|
30
|
-
log.info(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
await this.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
log.info('Starting projects export', this.exportConfig.context);
|
|
35
|
+
|
|
36
|
+
// Initial setup with loading spinner
|
|
37
|
+
await this.withLoadingSpinner('PROJECTS: Initializing export and fetching data...', async () => {
|
|
38
|
+
log.debug('Initializing personalization adapter...', this.exportConfig.context);
|
|
39
|
+
await this.init();
|
|
40
|
+
log.debug('Personalization adapter initialized successfully', this.exportConfig.context);
|
|
41
|
+
|
|
42
|
+
log.debug(`Creating projects directory at: ${this.projectsFolderPath}`, this.exportConfig.context);
|
|
43
|
+
await fsUtil.makeDirectory(this.projectsFolderPath);
|
|
44
|
+
log.debug('Projects directory created successfully', this.exportConfig.context);
|
|
45
|
+
|
|
46
|
+
log.debug('Fetching projects from personalization API...', this.exportConfig.context);
|
|
47
|
+
// talisman-ignore-line
|
|
48
|
+
this.projectsData = (await this.projects({ connectedStackApiKey: this.exportConfig.apiKey })) || [];
|
|
49
|
+
log.debug(`Fetched ${this.projectsData?.length || 0} projects`, this.exportConfig.context);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (!this.projectsData?.length) {
|
|
45
53
|
log.debug('No projects found, disabling personalization', this.exportConfig.context);
|
|
46
|
-
log.info(
|
|
54
|
+
log.info('No Personalize Project connected with the given stack', this.exportConfig.context);
|
|
47
55
|
this.exportConfig.personalizationEnabled = false;
|
|
48
56
|
return;
|
|
49
57
|
}
|
|
50
|
-
|
|
51
|
-
|
|
58
|
+
|
|
59
|
+
// Enable personalization and set project config
|
|
60
|
+
log.debug(`Found ${this.projectsData.length} projects, enabling personalization`, this.exportConfig.context);
|
|
52
61
|
this.exportConfig.personalizationEnabled = true;
|
|
53
|
-
this.exportConfig.project_id =
|
|
54
|
-
log.debug(`Set project ID: ${
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
} catch (error) {
|
|
63
|
-
if (error !== 'Forbidden') {
|
|
64
|
-
log.debug(`Error occurred during projects export: ${error}`, this.exportConfig.context);
|
|
65
|
-
log.error('Failed to export projects!', this.exportConfig.context);
|
|
62
|
+
this.exportConfig.project_id = this.projectsData[0]?.uid;
|
|
63
|
+
log.debug(`Set project ID: ${this.projectsData[0]?.uid}`, this.exportConfig.context);
|
|
64
|
+
|
|
65
|
+
let progress: any;
|
|
66
|
+
if (this.parentProgressManager) {
|
|
67
|
+
progress = this.parentProgressManager;
|
|
68
|
+
this.progressManager = this.parentProgressManager;
|
|
69
|
+
// Parent already has correct count, just update status
|
|
70
|
+
progress.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.PROJECTS].EXPORTING, PROCESS_NAMES.PROJECTS);
|
|
66
71
|
} else {
|
|
67
|
-
|
|
72
|
+
progress = this.createNestedProgress(PROCESS_NAMES.PROJECTS);
|
|
73
|
+
progress.addProcess(PROCESS_NAMES.PROJECTS, this.projectsData?.length);
|
|
74
|
+
progress
|
|
75
|
+
.startProcess(PROCESS_NAMES.PROJECTS)
|
|
76
|
+
.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.PROJECTS].EXPORTING, PROCESS_NAMES.PROJECTS);
|
|
68
77
|
}
|
|
78
|
+
|
|
79
|
+
const projectsFilePath = pResolve(sanitizePath(this.projectsFolderPath), 'projects.json');
|
|
80
|
+
log.debug(`Writing projects to: ${projectsFilePath}`, this.exportConfig.context);
|
|
81
|
+
fsUtil.writeFile(projectsFilePath, this.projectsData);
|
|
82
|
+
log.debug('Projects export completed successfully', this.exportConfig.context);
|
|
83
|
+
|
|
84
|
+
const processName = PROCESS_NAMES.PROJECTS;
|
|
85
|
+
this.updateProgress(true, 'project export', undefined, processName);
|
|
86
|
+
|
|
87
|
+
// Complete process only if we're managing our own progress
|
|
88
|
+
if (!this.parentProgressManager) {
|
|
89
|
+
progress.completeProcess(PROCESS_NAMES.PROJECTS, true);
|
|
90
|
+
this.completeProgress(true);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
log.success(
|
|
94
|
+
`Projects exported successfully! Total projects: ${this.projectsData.length} - personalization enabled`,
|
|
95
|
+
this.exportConfig.context,
|
|
96
|
+
);
|
|
97
|
+
} catch (error: any) {
|
|
98
|
+
log.debug(`Error occurred during projects export: ${error}`, this.exportConfig.context);
|
|
99
|
+
this.completeProgress(false, error?.message || 'Projects export failed');
|
|
100
|
+
handleAndLogError(error, { ...this.exportConfig.context });
|
|
69
101
|
throw error;
|
|
70
102
|
}
|
|
71
103
|
}
|