@contentstack/cli-variants 1.3.3 → 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.
Files changed (55) hide show
  1. package/lib/export/attributes.d.ts +2 -2
  2. package/lib/export/attributes.js +51 -23
  3. package/lib/export/audiences.d.ts +2 -2
  4. package/lib/export/audiences.js +50 -24
  5. package/lib/export/events.d.ts +2 -2
  6. package/lib/export/events.js +52 -24
  7. package/lib/export/experiences.js +87 -54
  8. package/lib/export/projects.d.ts +3 -2
  9. package/lib/export/projects.js +55 -63
  10. package/lib/export/variant-entries.d.ts +19 -0
  11. package/lib/export/variant-entries.js +76 -1
  12. package/lib/import/attribute.d.ts +2 -0
  13. package/lib/import/attribute.js +83 -37
  14. package/lib/import/audiences.d.ts +2 -0
  15. package/lib/import/audiences.js +85 -41
  16. package/lib/import/events.d.ts +3 -1
  17. package/lib/import/events.js +86 -30
  18. package/lib/import/experiences.d.ts +2 -0
  19. package/lib/import/experiences.js +93 -39
  20. package/lib/import/project.d.ts +2 -0
  21. package/lib/import/project.js +81 -22
  22. package/lib/import/variant-entries.d.ts +10 -0
  23. package/lib/import/variant-entries.js +132 -47
  24. package/lib/index.d.ts +1 -0
  25. package/lib/index.js +1 -0
  26. package/lib/types/export-config.d.ts +0 -2
  27. package/lib/types/import-config.d.ts +0 -1
  28. package/lib/types/utils.d.ts +1 -1
  29. package/lib/utils/constants.d.ts +91 -0
  30. package/lib/utils/constants.js +93 -0
  31. package/lib/utils/personalization-api-adapter.d.ts +34 -1
  32. package/lib/utils/personalization-api-adapter.js +171 -44
  33. package/lib/utils/variant-api-adapter.d.ts +28 -1
  34. package/lib/utils/variant-api-adapter.js +75 -0
  35. package/package.json +1 -1
  36. package/src/export/attributes.ts +84 -34
  37. package/src/export/audiences.ts +87 -41
  38. package/src/export/events.ts +84 -41
  39. package/src/export/experiences.ts +155 -83
  40. package/src/export/projects.ts +71 -39
  41. package/src/export/variant-entries.ts +136 -12
  42. package/src/import/attribute.ts +105 -49
  43. package/src/import/audiences.ts +110 -54
  44. package/src/import/events.ts +104 -41
  45. package/src/import/experiences.ts +140 -62
  46. package/src/import/project.ts +108 -38
  47. package/src/import/variant-entries.ts +179 -65
  48. package/src/index.ts +2 -1
  49. package/src/types/export-config.ts +0 -2
  50. package/src/types/import-config.ts +0 -1
  51. package/src/types/utils.ts +1 -1
  52. package/src/utils/constants.ts +98 -0
  53. package/src/utils/personalization-api-adapter.ts +202 -66
  54. package/src/utils/variant-api-adapter.ts +82 -1
  55. package/tsconfig.json +1 -1
@@ -1,73 +1,99 @@
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, AudiencesConfig, AudienceStruct } from '../types';
5
5
  import { fsUtil, PersonalizationAdapter } from '../utils';
6
- import { PersonalizeConfig, ExportConfig, AudienceStruct, AudiencesConfig } from '../types';
6
+ import { PROCESS_NAMES, MODULE_CONTEXTS, EXPORT_PROCESS_STATUS } from '../utils/constants';
7
7
 
