@contentstack/cli-variants 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/lib/export/attributes.js +27 -10
  2. package/lib/export/audiences.js +28 -10
  3. package/lib/export/events.js +28 -10
  4. package/lib/export/experiences.js +48 -13
  5. package/lib/export/projects.js +24 -6
  6. package/lib/export/variant-entries.js +25 -4
  7. package/lib/import/attribute.d.ts +2 -3
  8. package/lib/import/attribute.js +16 -8
  9. package/lib/import/audiences.d.ts +2 -3
  10. package/lib/import/audiences.js +21 -8
  11. package/lib/import/events.d.ts +3 -4
  12. package/lib/import/events.js +16 -9
  13. package/lib/import/experiences.d.ts +2 -3
  14. package/lib/import/experiences.js +60 -17
  15. package/lib/import/project.d.ts +2 -3
  16. package/lib/import/project.js +11 -6
  17. package/lib/import/variant-entries.js +62 -25
  18. package/lib/types/export-config.d.ts +2 -1
  19. package/lib/types/utils.d.ts +11 -0
  20. package/lib/utils/attributes-helper.js +17 -1
  21. package/lib/utils/audiences-helper.js +37 -6
  22. package/lib/utils/events-helper.js +17 -4
  23. package/lib/utils/personalization-api-adapter.d.ts +2 -1
  24. package/lib/utils/personalization-api-adapter.js +119 -27
  25. package/lib/utils/variant-api-adapter.d.ts +4 -1
  26. package/lib/utils/variant-api-adapter.js +91 -17
  27. package/package.json +5 -2
  28. package/src/export/attributes.ts +34 -10
  29. package/src/export/audiences.ts +35 -7
  30. package/src/export/events.ts +35 -7
  31. package/src/export/experiences.ts +74 -24
  32. package/src/export/projects.ts +31 -7
  33. package/src/export/variant-entries.ts +47 -12
  34. package/src/import/attribute.ts +22 -9
  35. package/src/import/audiences.ts +28 -10
  36. package/src/import/events.ts +21 -10
  37. package/src/import/experiences.ts +74 -20
  38. package/src/import/project.ts +22 -8
  39. package/src/import/variant-entries.ts +116 -40
  40. package/src/types/export-config.ts +2 -1
  41. package/src/types/utils.ts +12 -0
  42. package/src/utils/attributes-helper.ts +21 -2
  43. package/src/utils/audiences-helper.ts +41 -1
  44. package/src/utils/events-helper.ts +19 -1
  45. package/src/utils/personalization-api-adapter.ts +95 -19
  46. package/src/utils/variant-api-adapter.ts +79 -8
@@ -1,31 +1,34 @@
1
1
  import { join, resolve as pResolve } from 'path';
2
2
  import { existsSync, readFileSync } from 'fs';
3
- import { sanitizePath } from '@contentstack/cli-utilities';
3
+ import { sanitizePath, log } from '@contentstack/cli-utilities';
4
4
  import { PersonalizationAdapter, askProjectName, fsUtil } from '../utils';
5
- import { APIConfig, CreateProjectInput, ImportConfig, LogType, ProjectStruct } from '../types';
5
+ import { APIConfig, CreateProjectInput, ImportConfig, ProjectStruct } from '../types';
6
6
 
