@contentstack/cli-cm-export-to-csv 1.9.2 → 1.10.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.
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@contentstack/cli-cm-export-to-csv",
3
3
  "description": "Export entities to csv",
4
- "version": "1.9.2",
4
+ "version": "1.10.0",
5
5
  "author": "Abhinav Gupta @abhinav-from-contentstack",
6
6
  "bugs": "https://github.com/contentstack/cli/issues",
7
7
  "dependencies": {
8
8
  "@contentstack/cli-command": "~1.6.1",
9
- "@contentstack/cli-utilities": "~1.15.0",
9
+ "@contentstack/cli-utilities": "~1.14.4",
10
10
  "@oclif/core": "^4.3.0",
11
11
  "@oclif/plugin-help": "^6.2.32",
12
12
  "fast-csv": "^4.3.6",
@@ -7,7 +7,6 @@ const {
7
7
  cliux,
8
8
  doesBranchExist,
9
9
  isManagementTokenValid,
10
- log
11
10
  } = require('@contentstack/cli-utilities');
12
11
  const util = require('../../util');
13
12
  const config = require('../../util/config');
@@ -18,7 +17,8 @@ class ExportToCsvCommand extends Command {
18
17
  required: false,
19
18
  multiple: false,
20
19
  options: ['entries', 'users', 'teams', 'taxonomies'],
21
- description: 'Option to export data (entries, users, teams, taxonomies). <options: entries|users|teams|taxonomies>',
20
+ description:
21
+ 'Option to export data (entries, users, teams, taxonomies). <options: entries|users|teams|taxonomies>',
22
22
  }),