8
8
  export default class ExportAudiences extends PersonalizationAdapter<ExportConfig> {
9
9
  private audiencesConfig: AudiencesConfig;
10
10
  private audiencesFolderPath: string;
11
11
  private audiences: Record<string, unknown>[];
12
+ public exportConfig: ExportConfig;
12
13
  public personalizeConfig: PersonalizeConfig;
13
14
 
14
- constructor(readonly exportConfig: ExportConfig) {
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.audiencesConfig = exportConfig.modules.audiences;
22
24
  this.audiencesFolderPath = pResolve(
23
- exportConfig.data,
24
- exportConfig.branchName || '',
25
- this.personalizeConfig.dirName,
26
- this.audiencesConfig.dirName,
25
+ sanitizePath(exportConfig.data),
26
+ sanitizePath(exportConfig.branchName || ''),
27
+ sanitizePath(this.personalizeConfig.dirName),
28
+ sanitizePath(this.audiencesConfig.dirName),
27
29
  );
28
30
  this.audiences = [];
29
- this.exportConfig.context.module = 'audiences';
31
+ this.exportConfig.context.module = MODULE_CONTEXTS.AUDIENCES;
30
32
  }
31
33
 
32
34
  async start() {
33
35
  try {
34
36
  log.debug('Starting audiences export process...', this.exportConfig.context);
35
37
  log.info('Starting audiences export', this.exportConfig.context);
36
-
37
- log.debug('Initializing personalization adapter...', this.exportConfig.context);
38
- await this.init();
39
- log.debug('Personalization adapter initialized successfully', this.exportConfig.context);
40
-
41
- log.debug(`Creating audiences directory at: ${this.audiencesFolderPath}`, this.exportConfig.context);
42
- await fsUtil.makeDirectory(this.audiencesFolderPath);
43
- log.debug('Audiences directory created successfully', this.exportConfig.context);
44
-
45
- log.debug('Fetching audiences from personalization API...', this.exportConfig.context);
46
- this.audiences = (await this.getAudiences()) as AudienceStruct[];
47
- log.debug(`Fetched ${this.audiences?.length || 0} audiences`, this.exportConfig.context);
38
+
39
+ await this.withLoadingSpinner('AUDIENCES: 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 audiences directory at: ${this.audiencesFolderPath}`, this.exportConfig.context);
45
+ await fsUtil.makeDirectory(this.audiencesFolderPath);
46
+ log.debug('Audiences directory created successfully', this.exportConfig.context);
47
+
48
+ log.debug('Fetching audiences from personalization API...', this.exportConfig.context);
49
+ this.audiences = (await this.getAudiences()) as AudienceStruct[];
50
+ log.debug(`Fetched ${this.audiences?.length || 0} audiences`, this.exportConfig.context);
51
+ });
48
52
 
49
53
  if (!this.audiences?.length) {
50
54
  log.debug('No audiences found, completing export', this.exportConfig.context);
51
55
  log.info('No Audiences 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.AUDIENCES, this.audiences.length);
53
65
  } else {
54
- log.debug(`Processing ${this.audiences.length} audiences`, this.exportConfig.context);
55
- this.sanitizeAttribs();
56
- log.debug('Audiences sanitization completed', this.exportConfig.context);
57
-
58
- const audiencesFilePath = pResolve(this.audiencesFolderPath, this.audiencesConfig.fileName);
59
- log.debug(`Writing audiences to: ${audiencesFilePath}`, this.exportConfig.context);
60
- fsUtil.writeFile(audiencesFilePath, this.audiences);
61
-
62
- log.debug('Audiences export completed successfully', this.exportConfig.context);
63
- log.success(
64
- `Audiences exported successfully! Total audiences: ${this.audiences.length}`,
65
- this.exportConfig.context,
66
- );
67
- return;
66
+ progress = this.createSimpleProgress(PROCESS_NAMES.AUDIENCES, this.audiences.length);
68
67
  }
69
- } catch (error) {
68
+
69
+ log.debug(`Processing ${this.audiences.length} audiences`, this.exportConfig.context);
70
+ progress.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.AUDIENCES].EXPORTING, PROCESS_NAMES.AUDIENCES);
71
+
72
+ this.sanitizeAttribs();
73
+ log.debug('Audiences sanitization completed', this.exportConfig.context);
74
+
75
+ // Write audiences to file
76
+ progress.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.AUDIENCES].EXPORTING, PROCESS_NAMES.AUDIENCES);
77
+ const audiencesFilePath = pResolve(
78
+ sanitizePath(this.audiencesFolderPath),
79
+ sanitizePath(this.audiencesConfig.fileName),
80
+ );
81
+ log.debug(`Writing audiences to: ${audiencesFilePath}`, this.exportConfig.context);
82
+ fsUtil.writeFile(audiencesFilePath, this.audiences);
83
+
84
+ // Complete progress only if we're managing our own progress
85
+ if (!this.parentProgressManager) {
86
+ this.completeProgress(true);
87
+ }
88
+
89
+ log.debug('Audiences export completed successfully', this.exportConfig.context);
90
+ log.success(
91
+ `Audiences exported successfully! Total audiences: ${this.audiences.length}`,
92
+ this.exportConfig.context,
93
+ );
94
+ } catch (error: any) {
70
95
  log.debug(`Error occurred during audiences export: ${error}`, this.exportConfig.context);
96
+ this.completeProgress(false, error?.message || 'Audiences export failed');
71
97
  handleAndLogError(error, { ...this.exportConfig.context });
72
98
  }