7
7
  export default class Project extends PersonalizationAdapter<ImportConfig> {
8
8
  private projectMapperFolderPath: string;
9
- constructor(public readonly config: ImportConfig, private readonly log: LogType = console.log) {
9
+
10
+ constructor(public readonly config: ImportConfig) {
10
11
  const conf: APIConfig = {
11
12
  config,
12
13
  baseURL: config.modules.personalize.baseURL[config.region.name],
13
14
  headers: { organization_uid: config.org_uid },
14
15
  };
15
16
  super(Object.assign(config, conf));
17
+
16
18
  this.projectMapperFolderPath = pResolve(
17
19
  sanitizePath(this.config.backupDir),
18
20
  'mapper',
19
21
  sanitizePath(this.config.modules.personalize.dirName),
20
22
  'projects',
21
23
  );
24
+ this.config.context.module = 'project';
22
25
  }
23
26
 
24
27
  /**
25
28
  * The function asynchronously imports projects data from a file and creates projects based on the
26
29
  * data.
27
30
  */
28
- async import() {
31
+ async import() {
29
32
  const personalize = this.config.modules.personalize;
30
33
  const { dirName, fileName } = personalize.projects;
31
34
  const projectPath = join(
@@ -35,17 +38,26 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
35
38
  sanitizePath(fileName),
36
39
  );
37
40
 
41
+ log.debug(`Checking for project file: ${projectPath}`, this.config.context);
42
+
38
43
  if (existsSync(projectPath)) {
39
44
  const projects = JSON.parse(readFileSync(projectPath, 'utf8')) as CreateProjectInput[];
45
+ log.debug(`Loaded ${projects?.length || 0} projects from file`, this.config.context);
40
46
 
41
47
  if (!projects || projects.length < 1) {
42
48
  this.config.modules.personalize.importData = false; // Stop personalize import if stack not connected to any project
43
- this.log(this.config, 'No project found!', 'info');
49
+ log.warn('No projects found in file', this.config.context);
44
50
  return;
45
51
  }
52
+
46
53
  await this.init();
54
+
47
55
  for (const project of projects) {
56
+ log.debug(`Processing project: ${project.name}`, this.config.context);
57
+
48
58
  const createProject = async (newName: void | string): Promise<ProjectStruct> => {
59
+ log.debug(`Creating project with name: ${newName || project.name}`, this.config.context);
60
+
49
61
  return await this.createProject({
50
62
  name: newName || project.name,
51
63
  description: project.description,
@@ -55,10 +67,10 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
55
67
  error.includes('personalization.PROJECTS.DUPLICATE_NAME') ||
56
68
  error.includes('personalize.PROJECTS.DUPLICATE_NAME')
57
69
  ) {
70
+ log.warn(`Project name already exists, generating new name`, this.config.context);
58
71
  const projectName = await askProjectName('Copy Of ' + (newName || project.name));
59
72
  return await createProject(projectName);
60
73
  }
61
- this.log(this.config, this.$t(this.messages.CREATE_FAILURE, { module: 'Projects' }), 'error');
62
74
  throw error;
63
75
  });
64
76
  };
@@ -69,11 +81,13 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
69
81
 
70
82
  await fsUtil.makeDirectory(this.projectMapperFolderPath);
71
83
  fsUtil.writeFile(pResolve(sanitizePath(this.projectMapperFolderPath), 'projects.json'), projectRes);
72
- this.log(this.config, `Project Created Successfully: ${projectRes.uid}`, 'info');
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);
73
87
  }
74
88
  } else {
75
89
  this.config.modules.personalize.importData = false; // Stop personalize import if stack not connected to any project
76
- this.log(this.config, 'No project found!', 'info');
90
+ log.warn(`Project file not found: ${projectPath}`, this.config.context);
77
91
  }
78
92
  }
79
93
  }
@@ -6,7 +6,7 @@ import forEach from 'lodash/forEach';
6
6
  import indexOf from 'lodash/indexOf';
7
7
  import { join, resolve } from 'path';
8
8
  import { readFileSync, existsSync } from 'fs';
