@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.
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 +139 -53
  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 +180 -53
  33. package/lib/utils/variant-api-adapter.d.ts +28 -1
  34. package/lib/utils/variant-api-adapter.js +89 -32
  35. package/package.json +2 -2
  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 +188 -75
  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 +212 -76
  54. package/src/utils/variant-api-adapter.ts +137 -50
  55. package/tsconfig.json +1 -1
@@ -1,12 +1,14 @@
1
1
  import { join, resolve as pResolve } from 'path';
2
2
  import { existsSync, readFileSync } from 'fs';
3
- import { sanitizePath, log } from '@contentstack/cli-utilities';
3
+ import { sanitizePath, log, cliux } from '@contentstack/cli-utilities';
4
4
  import { PersonalizationAdapter, askProjectName, fsUtil } from '../utils';
5
5
  import { APIConfig, CreateProjectInput, ImportConfig, ProjectStruct } from '../types';
6
+ import { PROCESS_NAMES, MODULE_CONTEXTS, IMPORT_PROCESS_STATUS } from '../utils/constants';
6
7
 
7
8
  export default class Project extends PersonalizationAdapter<ImportConfig> {
8
9
  private projectMapperFolderPath: string;
9
-
10
+ private projectsData: CreateProjectInput[];
11
+
10
12
  constructor(public readonly config: ImportConfig) {
11
13
  const conf: APIConfig = {
12
14
  config,
@@ -14,50 +16,56 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
14
16
  headers: { organization_uid: config.org_uid },
15
17
  };
16
18
  super(Object.assign(config, conf));
17
-
19
+
18
20
  this.projectMapperFolderPath = pResolve(
19
21
  sanitizePath(this.config.backupDir),
20
22
  'mapper',
21
23
  sanitizePath(this.config.modules.personalize.dirName),
22
24
  'projects',
23
25
  );
24
- this.config.context.module = 'project';
26
+ this.config.context.module = MODULE_CONTEXTS.PROJECTS;
27
+ this.projectsData = [];
25
28
  }
26
29
 
27
30
  /**
28
31
  * The function asynchronously imports projects data from a file and creates projects based on the
29
32
  * data.
30
33
  */
31
- async import() {
32
- const personalize = this.config.modules.personalize;
33
- const { dirName, fileName } = personalize.projects;
34
- const projectPath = join(
35
- sanitizePath(this.config.data),
36
- sanitizePath(personalize.dirName),
37
- sanitizePath(dirName),
38
- sanitizePath(fileName),
39
- );
40
-
41
- log.debug(`Checking for project file: ${projectPath}`, this.config.context);
42
-
43
- if (existsSync(projectPath)) {
44
- const projects = JSON.parse(readFileSync(projectPath, 'utf8')) as CreateProjectInput[];
45
- log.debug(`Loaded ${projects?.length || 0} projects from file`, this.config.context);
34
+ async import() {
35
+ try {
36
+ log.debug('Starting personalize project import...', this.config.context);
46
37
 
47
- if (!projects || projects.length < 1) {
48
- this.config.modules.personalize.importData = false; // Stop personalize import if stack not connected to any project
49
- log.warn('No projects found in file', this.config.context);
38
+ const [canImport, projectsCount] = await this.analyzeProjects();
39
+ if (!canImport) {
40
+ log.info('No projects found to import', this.config.context);
41
+ if (this.parentProgressManager) {
42
+ this.parentProgressManager.tick(true, 'projects module (no data)', null, PROCESS_NAMES.PROJECTS);
43
+ }
50
44
  return;
51
45
  }
52
-
46
+
47
+ // Fix 1: Always use parent progress manager when available
48
+ let progress;
49
+ if (this.parentProgressManager) {
50
+ progress = this.parentProgressManager;
51
+ log.debug('Using parent progress manager for projects import', this.config.context);
52
+ // Don't create our own progress - use parent's
53
+ } else {
54
+ progress = this.createSimpleProgress(PROCESS_NAMES.PROJECTS, projectsCount);
55
+ log.debug('Created standalone progress manager for projects import', this.config.context);
56
+ }
57
+
53
58
  await this.init();
54
-
55
- for (const project of projects) {
59
+
60
+ for (const project of this.projectsData) {
61
+ if (!this.parentProgressManager) {
62
+ progress.updateStatus(IMPORT_PROCESS_STATUS[PROCESS_NAMES.PROJECTS].CREATING);
63
+ }
56
64
  log.debug(`Processing project: ${project.name}`, this.config.context);
57
-
65
+
58
66
  const createProject = async (newName: void | string): Promise<ProjectStruct> => {
59
67
  log.debug(`Creating project with name: ${newName || project.name}`, this.config.context);
60
-
68
+
61
69
  return await this.createProject({
62
70
  name: newName || project.name,
63
71
  description: project.description,
@@ -68,26 +76,88 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
68
76
  error.includes('personalize.PROJECTS.DUPLICATE_NAME')
69
77
  ) {
70
78
  log.warn(`Project name already exists, generating new name`, this.config.context);
79
+
80
+ // Prevent progress bar corruption with clean newlines
81
+ cliux.print('\n');
71
82
  const projectName = await askProjectName('Copy Of ' + (newName || project.name));
83
+ cliux.print('\n');
84
+ if (this.parentProgressManager) {
85
+ this.parentProgressManager.updateStatus(
86
+ IMPORT_PROCESS_STATUS[PROCESS_NAMES.PROJECTS].CREATING,
87
+ PROCESS_NAMES.PROJECTS,
88
+ );
89
+ }
90
+
72
91
  return await createProject(projectName);
73
92
  }
74
93
  throw error;
75
94
  });
76
95
  };
77
96
 
78
- const projectRes = await createProject(this.config.personalizeProjectName);
79
- this.config.modules.personalize.project_id = projectRes.uid;
80
- this.config.modules.personalize.importData = true;
97
+ try {
98
+ const projectRes = await createProject(this.config.personalizeProjectName);
99
+ this.config.modules.personalize.project_id = projectRes.uid;
100
+ this.config.modules.personalize.importData = true;
101
+
102
+ await fsUtil.makeDirectory(this.projectMapperFolderPath);
103
+ fsUtil.writeFile(pResolve(sanitizePath(this.projectMapperFolderPath), 'projects.json'), projectRes);
81
104
 
82
- await fsUtil.makeDirectory(this.projectMapperFolderPath);
83
- fsUtil.writeFile(pResolve(sanitizePath(this.projectMapperFolderPath), 'projects.json'), projectRes);
84
-
85
- log.success(`Project created successfully: ${projectRes.uid}`, this.config.context);
86
- log.debug(`Project data saved to: ${this.projectMapperFolderPath}/projects.json`, this.config.context);
105
+ this.updateProgress(true, `project: ${project.name}`, undefined, PROCESS_NAMES.PROJECTS);
106
+ log.success(`Project created successfully: ${projectRes.uid}`, this.config.context);
107
+ } catch (error) {
108
+ this.updateProgress(false, `project: ${project.name}`, (error as any)?.message, PROCESS_NAMES.PROJECTS);
109
+ throw error;
110
+ }
87
111
  }
88
- } else {
89
- this.config.modules.personalize.importData = false; // Stop personalize import if stack not connected to any project
90
- log.warn(`Project file not found: ${projectPath}`, this.config.context);
112
+
113
+ // Only complete progress if we own the progress manager (no parent)
114
+ if (!this.parentProgressManager) {
115
+ this.completeProgress(true);
116
+ }
117
+
118
+ log.success(
119
+ `Projects imported successfully! Total projects: ${projectsCount} - personalization enabled`,
120
+ this.config.context,
121
+ );
122
+ } catch (error) {
123
+ this.config.modules.personalize.importData = false;
124
+ if (!this.parentProgressManager) {
125
+ this.completeProgress(false, (error as any)?.message || 'Project import failed');
126
+ }
127
+ throw error;
91
128
  }
92
129
  }
130
+
131
+ private async analyzeProjects(): Promise<[boolean, number]> {
132
+ return this.withLoadingSpinner('PROJECT: Analyzing import data...', async () => {
133
+ const personalize = this.config.modules.personalize;
134
+ const { dirName, fileName } = personalize.projects;
135
+ const projectPath = join(
136
+ sanitizePath(this.config.data),
137
+ sanitizePath(personalize.dirName),
138
+ sanitizePath(dirName),
139
+ sanitizePath(fileName),
140
+ );
141
+
142
+ log.debug(`Checking for project file: ${projectPath}`, this.config.context);
143
+
144
+ if (!existsSync(projectPath)) {
145
+ this.config.modules.personalize.importData = false;
146
+ log.warn(`Project file not found: ${projectPath}`, this.config.context);
147
+ return [false, 0];
148
+ }
149
+
150
+ this.projectsData = JSON.parse(readFileSync(projectPath, 'utf8')) as CreateProjectInput[];
151
+ const projectsCount = this.projectsData?.length || 0;
152
+
153
+ if (projectsCount < 1) {
154
+ this.config.modules.personalize.importData = false;
155
+ log.warn('No projects found in file', this.config.context);
156
+ return [false, 0];
157
+ }
158
+
159
+ log.debug(`Found ${projectsCount} projects to import`, this.config.context);
160
+ return [true, projectsCount];
161
+ });
162
+ }
93
163
  }
@@ -23,6 +23,7 @@ import {
23
23
  PublishVariantEntryDto,
24
24
  } from '../types';
25
25
  import { fsUtil } from '../utils';
26
+ import { PROCESS_NAMES, MODULE_CONTEXTS } from '../utils/constants';
26
27
 
27
28
  export default class VariantEntries extends VariantAdapter<VariantHttpClient<ImportConfig>> {
28
29
  public entriesDirPath: string;
@@ -39,6 +40,8 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
39
40
  private failedVariantPath!: string;
40
41
  private failedVariantEntries!: Record<string, any>;
41
42
  private environments!: Record<string, any>;
43
+ public progress: any;
44
+ private processInitialized: boolean = false;
42
45
 
43
46
  constructor(readonly config: ImportConfig & { helpers?: ImportHelperMethodsConfig }) {
44
47
  const conf: APIConfig & AdapterType<VariantHttpClient<ImportConfig>, APIConfig> = {
@@ -61,7 +64,24 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
61
64
  this.failedVariantPath = resolve(sanitizePath(this.entriesMapperPath), 'failed-entry-variants.json');
62
65
  this.failedVariantEntries = new Map();
63
66
  if (this.config && this.config.context) {
64
- this.config.context.module = 'variant-entries';
67
+ this.config.context.module = MODULE_CONTEXTS.VARIANT_ENTRIES;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Set parent progress manager for integration with entries module
73
+ */
74
+ public setParentProgressManager(parentProgress: any): void {
75
+ this.parentProgressManager = parentProgress;
76
+ this.progress = parentProgress;
77
+ }
78
+
79
+ /**
80
+ * Update progress for a specific item
81
+ */
82
+ protected updateProgress(success: boolean, itemName: string, error?: string, processName?: string): void {
83
+ if (this.progress) {
84
+ this.progress.tick(success, itemName, error, processName);
65
85
  }
66
86
  }
67
87
 
@@ -74,87 +94,141 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
74
94
  * message indicating that no entries were found and return.
75
95
  */
76
96
  async import() {
77
- const filePath = resolve(sanitizePath(this.entriesMapperPath), 'data-for-variant-entry.json');
78
- const variantIdPath = resolve(
79
- sanitizePath(this.config.backupDir),
80
- 'mapper',
81
- sanitizePath(this.personalizeConfig.dirName),
82
- sanitizePath(this.personalizeConfig.experiences.dirName),
83
- 'variants-uid-mapping.json',
84
- );
97
+ try {
98
+ const filePath = resolve(sanitizePath(this.entriesMapperPath), 'data-for-variant-entry.json');
99
+ const variantIdPath = resolve(
100
+ sanitizePath(this.config.backupDir),
101
+ 'mapper',
102
+ sanitizePath(this.personalizeConfig.dirName),
103
+ sanitizePath(this.personalizeConfig.experiences.dirName),
104
+ 'variants-uid-mapping.json',
105
+ );
85
106
 
86
- log.debug(`Checking for variant entry data file: ${filePath}`, this.config.context);
87
- if (!existsSync(filePath)) {
88
- log.warn(`Variant entry data file not found at path: ${filePath}, skipping import`, this.config.context);
89
- return;
90
- }
107
+ log.debug(`Checking for variant entry data file: ${filePath}`, this.config.context);
108
+ if (!existsSync(filePath)) {
109
+ log.warn(`Variant entry data file not found at path: ${filePath}, skipping import`, this.config.context);
110
+ return;
111
+ }
91
112
 
92
- log.debug(`Checking for variant ID mapping file: ${variantIdPath}`, this.config.context);
93
- if (!existsSync(variantIdPath)) {
94
- log.error('Variant UID mapping file not found', this.config.context);
95
- return;
96
- }
113
+ log.debug(`Checking for variant ID mapping file: ${variantIdPath}`, this.config.context);
114
+ if (!existsSync(variantIdPath)) {
115
+ log.error('Variant UID mapping file not found', this.config.context);
116
+ return;
117
+ }
97
118
 
98
- const entriesForVariants = fsUtil.readFile(filePath, true) as EntryDataForVariantEntries[];
99
- log.debug(`Loaded ${entriesForVariants?.length || 0} entries for variant processing`, this.config.context);
119
+ const entriesForVariants = fsUtil.readFile(filePath, true) as EntryDataForVariantEntries[];
120
+ log.debug(`Loaded ${entriesForVariants?.length || 0} entries for variant processing`, this.config.context);
100
121
 
101
- if (isEmpty(entriesForVariants)) {
102
- log.warn('No entries found for variant import', this.config.context);
103
- return;
104
- }
122
+ if (isEmpty(entriesForVariants)) {
123
+ log.warn('No entries found for variant import', this.config.context);
124
+ return;
125
+ }
105
126
 
106
- const entriesUidMapperPath = join(sanitizePath(this.entriesMapperPath), 'uid-mapping.json');
107
- const assetUidMapperPath = resolve(sanitizePath(this.config.backupDir), 'mapper', 'assets', 'uid-mapping.json');
108
- const assetUrlMapperPath = resolve(sanitizePath(this.config.backupDir), 'mapper', 'assets', 'url-mapping.json');
109
- const taxonomiesPath = resolve(
110
- sanitizePath(this.config.backupDir),
111
- 'mapper',
112
- sanitizePath(this.config.modules.taxonomies.dirName),
113
- 'terms',
114
- 'success.json',
115
- );
116
- const marketplaceAppMapperPath = resolve(
117
- sanitizePath(this.config.backupDir),
118
- 'mapper',
119
- 'marketplace_apps',
120
- 'uid-mapping.json',
121
- );
122
- const envPath = resolve(sanitizePath(this.config.backupDir), 'environments', 'environments.json');
127
+ const entriesUidMapperPath = join(sanitizePath(this.entriesMapperPath), 'uid-mapping.json');
128
+ const assetUidMapperPath = resolve(sanitizePath(this.config.backupDir), 'mapper', 'assets', 'uid-mapping.json');
129
+ const assetUrlMapperPath = resolve(sanitizePath(this.config.backupDir), 'mapper', 'assets', 'url-mapping.json');
130
+ const taxonomiesPath = resolve(
131
+ sanitizePath(this.config.backupDir),
132
+ 'mapper',
133
+ sanitizePath(this.config.modules.taxonomies.dirName),
134
+ 'terms',
135
+ 'success.json',
136
+ );
137
+ const marketplaceAppMapperPath = resolve(
138
+ sanitizePath(this.config.backupDir),
139
+ 'mapper',
140
+ 'marketplace_apps',
141
+ 'uid-mapping.json',
142
+ );
143
+ const envPath = resolve(sanitizePath(this.config.backupDir), 'environments', 'environments.json');
123
144
 
124
- log.debug('Loading variant ID mapping and dependency data', this.config.context);
145
+ log.debug('Loading variant ID mapping and dependency data', this.config.context);
125
146
 
126
- // NOTE Read and store list of variant IDs
127
- this.variantIdList = (fsUtil.readFile(variantIdPath, true) || {}) as Record<string, unknown>;
128
- if (isEmpty(this.variantIdList)) {
129
- log.warn('Empty variant UID data found', this.config.context);
130
- return;
131
- }
147
+ // NOTE Read and store list of variant IDs
148
+ this.variantIdList = (fsUtil.readFile(variantIdPath, true) || {}) as Record<string, unknown>;
149
+ if (isEmpty(this.variantIdList)) {
150
+ log.warn('Empty variant UID data found', this.config.context);
151
+ return;
152
+ }
132
153
 
133
- // NOTE entry relational data lookup dependencies.
134
- this.entriesUidMapper = (fsUtil.readFile(entriesUidMapperPath, true) || {}) as Record<string, any>;
135
- this.installedExtensions = ((fsUtil.readFile(marketplaceAppMapperPath) as any) || { extension_uid: {} })
136
- .extension_uid as Record<string, any>[];
137
- this.taxonomies = (fsUtil.readFile(taxonomiesPath, true) || {}) as Record<string, unknown>;
138
- this.assetUidMapper = (fsUtil.readFile(assetUidMapperPath, true) || {}) as Record<string, any>;
139
- this.assetUrlMapper = (fsUtil.readFile(assetUrlMapperPath, true) || {}) as Record<string, any>;
140
- this.environments = (fsUtil.readFile(envPath, true) || {}) as Record<string, any>;
154
+ // NOTE entry relational data lookup dependencies.
155
+ this.entriesUidMapper = (fsUtil.readFile(entriesUidMapperPath, true) || {}) as Record<string, any>;
156
+ this.installedExtensions = ((fsUtil.readFile(marketplaceAppMapperPath) as any) || { extension_uid: {} })
157
+ .extension_uid as Record<string, any>[];
158
+ this.taxonomies = (fsUtil.readFile(taxonomiesPath, true) || {}) as Record<string, unknown>;
159
+ this.assetUidMapper = (fsUtil.readFile(assetUidMapperPath, true) || {}) as Record<string, any>;
160
+ this.assetUrlMapper = (fsUtil.readFile(assetUrlMapperPath, true) || {}) as Record<string, any>;
161
+ this.environments = (fsUtil.readFile(envPath, true) || {}) as Record<string, any>;
141
162
 
142
163
  log.debug(
143
- `Loaded dependency data - Entries: ${Object.keys(this.entriesUidMapper).length}, Assets: ${
144
- Object.keys(this.assetUidMapper).length
145
- }, Taxonomies: ${Object.keys(this.taxonomies).length}`,
164
+ `Loaded dependency data - Entries: ${Object.keys(this.entriesUidMapper)?.length}, Assets: ${
165
+ Object.keys(this.assetUidMapper)?.length
166
+ }, Taxonomies: ${Object.keys(this.taxonomies)?.length}`,
146
167
  this.config.context,
147
168
  );
148
169
 
149
- // set the token
150
- await this.variantInstance.init();
170
+ // Initialize progress manager - will be set up lazily when first variants are found
171
+ if (this.parentProgressManager) {
172
+ this.progress = this.parentProgressManager;
173
+ log.debug('Using parent progress manager for variant entries import', this.config.context);
174
+ } else {
175
+ this.progress = this.createSimpleProgress(PROCESS_NAMES.VARIANT_ENTRIES);
176
+ log.debug('Created standalone progress manager for variant entries import', this.config.context);
177
+ }
151
178
 
152
- log.info(`Processing ${entriesForVariants.length} entries for variant import`, this.config.context);
153
- for (const entriesForVariant of entriesForVariants) {
154
- await this.importVariantEntries(entriesForVariant);
155
- }
179
+ // set the token
180
+ await this.variantInstance.init();
181
+ log.info(`Processing ${entriesForVariants?.length} entries for variant import`, this.config.context);
182
+ for (const entriesForVariant of entriesForVariants) {
183
+ try {
184
+ await this.importVariantEntries(entriesForVariant);
185
+ log.debug(
186
+ `Successfully processed variant entry: ${entriesForVariant.content_type}/${entriesForVariant.locale}/${entriesForVariant.entry_uid}`,
187
+ this.config.context,
188
+ );
189
+ } catch (error) {
190
+ handleAndLogError(
191
+ error,
192
+ this.config.context,
193
+ `Failed to import variant entry: ${entriesForVariant.content_type}/${entriesForVariant.locale}/${entriesForVariant.entry_uid}`,
194
+ );
195
+ }
196
+ }
197
+
198
+ // Complete progress if we initialized it and own the progress manager
199
+ if (this.processInitialized && this.progress) {
200
+ const processName = this.parentProgressManager ? 'Variant Entries' : PROCESS_NAMES.VARIANT_ENTRIES;
201
+ this.progress.completeProcess(processName, true);
202
+ log.success(
203
+ `Completed import of variant entries across ${entriesForVariants.length} entries`,
204
+ this.config.context,
205
+ );
206
+ } else if (entriesForVariants.length === 0) {
207
+ log.info(`No variant entries found for import`, this.config.context);
208
+ }
209
+
210
+ // Only complete overall progress if we own the progress manager (no parent)
211
+ if (!this.parentProgressManager) {
212
+ this.completeProgress(true);
213
+ }
156
214
 
157
- log.success('All variant entries have been imported and published successfully', this.config.context);
215
+ log.success(
216
+ `Variant entries imported successfully! Total entries: ${entriesForVariants.length} - processing completed`,
217
+ this.config.context,
218
+ );
219
+ } catch (error) {
220
+ // Complete progress with error if we initialized it
221
+ if (this.processInitialized && this.progress) {
222
+ const processName = this.parentProgressManager ? 'Variant Entries' : PROCESS_NAMES.VARIANT_ENTRIES;
223
+ this.progress.completeProcess(processName, false);
224
+ }
225
+
226
+ if (!this.parentProgressManager) {
227
+ this.completeProgress(false, (error as any)?.message || 'Variant entries import failed');
228
+ }
229
+ handleAndLogError(error, this.config.context, 'Variant entries import failed');
230
+ throw error;
231
+ }
158
232
  }
159
233
 
160
234
  /**
@@ -163,10 +237,11 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
163
237
  * @param {EntryDataForVariantEntries} entriesForVariant - EntryDataForVariantEntries {
164
238
  */
165
239
  async importVariantEntries(entriesForVariant: EntryDataForVariantEntries) {
240
+ let totalVariantEntries = 0;
166
241
  const variantEntry = this.config.modules.variantEntry;
167
242
  const { content_type, locale, entry_uid } = entriesForVariant;
168
243
 
169
- log.debug(`Importing variant entries for: ${content_type}/${locale}/${entry_uid}`, this.config.context);
244
+ log.info(`Importing variant entries for: ${content_type}/${locale}/${entry_uid}`, this.config.context);
170
245
 
171
246
  const ctConfig = this.config.modules['content-types'];
172
247
  const contentType: ContentTypeStruct = JSON.parse(
@@ -195,6 +270,29 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
195
270
  try {
196
271
  const variantEntries = (await fs.readChunkFiles.next()) as VariantEntryStruct[];
197
272
  if (variantEntries?.length) {
273
+ totalVariantEntries = totalVariantEntries + variantEntries.length;
274
+
275
+ // Initialize progress ONLY when we find the first variants (lazy initialization)
276
+ if (!this.processInitialized && this.progress) {
277
+ const processName = this.parentProgressManager ? 'Variant Entries' : PROCESS_NAMES.VARIANT_ENTRIES;
278
+
279
+ if (this.parentProgressManager) {
280
+ // Update the existing process total instead of creating a new one
281
+ this.progress.updateProcessTotal(processName, variantEntries.length);
282
+ } else {
283
+ this.progress.addProcess(PROCESS_NAMES.VARIANT_ENTRIES, variantEntries.length);
284
+ this.progress.startProcess(PROCESS_NAMES.VARIANT_ENTRIES);
285
+ }
286
+ this.processInitialized = true;
287
+ log.debug(`Initialized variant entries progress with first batch of ${variantEntries.length} variants`, this.config.context);
288
+ }
289
+
290
+ if (this.processInitialized && this.progress) {
291
+ const processName = this.parentProgressManager ? 'Variant Entries' : PROCESS_NAMES.VARIANT_ENTRIES;
292
+ this.progress.updateProcessTotal(processName, totalVariantEntries);
293
+ log.debug(`Updated progress total to: ${totalVariantEntries}`, this.config.context);
294
+ }
295
+
198
296
  log.debug(`Processing batch of ${variantEntries.length} variant entries`, this.config.context);
199
297
  await this.handleConcurrency(contentType, variantEntries, entriesForVariant);
200
298
  }
@@ -234,7 +332,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
234
332
  const batches = chunk(variantEntries, variantEntryConfig.apiConcurrency || 5);
235
333
  if (isEmpty(batches)) return;
236
334
 
237
- log.debug(`Starting concurrent processing for ${variantEntries.length} variant entries`, this.config.context);
335
+ log.debug(`Starting concurrent processing for ${variantEntries?.length} variant entries`, this.config.context);
238
336
 
239
337
  for (const [, batch] of entries(batches)) {
240
338
  batchNo += 1;
@@ -242,7 +340,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
242
340
  const start = Date.now();
243
341
 
244
342
  log.debug(
245
- `Processing batch ${batchNo}/${batches.length} with ${batch.length} variant entries`,
343
+ `Processing batch ${batchNo}/${batches?.length} with ${batch?.length} variant entries`,
246
344
  this.config.context,
247
345
  );
248
346
 
@@ -252,6 +350,13 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
252
350
  `Created entry variant: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
253
351
  this.config.context,
254
352
  );
353
+ const processName = this.parentProgressManager ? 'Variant Entries' : PROCESS_NAMES.VARIANT_ENTRIES;
354
+ this.updateProgress(
355
+ true,
356
+ `variant entry: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
357
+ undefined,
358
+ processName,
359
+ );
255
360
  };
256
361
 
257
362
  const onReject = ({ error, apiData }: any) => {
@@ -262,6 +367,14 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
262
367
  this.config.context,
263
368
  `Failed to create entry variant: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
264
369
  );
370
+ const processName = this.parentProgressManager ? 'Variant Entries' : PROCESS_NAMES.VARIANT_ENTRIES;
371
+ this.updateProgress(
372
+ false,
373
+ `'${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
374
+ (error as any)?.message ||
375
+ `Failed to create entry variant: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
376
+ processName,
377
+ );
265
378
  };
266
379
  // NOTE Find new variant Id by old Id
267
380
  const variantId = this.variantIdList[variantEntry._variant._uid] as string;
@@ -302,7 +415,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
302
415
  }
303
416
 
304
417
  // NOTE Handle the API response here
305
- log.debug(`Waiting for ${allPromise.length} variant entry creation promises to complete`, this.config.context);
418
+ log.debug(`Waiting for ${allPromise.length} variant entry creation promises to complete`, this.config.context);
306
419
  await Promise.allSettled(allPromise);
307
420
  log.debug(`Batch ${batchNo} creation completed`, this.config.context);
308
421
 
@@ -455,7 +568,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
455
568
  ?.filter((ref: any) => ref._content_type_uid === 'sys_assets')
456
569
  .map((ref: any) => ref.path) || [];
457
570
 
458
- log.debug(`Found ${pathsToUpdate.length} file field paths to update`, this.config.context);
571
+ log.debug(`Found ${pathsToUpdate?.length} file field paths to update`, this.config.context);
459
572
  pathsToUpdate.forEach((path: string) => setValue(variantEntry, path.split('.')));
460
573
  }
461
574
 
@@ -485,12 +598,12 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
485
598
  }
486
599
 
487
600
  if (this.failedVariantEntries.has(variantEntryUID)) {
488
- log.debug(`Variant UID not found. Skipping entry variant publish for ${variantEntryUID}`, this.config.context);
601
+ log.info(`Variant UID not found. Skipping entry variant publish for ${variantEntryUID}`, this.config.context);
489
602
  continue;
490
603
  }
491
604
 
492
605
  if (this.environments?.length) {
493
- log.debug('No environment found! Skipping entry variant publishing...', this.config.context);
606
+ log.info('No environment found! Skipping entry variant publishing...', this.config.context);
494
607
  return;
495
608
  }
496
609
 
@@ -510,7 +623,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
510
623
 
511
624
  const { environments, locales } = this.serializePublishEntries(variantEntry);
512
625
  if (environments?.length === 0 || locales?.length === 0) {
513
- log.debug(`Skipping publish for variant ${newVariantUid} - no environments or locales`, this.config.context);
626
+ log.info(`Skipping publish for variant ${newVariantUid} - no environments or locales`, this.config.context);
514
627
  continue;
515
628
  }
516
629
 
package/src/index.ts CHANGED
@@ -2,4 +2,5 @@ export * from './types';
2
2
  export * from './utils';
3
3
  export * from './export';
4
4
  export * from './import';
5
- export * from './messages'
5
+ export * from './messages';
6
+ export * from './utils/constants';
@@ -33,7 +33,6 @@ export type masterLocale = {
33
33
 
34
34
  export interface DefaultConfig {
35
35
  context: Context;
36
- contentVersion: number;
37
36
  versioning: boolean;
38
37
  host: string;
39
38
  cdn?: string;
@@ -233,7 +232,6 @@ export interface DefaultConfig {
233
232
  writeConcurrency: number;
234
233
  developerHubBaseUrl: string;
235
234
  marketplaceAppEncryptionKey: string;
236
- onlyTSModules: string[];
237
235
  }
238
236
 
239
237
  export interface ExportConfig extends DefaultConfig {
@@ -79,7 +79,6 @@ export interface ImportConfig extends ImportDefaultConfig, AnyProperty {
79
79
  authtoken?: string;
80
80
  destinationStackName?: string;
81
81
  org_uid?: string;
82
- contentVersion: number;
83
82
  replaceExisting?: boolean;
84
83
  skipExisting?: boolean;
85
84
  stackName?: string;
@@ -11,7 +11,7 @@ export interface Context {
11
11
  command: string;
12
12
  module: string;
13
13
  userId: string | undefined;
14
- email: string | undefined;
14
+ email?: string | undefined;
15
15
  sessionId: string | undefined;
16
16
  clientId?: string | undefined;
17
17
  apiKey: string;