73
99
  }
@@ -77,10 +103,30 @@ export default class ExportAudiences extends PersonalizationAdapter<ExportConfig
77
103
  */
78
104
  sanitizeAttribs() {
79
105
  log.debug(`Sanitizing ${this.audiences?.length || 0} audiences`, this.exportConfig.context);
80
- log.debug(`Invalid keys to remove: ${JSON.stringify(this.audiencesConfig.invalidKeys)}`, this.exportConfig.context);
81
-
82
- this.audiences = this.audiences?.map((audience) => omit(audience, this.audiencesConfig.invalidKeys)) || [];
83
-
84
- log.debug(`Sanitization complete. Total audiences after sanitization: ${this.audiences.length}`, this.exportConfig.context);
106
+
107
+ this.audiences =
108
+ this.audiences?.map((audience, index) => {
109
+ const sanitizedAudience = omit(audience, this.audiencesConfig.invalidKeys);
110
+
111
+ // Update progress for each processed audience
112
+ if (this.progressManager) {
113
+ const processName = this.parentProgressManager ? PROCESS_NAMES.AUDIENCES : undefined;
114
+ this.updateProgress(
115
+ true,
116
+ `audience ${index + 1}/${this.audiences.length}: ${
117
+ (audience as any).name || (audience as any).uid || 'unknown'
118
+ }`,
119
+ undefined,
120
+ processName,
121
+ );
122
+ }
123
+
124
+ return sanitizedAudience;
125
+ }) || [];
126
+
127
+ log.debug(
128
+ `Sanitization complete. Total audiences after sanitization: ${this.audiences.length}`,
129
+ this.exportConfig.context,
130
+ );
85
131
  }
86
132
  }