9
- import { FsUtility, HttpResponse, sanitizePath } from '@contentstack/cli-utilities';
9
+ import { FsUtility, HttpResponse, sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
10
10
 
11
11
  import VariantAdapter, { VariantHttpClient } from '../utils/variant-api-adapter';
12
12
  import {
@@ -22,7 +22,7 @@ import {
22
22
  CreateVariantEntryDto,
23
23
  PublishVariantEntryDto,
24
24
  } from '../types';
25
- import { formatError, fsUtil, log } from '../utils';
25
+ import { fsUtil } from '../utils';
26
26
 
27
27
  export default class VariantEntries extends VariantAdapter<VariantHttpClient<ImportConfig>> {
28
28
  public entriesDirPath: string;
@@ -54,11 +54,15 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
54
54
  },
55
55
  };
56
56
  super(Object.assign(omit(config, ['helpers']), conf));
57
+
57
58
  this.entriesMapperPath = resolve(sanitizePath(config.backupDir), 'mapper', 'entries');
58
59
  this.personalizeConfig = this.config.modules.personalize;
59
60
  this.entriesDirPath = resolve(sanitizePath(config.backupDir), sanitizePath(config.modules.entries.dirName));
60
61
  this.failedVariantPath = resolve(sanitizePath(this.entriesMapperPath), 'failed-entry-variants.json');
61
62
  this.failedVariantEntries = new Map();
63
+ if (this.config && this.config.context) {
64
+ this.config.context.module = 'variant-entries';
65
+ }
62
66
  }
63
67
 