23
23
  alias: flags.string({
24
24
  char: 'a',
@@ -67,12 +67,22 @@ class ExportToCsvCommand extends Command {
67
67
  'taxonomy-uid': flags.string({
68
68
  description: 'Provide the taxonomy UID of the related terms you want to export.',
69
69
  }),
70
+ 'include-fallback': flags.boolean({
71
+ description:
72
+ "[Optional] Include fallback locale data when exporting taxonomies. When enabled, if a taxonomy term doesn't exist in the specified locale, it will fallback to the hierarchy defined in the branch settings.",
73
+ default: false,
74
+ }),
75
+ 'fallback-locale': flags.string({
76
+ description:
77
+ "[Optional] Specify a specific fallback locale for taxonomy export. This locale will be used when a taxonomy term doesn't exist in the primary locale. Takes priority over branch fallback hierarchy when both are specified.",
78
+ required: false,
79
+ }),
70
80
  delimiter: flags.string({
71
- description: '[optional] Provide a delimiter to separate individual data fields within the CSV file. For example: cm:export-to-csv --delimiter \'|\'',
81
+ description:
82
+ "[optional] Provide a delimiter to separate individual data fields within the CSV file. For example: cm:export-to-csv --delimiter '|'",
72
83
  default: ',',
73
84
  }),
74
- };
75
-
85
+ };
76
86
  async run() {
77
87
  try {
78
88
  let action, managementAPIClient;
@@ -87,9 +97,11 @@ class ExportToCsvCommand extends Command {
87
97
  'content-type': contentTypesFlag,
88
98
  alias: managementTokenAlias,
89
99
  branch: branchUid,
90
- "team-uid": teamUid,
100
+ 'team-uid': teamUid,
91
101
  'taxonomy-uid': taxonomyUID,
92
- delimiter
102
+ 'include-fallback': includeFallback,
103
+ 'fallback-locale': fallbackLocale,
104
+ delimiter,
93
105
  },
94
106
  } = await this.parse(ExportToCsvCommand);
95
107
 
@@ -127,7 +139,12 @@ class ExportToCsvCommand extends Command {
127
139
  }
128
140
 
129
141
  stackAPIClient = this.getStackClient(managementAPIClient, stack);
130
- stackAPIClient = await this.checkAndUpdateBranchDetail(branchUid, stack, stackAPIClient, managementAPIClient);
142
+ stackAPIClient = await this.checkAndUpdateBranchDetail(
143
+ branchUid,
144
+ stack,
145
+ stackAPIClient,
146
+ managementAPIClient,
147
+ );
131
148
 
132
149
  const contentTypeCount = await util.getContentTypeCount(stackAPIClient);
133
150
 
@@ -223,15 +240,15 @@ class ExportToCsvCommand extends Command {
223
240
  }
224
241
  case config.exportTeams:
225
242
  case 'teams': {
226
- try{
243
+ try {
227
244
  let organization;
228
245
  if (org) {
229
246
  organization = { uid: org, name: orgName || org };
230
247
  } else {
231
248
  organization = await util.chooseOrganization(managementAPIClient, action); // prompt for organization
232
249
  }
233
-
234
- await util.exportTeams(managementAPIClient,organization,teamUid, delimiter);
250
+
251
+ await util.exportTeams(managementAPIClient, organization, teamUid, delimiter);
235
252
  } catch (error) {
236
253
  if (error.message || error.errorMessage) {
237
254
  cliux.error(util.formatError(error));
@@ -242,7 +259,11 @@ class ExportToCsvCommand extends Command {
242
259
  case config.exportTaxonomies:
243
260
  case 'taxonomies': {
244
261
  let stack;
262
+ let language;
245
263
  let stackAPIClient;
264
+ let finalIncludeFallback = includeFallback;
265
+ let finalFallbackLocale = fallbackLocale;
266
+
246
267
  if (managementTokenAlias) {
247
268
  const { stackDetails, apiClient } = await this.getAliasDetails(managementTokenAlias, stackName);
248
269
  managementAPIClient = apiClient;
@@ -252,7 +273,27 @@ class ExportToCsvCommand extends Command {
252
273
  }
253
274
 
254
275
  stackAPIClient = this.getStackClient(managementAPIClient, stack);
255
- await this.createTaxonomyAndTermCsvFile(stackAPIClient, stackName, stack, taxonomyUID, delimiter);
276
+ if (locale) {
277
+ language = { code: locale };
278
+ } else {
279
+ language = await util.chooseLanguage(stackAPIClient);
280
+ }
281
+
282
+ // if (includeFallback === undefined || fallbackLocale === undefined) {
283
+ // const fallbackOptions = await util.chooseFallbackOptions(stackAPIClient);
284
+ // }
285
+
286
+
287
+ if (fallbackLocale !== undefined) {
288
+ finalFallbackLocale = fallbackLocale;
289
+ }
290
+
291
+ await this.createTaxonomyAndTermCsvFile(stackAPIClient, stackName, stack, taxonomyUID, delimiter, {
292
+ locale: language.code,
293
+ branch: branchUid,
294
+ include_fallback: finalIncludeFallback,
295
+ fallback_locale: finalFallbackLocale,
296
+ });
256
297
  break;
257
298
  }
258
299
  }
@@ -287,7 +328,7 @@ class ExportToCsvCommand extends Command {
287
328
  .query()
288
329
  .find()
289
330
  .then(({ items }) => (items !== undefined ? items : []))
290
- .catch((_err) => {});
331
+ .catch(() => {});
291
332
  }
292
333
 
293
334
  /**
@@ -335,9 +376,14 @@ class ExportToCsvCommand extends Command {
335
376
  let apiClient, stackDetails;
336
377
  const listOfTokens = configHandler.get('tokens');
337
378
  if (managementTokenAlias && listOfTokens[managementTokenAlias]) {
338
- const checkManagementTokenValidity = await isManagementTokenValid((listOfTokens[managementTokenAlias].apiKey) ,listOfTokens[managementTokenAlias].token);
339
- if(checkManagementTokenValidity.hasOwnProperty('message')) {
340
- throw checkManagementTokenValidity.valid==='failedToCheck'?checkManagementTokenValidity.message:(`error: Management token or stack API key is invalid. ${checkManagementTokenValidity.message}`);
379
+ const checkManagementTokenValidity = await isManagementTokenValid(
380
+ listOfTokens[managementTokenAlias].apiKey,
381
+ listOfTokens[managementTokenAlias].token,
382
+ );
383
+ if (Object.prototype.hasOwnProperty.call(checkManagementTokenValidity, 'message')) {
384
+ throw checkManagementTokenValidity.valid === 'failedToCheck'
385
+ ? checkManagementTokenValidity.message
386
+ : `error: Management token or stack API key is invalid. ${checkManagementTokenValidity.message}`;
341
387
  }
342
388
  apiClient = await managementSDKClient({
343
389
  host: this.cmaHost,
@@ -393,13 +439,12 @@ class ExportToCsvCommand extends Command {
393
439
  * @param {object} stack
394
440
  * @param {string} taxUID
395
441
  */
396
- async createTaxonomyAndTermCsvFile(stackAPIClient, stackName, stack, taxUID, delimiter) {
397
- //TODO: Temp variable to export taxonomies in importable format will replaced with flag once decided
398
- const importableCSV = true;
442
+ async createTaxonomyAndTermCsvFile(stackAPIClient, stackName, stack, taxUID, delimiter, localeOptions = {}) {
399
443
  const payload = {
400
444
  stackAPIClient,
401
445
  type: '',
402
446
  limit: config.limit || 100,
447
+ ...localeOptions, // Spread locale, branch, include_fallback, fallback_locale
403
448
  };
404
449
  //check whether the taxonomy is valid or not
405
450
  let taxonomies = [];
@@ -411,36 +456,13 @@ class ExportToCsvCommand extends Command {
411
456
  taxonomies = await util.getAllTaxonomies(payload);
412
457
  }
413
458
 
414
- if (!importableCSV) {
415
- const formattedTaxonomiesData = util.formatTaxonomiesData(taxonomies);
416
- if (formattedTaxonomiesData?.length) {
417
- const fileName = `${stackName ? stackName : stack.name}_taxonomies.csv`;
418
- util.write(this, formattedTaxonomiesData, fileName, 'taxonomies', delimiter);
419
- } else {
420
- cliux.print('info: No taxonomies found! Please provide a valid stack.', { color: 'blue' });
421
- }
422
-
423
- for (let index = 0; index < taxonomies?.length; index++) {
424
- const taxonomy = taxonomies[index];
425
- const taxonomyUID = taxonomy?.uid;
426
- if (taxonomyUID) {
427
- payload['taxonomyUID'] = taxonomyUID;
428
- const terms = await util.getAllTermsOfTaxonomy(payload);
429
- const formattedTermsData = util.formatTermsOfTaxonomyData(terms, taxonomyUID);
430
- const taxonomyName = taxonomy?.name ?? '';
431
- const termFileName = `${stackName ?? stack.name}_${taxonomyName}_${taxonomyUID}_terms.csv`;
432
- if (formattedTermsData?.length) {
433
- util.write(this, formattedTermsData, termFileName, 'terms', delimiter);
434
- } else {
435
- cliux.print(`info: No terms found for the taxonomy UID - '${taxonomyUID}'!`, { color: 'blue' });
436
- }
437
- }
438
- }
459
+ if (!taxonomies?.length) {
460
+ cliux.print('info: No taxonomies found!', { color: 'blue' });
439
461
  } else {
440
462
  const fileName = `${stackName ?? stack.name}_taxonomies.csv`;
441
463
  const { taxonomiesData, headers } = await util.createImportableCSV(payload, taxonomies);
442
464
  if (taxonomiesData?.length) {
443
- util.write(this, taxonomiesData, fileName, 'taxonomies',delimiter, headers);
465
+ util.write(this, taxonomiesData, fileName, 'taxonomies', delimiter, headers);
444
466
  }
445
467
  }
446
468
  }
@@ -486,6 +508,16 @@ ExportToCsvCommand.examples = [
486
508
  '',
487
509
  'Exporting taxonomies and respective terms to a .CSV file with a delimiter',
488
510
  'csdx cm:export-to-csv --action <taxonomies> --alias <management-token-alias> --delimiter <delimiter>',
511
+ '',
512
+ 'Exporting taxonomies with specific locale',
513
+ 'csdx cm:export-to-csv --action <taxonomies> --alias <management-token-alias> --locale <locale>',
514
+ '',
515
+ 'Exporting taxonomies with fallback locale support',
516
+ 'csdx cm:export-to-csv --action <taxonomies> --alias <management-token-alias> --locale <locale> --include-fallback',
517
+ '',
518
+ 'Exporting taxonomies with custom fallback locale',
519
+ 'csdx cm:export-to-csv --action <taxonomies> --alias <management-token-alias> --locale <locale> --include-fallback --fallback-locale <fallback-locale>',
520
+ '',
489
521
  ];
490
522
 
491
523
  module.exports = ExportToCsvCommand;
package/src/util/index.js CHANGED
@@ -485,6 +485,50 @@ function startupQuestions() {
485
485
  });
486
486
  }
487
487
 
488
+ function chooseFallbackOptions(stackAPIClient) {
489
+ return new Promise(async (resolve, reject) => {
490
+ try {
491
+ const questions = [
492
+ {
493
+ type: 'confirm',
494
+ name: 'includeFallback',
495
+ message: 'Include fallback locale data when exporting taxonomies?',
496
+ default: false,
497
+ },
498
+ ];
499
+
500
+ const { includeFallback } = await inquirer.prompt(questions);
501
+
502
+ let fallbackLocale = null;
503
+
504
+ if (includeFallback) {
505
+ // Get available languages for fallback locale selection
506
+ const languages = await getLanguages(stackAPIClient);
507
+ const languagesList = Object.keys(languages);
508
+
509
+ const fallbackQuestion = [
510
+ {
511
+ type: 'list',
512
+ name: 'selectedFallbackLocale',
513
+ message: 'Choose fallback locale',
514
+ choices: languagesList,
515
+ },
516
+ ];
517
+
518
+ const { selectedFallbackLocale } = await inquirer.prompt(fallbackQuestion);
519
+ fallbackLocale = languages[selectedFallbackLocale];
520
+ }
521
+
522
+ resolve({
523
+ includeFallback,
524
+ fallbackLocale,
525
+ });
526
+ } catch (error) {
527
+ reject(error);
528
+ }
529
+ });
530
+ }
531
+
488
532
  function getOrgUsers(managementAPIClient, orgUid) {
489
533
  return new Promise((resolve, reject) => {
490
534
  managementAPIClient
@@ -1080,11 +1124,17 @@ async function getTaxonomy(payload) {
1080
1124
  * @returns {*} Promise<any>
1081
1125
  */
1082
1126
  async function taxonomySDKHandler(payload, skip) {
1083
- const { stackAPIClient, taxonomyUID, type, format } = payload;
1127
+ const { stackAPIClient, taxonomyUID, type, format, locale, branch, include_fallback, fallback_locale } = payload;
1084
1128
 
1085
1129
  const queryParams = { include_count: true, limit: payload.limit };
1086
1130
  if (skip >= 0) queryParams['skip'] = skip || 0;
1087
1131
 
1132
+ // Add locale and branch parameters if provided
1133
+ if (locale) queryParams['locale'] = locale;
1134
+ if (branch) queryParams['branch'] = branch;
1135
+ if (include_fallback !== undefined) queryParams['include_fallback'] = include_fallback;
1136
+ if (fallback_locale) queryParams['fallback_locale'] = fallback_locale;
1137
+
1088
1138
  switch (type) {
1089
1139
  case 'taxonomies':
1090
1140
  return await stackAPIClient
@@ -1109,9 +1159,15 @@ async function taxonomySDKHandler(payload, skip) {
1109
1159
  .then((data) => data)
1110
1160
  .catch((err) => handleTaxonomyErrorMsg(err));
1111
1161
  case 'export-taxonomies':
1162
+ const exportParams = { format };
1163
+ if (locale) exportParams.locale = locale;
1164
+ if (branch) exportParams.branch = branch;
1165
+ if (include_fallback !== undefined) exportParams.include_fallback = include_fallback;
1166
+ if (fallback_locale) exportParams.fallback_locale = fallback_locale;
1167
+
1112
1168
  return await stackAPIClient
1113
1169
  .taxonomy(taxonomyUID)
1114
- .export({ format })
1170
+ .export(exportParams)
1115
1171
  .then((data) => data)
1116
1172
  .catch((err) => handleTaxonomyErrorMsg(err));
1117
1173
  default:
@@ -1176,20 +1232,20 @@ function handleTaxonomyErrorMsg(err) {
1176
1232
  * @returns
1177
1233
  */
1178
1234
  async function createImportableCSV(payload, taxonomies) {
1179
- let taxonomiesData = [];
1180
- let headers = [];
1181
- payload['type'] = 'export-taxonomies';
1182
- payload['format'] = 'csv';
1183
- for (const taxonomy of taxonomies) {
1184
- if (taxonomy?.uid) {
1185
- payload['taxonomyUID'] = taxonomy?.uid;
1186
- const data = await taxonomySDKHandler(payload);
1187
- const taxonomies = await csvParse(data, headers);
1188
- taxonomiesData.push(...taxonomies);
1189
- }
1235
+ let taxonomiesData = [];
1236
+ let headers = [];
1237
+ payload['type'] = 'export-taxonomies';
1238
+ payload['format'] = 'csv';
1239
+ for (const taxonomy of taxonomies) {
1240
+ if (taxonomy?.uid) {
1241
+ payload['taxonomyUID'] = taxonomy?.uid;
1242
+ const data = await taxonomySDKHandler(payload);
1243
+ const taxonomies = await csvParse(data, headers);
1244
+ taxonomiesData.push(...taxonomies);
1190
1245
  }
1246
+ }
1191
1247
 
1192
- return { taxonomiesData, headers };
1248
+ return { taxonomiesData, headers };
1193
1249
  }
1194
1250
 
1195
1251
  /**
@@ -1224,6 +1280,7 @@ module.exports = {
1224
1280
  chooseBranch: chooseBranch,
1225
1281
  chooseContentType: chooseContentType,
1226
1282
  chooseLanguage: chooseLanguage,
1283
+ chooseFallbackOptions: chooseFallbackOptions,
1227
1284
  getEntries: getEntries,
1228
1285
  getEnvironments: getEnvironments,
1229
1286
  cleanEntries: cleanEntries,