@@ -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 { PersonalizeConfig, ExportConfig, EventStruct, EventsConfig } from '../types';
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(readonly exportConfig: ExportConfig) {
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 = 'events';
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
- log.debug('Initializing personalization adapter...', this.exportConfig.context);
38
- await this.init();
39
- log.debug('Personalization adapter initialized successfully', this.exportConfig.context);
40
-
41
- log.debug(`Creating events directory at: ${this.eventsFolderPath}`, this.exportConfig.context);
42
- await fsUtil.makeDirectory(this.eventsFolderPath);
43
- log.debug('Events directory created successfully', this.exportConfig.context);
44
-
45
- log.debug('Fetching events from personalization API...', this.exportConfig.context);
46
- this.events = (await this.getEvents()) as EventStruct[];
47
- log.debug(`Fetched ${this.events?.length || 0} events`, this.exportConfig.context);
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
- log.debug(`Processing ${this.events.length} events`, this.exportConfig.context);
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
- } catch (error) {
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
- log.debug(`Invalid keys to remove: ${JSON.stringify(this.eventsConfig.invalidKeys)}`, this.exportConfig.context);
81
-
82
- this.events = this.events?.map((event) => omit(event, this.eventsConfig.invalidKeys)) || [];
83
-
84
- log.debug(`Sanitization complete. Total events after sanitization: ${this.events.length}`, this.exportConfig.context);
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 = 'experiences';
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
- log.debug('Initializing personalization adapter...', this.exportConfig.context);
40
- await this.init();
41
- log.debug('Personalization adapter initialized successfully', this.exportConfig.context);
42
-
43
- log.debug(`Creating experiences directory at: ${this.experiencesFolderPath}`, this.exportConfig.context);
44
- await fsUtil.makeDirectory(this.experiencesFolderPath);
45
- log.debug('Experiences directory created successfully', this.exportConfig.context);
46
-
47
- const versionsDirPath = path.resolve(sanitizePath(this.experiencesFolderPath), 'versions');
48
- log.debug(`Creating versions directory at: ${versionsDirPath}`, this.exportConfig.context);
49
- await fsUtil.makeDirectory(versionsDirPath);
50
- log.debug('Versions directory created successfully', this.exportConfig.context);
51
-
52
- log.debug('Fetching experiences from personalization API...', this.exportConfig.context);
53
- const experiences: Array<ExperienceStruct> = (await this.getExperiences()) || [];
54
- log.debug(`Fetched ${experiences?.length || 0} experiences`, this.exportConfig.context);
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
- // fetch versions of experience
86
- log.debug(`Fetching versions for experience: ${experience.name}`, this.exportConfig.context);
87
- const experienceVersions = (await this.getExperienceVersions(experience.uid)) || [];
88
- log.debug(`Fetched ${experienceVersions.length} versions for experience: ${experience.name}`, this.exportConfig.context);
89
-
90
- if (experienceVersions.length > 0) {
91
- const versionsFilePath = path.resolve(sanitizePath(this.experiencesFolderPath), 'versions', `${experience.uid}.json`);
92
- log.debug(`Writing experience versions to: ${versionsFilePath}`, this.exportConfig.context);
93
- fsUtil.writeFile(versionsFilePath, experienceVersions);
94
- } else {
95
- log.debug(`No versions found for experience: ${experience.name}`, this.exportConfig.context);
96
- log.info(
97
- `No versions found for experience '${experience.name}'`,
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
- try {
111
- // fetch content of experience
112
- log.debug(`Fetching variant group for experience: ${experience.name}`, this.exportConfig.context);
113
- const { variant_groups: [variantGroup] = [] } =
114
- (await this.getVariantGroup({ experienceUid: experience.uid })) || {};
115
-
116
- if (variantGroup?.content_types?.length) {
117
- log.debug(`Found ${variantGroup.content_types.length} content types for experience: ${experience.name}`, this.exportConfig.context);
118
- experienceToContentTypesMap[experience.uid] = variantGroup.content_types;
119
- } else {
120
- log.debug(`No content types found for experience: ${experience.name}`, this.exportConfig.context);
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 fetching content types for experience: ${experience.name}`, this.exportConfig.context);
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
- const variantsIdsFilePath = path.resolve(sanitizePath(this.experiencesFolderPath), 'experiences-variants-ids.json');
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(sanitizePath(this.experiencesFolderPath), 'experiences-content-types.json');
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
- handleAndLogError(error, {...this.exportConfig.context});
215
+ this.completeProgress(false, error?.message || 'Experiences export failed');
216
+ handleAndLogError(error, { ...this.exportConfig.context });
145
217
  }
146
218
  }
147
219
  }