64
68
  /**
@@ -79,20 +83,23 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
79
83
  'variants-uid-mapping.json',
80
84
  );
81
85
 
86
+ log.debug(`Checking for variant entry data file: ${filePath}`, this.config.context);
82
87
  if (!existsSync(filePath)) {
83
- log(this.config, this.messages.VARIANT_ENTRY_FILE_NOT_FOUND, 'info');
88
+ log.warn(`Variant entry data file not found at path: ${filePath}, skipping import`, this.config.context);
84
89
  return;
85
90
  }
86
91
 
92
+ log.debug(`Checking for variant ID mapping file: ${variantIdPath}`, this.config.context);
87
93
  if (!existsSync(variantIdPath)) {
88
- log(this.config, this.messages.EMPTY_VARIANT_UID_DATA, 'error');
94
+ log.error('Variant UID mapping file not found', this.config.context);
89
95
  return;
90
96
  }
91
97
 
92
98
  const entriesForVariants = fsUtil.readFile(filePath, true) as EntryDataForVariantEntries[];
99
+ log.debug(`Loaded ${entriesForVariants?.length || 0} entries for variant processing`, this.config.context);
93
100
 
94
101
  if (isEmpty(entriesForVariants)) {
95
- log(this.config, this.messages.IMPORT_ENTRY_NOT_FOUND, 'info');
102
+ log.warn('No entries found for variant import', this.config.context);
96
103
  return;
97
104
  }
98
105
 
@@ -113,10 +120,13 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
113
120
  'uid-mapping.json',
114
121
  );
115
122
  const envPath = resolve(sanitizePath(this.config.backupDir), 'environments', 'environments.json');
123
+
124
+ log.debug('Loading variant ID mapping and dependency data', this.config.context);
125
+
116
126
  // NOTE Read and store list of variant IDs
117
127
  this.variantIdList = (fsUtil.readFile(variantIdPath, true) || {}) as Record<string, unknown>;
118
128
  if (isEmpty(this.variantIdList)) {
119
- log(this.config, this.messages.EMPTY_VARIANT_UID_DATA, 'info');
129
+ log.warn('Empty variant UID data found', this.config.context);
120
130
  return;
121
131
  }
122
132
 
@@ -128,12 +138,23 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
128
138
  this.assetUidMapper = (fsUtil.readFile(assetUidMapperPath, true) || {}) as Record<string, any>;
129
139
  this.assetUrlMapper = (fsUtil.readFile(assetUrlMapperPath, true) || {}) as Record<string, any>;
130
140
  this.environments = (fsUtil.readFile(envPath, true) || {}) as Record<string, any>;
141
+
142
+ 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}`,
146
+ this.config.context,
147
+ );
148
+
131
149
  // set the token
132
150
  await this.variantInstance.init();
151
+
152
+ log.info(`Processing ${entriesForVariants.length} entries for variant import`, this.config.context);
133
153
  for (const entriesForVariant of entriesForVariants) {
134
154
  await this.importVariantEntries(entriesForVariant);
135
155
  }
136
- log(this.config, 'All the entries variants have been imported & published successfully', 'success');
156
+
157
+ log.success('All variant entries have been imported and published successfully', this.config.context);
137
158
  }
138
159
 
139
160
  /**
@@ -144,6 +165,9 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
144
165
  async importVariantEntries(entriesForVariant: EntryDataForVariantEntries) {
145
166
  const variantEntry = this.config.modules.variantEntry;
146
167
  const { content_type, locale, entry_uid } = entriesForVariant;
168
+
169
+ log.debug(`Importing variant entries for: ${content_type}/${locale}/${entry_uid}`, this.config.context);
170
+
147
171
  const ctConfig = this.config.modules['content-types'];
148
172
  const contentType: ContentTypeStruct = JSON.parse(
149
173
  readFileSync(
@@ -155,6 +179,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
155
179
  'utf8',
156
180
  ),
157
181
  );
182
+
158
183
  const variantEntryBasePath = join(
159
184
  sanitizePath(this.entriesDirPath),
160
185
  sanitizePath(content_type),
@@ -162,16 +187,23 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
162
187
  sanitizePath(variantEntry.dirName),
163
188
  sanitizePath(entry_uid),
164
189
  );
190
+
191
+ log.debug(`Processing variant entries from: ${variantEntryBasePath}`, this.config.context);
165
192
  const fs = new FsUtility({ basePath: variantEntryBasePath, createDirIfNotExist: false });
166
193
 
167
194
  for (const _ in fs.indexFileContent) {
168
195
  try {
169
196
  const variantEntries = (await fs.readChunkFiles.next()) as VariantEntryStruct[];
170
197
  if (variantEntries?.length) {
198
+ log.debug(`Processing batch of ${variantEntries.length} variant entries`, this.config.context);
171
199
  await this.handleConcurrency(contentType, variantEntries, entriesForVariant);
172
200
  }
173
201
  } catch (error) {
174
- log(this.config, error, 'error');
202
+ handleAndLogError(
203
+ error,
204
+ this.config.context,
205
+ `Failed to process variant entries for ${content_type}/${locale}/${entry_uid}`,
206
+ );
175
207
  }
176
208
  }
177
209
  }
@@ -200,35 +232,44 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
200
232
  const { content_type, locale, entry_uid } = entriesForVariant;
201
233
  const entryUid = this.entriesUidMapper[entry_uid];
202
234
  const batches = chunk(variantEntries, variantEntryConfig.apiConcurrency || 5);
203
-
204
235
  if (isEmpty(batches)) return;
205
236
 
237
+ log.debug(`Starting concurrent processing for ${variantEntries.length} variant entries`, this.config.context);
238
+
206
239
  for (const [, batch] of entries(batches)) {
207
240
  batchNo += 1;
208
241
  const allPromise = [];
209
242
  const start = Date.now();
210
243
 
244
+ log.debug(
245
+ `Processing batch ${batchNo}/${batches.length} with ${batch.length} variant entries`,
246
+ this.config.context,
247
+ );
248
+
211
249
  for (let [, variantEntry] of entries(batch)) {
212
- const onSuccess = ({ response, apiData: { entryUid, variantUid }, log }: any) => {
213
- log(
214
- this.config,
250
+ const onSuccess = ({ response, apiData: { entryUid, variantUid } }: any) => {
251
+ log.info(
215
252
  `Created entry variant: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
216
- 'info',
253
+ this.config.context,
217
254
  );
218
255
  };
219
256
 
220
- const onReject = ({ error, apiData, log }: any) => {
257
+ const onReject = ({ error, apiData }: any) => {
221
258
  const { entryUid, variantUid } = apiData;
222
259
  this.failedVariantEntries.set(variantUid, apiData);
223
- log(
224
- this.config,
260
+ handleAndLogError(
261
+ error,
262
+ this.config.context,
225
263
  `Failed to create entry variant: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
226
- 'error',
227
264
  );
228
- log(this.config, error, 'error');
229
265
  };
230
266
  // NOTE Find new variant Id by old Id
231
267
  const variantId = this.variantIdList[variantEntry._variant._uid] as string;
268
+ log.debug(
269
+ `Looking up variant ID for ${variantEntry._variant._uid}: ${variantId ? 'found' : 'not found'}`,
270
+ this.config.context,
271
+ );
272
+
232
273
  // NOTE Replace all the relation data UID's
233
274
  variantEntry = this.handleVariantEntryRelationalData(contentType, variantEntry);
234
275
  const changeSet = this.serializeChangeSet(variantEntry);
@@ -238,6 +279,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
238
279
  };
239
280
 
240
281
  if (variantId) {
282
+ log.debug(`Creating variant entry for variant ID: ${variantId}`, this.config.context);
241
283
  const promise = this.variantInstance.createVariantEntry(
242
284
  createVariantReq,
243
285
  {
@@ -250,25 +292,29 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
250
292
  reject: onReject.bind(this),
251
293
  resolve: onSuccess.bind(this),
252
294
  variantUid: variantId,
253
- log: log,
254
295
  },
255
296
  );
256
297
 
257
298
  allPromise.push(promise);
258
299
  } else {
259
- log(this.config, this.messages.VARIANT_ID_NOT_FOUND, 'error');
300
+ log.error(`Variant ID not found for ${variantEntry._variant._uid}`, this.config.context);
260
301
  }
261
302
  }
262
303
 
263
304
  // NOTE Handle the API response here
305
+ log.debug(`Waiting for ${allPromise.length} variant entry creation promises to complete`, this.config.context);
264
306
  await Promise.allSettled(allPromise);
307
+ log.debug(`Batch ${batchNo} creation completed`, this.config.context);
308
+
265
309
  // NOTE publish all the entries
266
310
  await this.publishVariantEntries(batch, entryUid, content_type);
267
311
  const end = Date.now();
268
312
  const exeTime = end - start;
313
+ log.debug(`Batch ${batchNo} completed in ${exeTime}ms`, this.config.context);
269
314
  this.variantInstance.delay(1000 - exeTime);
270
315
  }
271
316
 
317
+ log.debug(`Writing failed variant entries to: ${this.failedVariantPath}`, this.config.context);
272
318
  fsUtil.writeFile(this.failedVariantPath, this.failedVariantEntries);
273
319
  }
274
320
 
@@ -309,6 +355,8 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
309
355
  contentType: ContentTypeStruct,
310
356
  variantEntry: VariantEntryStruct,
311
357
  ): VariantEntryStruct {
358
+ log.debug(`Processing relational data for variant entry: ${variantEntry.uid}`, this.config.context);
359
+
312
360
  if (this.config.helpers) {
313
361
  const { lookUpTerms, lookupAssets, lookupExtension, lookupEntries, restoreJsonRteEntryRefs } =
314
362
  this.config.helpers;
@@ -317,10 +365,12 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
317
365
  // Feel free to remove this flow if it's not valid
318
366
  // NOTE Find and replace extension's UID
319
367
  if (lookupExtension) {
368
+ log.debug('Processing extension lookups for variant entry', this.config.context);
320
369
  lookupExtension(this.config, contentType.schema, this.config.preserveStackVersion, this.installedExtensions);
321
370
  }
322
371
 
323
372
  // NOTE Find and replace RTE Ref UIDs
373
+ log.debug('Processing RTE reference lookups for variant entry', this.config.context);
324
374
  variantEntry = restoreJsonRteEntryRefs(variantEntry, variantEntry, contentType.schema, {
325
375
  uidMapper: this.entriesUidMapper,
326
376
  mappedAssetUids: this.assetUidMapper,
@@ -328,6 +378,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
328
378
  }) as VariantEntryStruct;
329
379
 
330
380
  // NOTE Find and replace Entry Ref UIDs
381
+ log.debug('Processing entry reference lookups for variant entry', this.config.context);
331
382
  variantEntry = lookupEntries(
332
383
  {
333
384
  entry: variantEntry,
@@ -340,12 +391,15 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
340
391
  // NOTE: will remove term if term doesn't exists in taxonomy
341
392
  // FIXME: Validate if taxonomy support available for variant entries,
342
393
  // if not, feel free to remove this lookup flow.
394
+ log.debug('Processing taxonomy term lookups for variant entry', this.config.context);
343
395
  lookUpTerms(contentType.schema, variantEntry, this.taxonomies, this.config);
344
396
 
345
397
  // update file fields of entry variants to support lookup asset logic
398
+ log.debug('Updating file fields for variant entry', this.config.context);
346
399
  this.updateFileFields(variantEntry);
347
400
 
348
401
  // NOTE Find and replace asset's UID
402
+ log.debug('Processing asset lookups for variant entry', this.config.context);
349
403
  variantEntry = lookupAssets(
350
404
  {
351
405
  entry: variantEntry,
@@ -367,6 +421,8 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
367
421
  * @param variantEntry - The entry variant to update.
368
422
  */
369
423
  updateFileFields(variantEntry: VariantEntryStruct) {
424
+ log.debug(`Updating file fields for variant entry: ${variantEntry.uid}`, this.config.context);
425
+
370
426
  const setValue = (currentObj: VariantEntryStruct, keys: string[]) => {
371
427
  if (!currentObj || keys.length === 0) return;
372
428
 
@@ -399,6 +455,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
399
455
  ?.filter((ref: any) => ref._content_type_uid === 'sys_assets')
400
456
  .map((ref: any) => ref.path) || [];
401
457
 
458
+ log.debug(`Found ${pathsToUpdate.length} file field paths to update`, this.config.context);
402
459
  pathsToUpdate.forEach((path: string) => setValue(variantEntry, path.split('.')));
403
460
  }
404
461
 
@@ -411,55 +468,58 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
411
468
  */
412
469
  async publishVariantEntries(batch: VariantEntryStruct[], entryUid: string, content_type: string) {
413
470
  const allPromise = [];
414
- log(
415
- this.config,
471
+ log.info(
416
472
  `Publishing variant entries for entry uid '${entryUid}' of Content Type '${content_type}'`,
417
- 'info',
473
+ this.config.context,
418
474
  );
475
+ log.debug(`Processing ${batch.length} variant entries for publishing`, this.config.context);
476
+
419
477
  for (let [, variantEntry] of entries(batch)) {
420
478
  const variantEntryUID = variantEntry.uid;
421
479
  const oldVariantUid = variantEntry._variant._uid || '';
422
480
  const newVariantUid = this.variantIdList[oldVariantUid] as string;
423
481
 
424
482
  if (!newVariantUid) {
425
- log(this.config, `${this.messages.VARIANT_ID_NOT_FOUND}. Skipping entry variant publish`, 'info');
483
+ log.debug('Variant ID not found', this.config.context);
426
484
  continue;
427
485
  }
428
486
 
429
487
  if (this.failedVariantEntries.has(variantEntryUID)) {
430
- log(
431
- this.config,
432
- `${this.messages.VARIANT_UID_NOT_FOUND}. Skipping entry variant publish for ${variantEntryUID}`,
433
- 'info',
434
- );
488
+ log.debug(`Variant UID not found. Skipping entry variant publish for ${variantEntryUID}`, this.config.context);
435
489
  continue;
436
490
  }
437
491
 
438
492
  if (this.environments?.length) {
439
- log(this.config, 'No environment found! Skipping entry variant publishing...', 'info');
493
+ log.debug('No environment found! Skipping entry variant publishing...', this.config.context);
440
494
  return;
441
495
  }
442
496
 
443
- const onSuccess = ({ response, apiData: { entryUid, variantUid }, log }: any) => {
444
- log(
445
- this.config,
497
+ const onSuccess = ({ response, apiData: { entryUid, variantUid } }: any) => {
498
+ log.info(
446
499
  `Entry variant: '${variantUid}' of entry '${entryUid}' published on locales '${locales.join(',')}'`,
447
- 'info',
500
+ this.config.context,
448
501
  );
449
502
  };
450
- const onReject = ({ error, apiData: { entryUid, variantUid }, log }: any) => {
451
- log(
452
- this.config,
503
+ const onReject = ({ error, apiData: { entryUid, variantUid } }: any) => {
504
+ handleAndLogError(
505
+ error,
506
+ this.config.context,
453
507
  `Failed to publish entry variant: '${variantUid}' of entry uid ${entryUid} on locales '${locales.join(',')}'`,
454
- 'error',
455
508
  );
456
- log(this.config, formatError(error), 'error');
457
509
  };
458
510
 
459
511
  const { environments, locales } = this.serializePublishEntries(variantEntry);
460
512
  if (environments?.length === 0 || locales?.length === 0) {
513
+ log.debug(`Skipping publish for variant ${newVariantUid} - no environments or locales`, this.config.context);
461
514
  continue;
462
515
  }
516
+
517
+ log.debug(
518
+ `Publishing variant ${newVariantUid} to environments: ${environments.join(', ')}, locales: ${locales.join(
519
+ ', ',
520
+ )}`,
521
+ this.config.context,
522
+ );
463
523
  const publishReq: PublishVariantEntryDto = {
464
524
  entry: {
465
525
  environments,
@@ -486,7 +546,10 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
486
546
  allPromise.push(promise);
487
547
  }
488
548
  await Promise.allSettled(allPromise);
489
- log(this.config, `Published variant entries for entry uid '${entryUid}' of Content Type '${content_type}'`, 'info');
549
+ log.info(
550
+ `Published variant entries for entry uid '${entryUid}' of Content Type '${content_type}'`,
551
+ this.config.context,
552
+ );
490
553
  }
491
554
 
492
555
  /**
@@ -498,6 +561,8 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
498
561
  environments: Array<string>;
499
562
  locales: Array<string>;
500
563
  } {
564
+ log.debug(`Serializing publish entries for variant: ${variantEntry.uid}`, this.config.context);
565
+
501
566
  const requestObject: {
502
567
  environments: Array<string>;
503
568
  locales: Array<string>;
@@ -505,6 +570,12 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
505
570
  environments: [],
506
571
  locales: [],
507
572
  };
573
+ if (variantEntry.publish_details && variantEntry.publish_details?.length > 0) {
574
+ log.debug(`Processing ${variantEntry.publish_details.length} publish details`, this.config.context);
575
+ } else {
576
+ log.debug('No publish details found for variant entry', this.config.context);
577
+ }
578
+
508
579
  if (variantEntry.publish_details && variantEntry.publish_details?.length > 0) {
509
580
  forEach(variantEntry.publish_details, (pubObject) => {
510
581
  if (
@@ -518,6 +589,11 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
518
589
  }
519
590
  });
520
591
  }
592
+
593
+ log.debug(
594
+ `Serialized publish data - environments: ${requestObject.environments.length}, locales: ${requestObject.locales.length}`,
595
+ this.config.context,
596
+ );
521
597
  return requestObject;
522
598
  }
523
599
  }
@@ -3,7 +3,7 @@
3
3
  * because it will create a circular dependency and cause the prepack/build command to fail.
4
4
  * Therefore, we are duplicating the following types from the export.
5
5
  */
6
- import { AnyProperty } from './utils';
6
+ import { AnyProperty, Context } from './utils';
7
7
 
8
8
  export type Modules =
9
9
  | 'stack'
@@ -32,6 +32,7 @@ export type masterLocale = {
32
32
  };
33
33
 
34
34
  export interface DefaultConfig {
35
+ context: Context;
35
36
  contentVersion: number;
36
37
  versioning: boolean;
37
38
  host: string;
@@ -6,3 +6,15 @@ export interface LogType {
6
6
  (message: any): void;
7
7
  (config: any, message: any, type: 'info' | 'error' | 'success'): void;
8
8
  }
9
+
10
+ export interface Context {
11
+ command: string;
12
+ module: string;
13
+ userId: string | undefined;
14
+ email: string | undefined;
15
+ sessionId: string | undefined;
16
+ clientId?: string | undefined;
17
+ apiKey: string;
18
+ orgId: string;
19
+ authMethod?: string;
20
+ }
@@ -1,3 +1,5 @@
1
+ import { log } from '@contentstack/cli-utilities';
2
+
1
3
  /**
2
4
  * function to either modify the UID or eliminate it if the attribute is not created in the target project
3
5
  * @param attributeRules
@@ -5,27 +7,44 @@
5
7
  * @returns
6
8
  */
7
9
  export const lookUpAttributes = (attributeRules: Record<string, any>[], attributesUid: Record<string, string>) => {
8
- for (let index =0; index< attributeRules?.length; index++) {
10
+ log.debug(`Looking up attributes in ${attributeRules?.length || 0} rules`);
11
+ log.debug(`Available attribute mappings: ${Object.keys(attributesUid).length}`);
12
+
13
+ for (let index = 0; index < attributeRules?.length; index++) {
9
14
  const rule = attributeRules[index];
15
+ log.debug(`Processing rule ${index + 1}/${attributeRules.length} of type: ${rule['__type']}`);
10
16
 
11
17
  if (rule['__type'] === 'Rule') {
12
18
  // Check if attribute reference exists in attributesUid
13
19
  const attributeRef = rule.attribute?.ref;
14
20
  const attributeType = rule.attribute['__type'];
21
+
22
+ log.debug(`Rule attribute type: ${attributeType}, reference: ${attributeRef}`);
23
+
15
24
  // check if type is CustomAttributeReference
16
25
  if (attributeType === 'CustomAttributeReference') {
17
26
  if (attributeRef && attributesUid.hasOwnProperty(attributeRef) && attributesUid[attributeRef]) {
18
- rule.attribute.ref = attributesUid[attributeRef];
27
+ const newAttributeRef = attributesUid[attributeRef];
28
+ log.debug(`Mapping attribute reference: ${attributeRef} -> ${newAttributeRef}`);
29
+ rule.attribute.ref = newAttributeRef;
19
30
  } else {
31
+ log.warn(`Attribute reference not found in mapping: ${attributeRef}. Removing rule.`);
20
32
  // Remove the rule if the attribute reference is not found
21
33
  attributeRules.splice(index, 1);
22
34
  --index;
23
35
  }
36
+ } else {
37
+ log.debug(`Skipping non-custom attribute reference: ${attributeType}`);
24
38
  }
25
39
  } else if (rule['__type'] === 'RuleCombination' && Array.isArray(rule.rules)) {
40
+ log.debug(`Processing nested rule combination with ${rule.rules.length} sub-rules`);
26
41
  // Recursively look up attributes in nested rule combinations
27
42
  lookUpAttributes(rule.rules, attributesUid);
43
+ } else {
44
+ log.debug(`Skipping rule of type: ${rule['__type']}`);
28
45
  }
29
46
  }
47
+
48
+ log.debug(`Attribute lookup completed. Final rule count: ${attributeRules.length}`);
30
49
  return attributeRules;
31
50
  };