@contentstack/cli-cm-export-to-csv 1.6.2 → 1.7.1

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Contentstack
3
+ Copyright (c) 2024 Contentstack
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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.6.2",
4
+ "version": "1.7.1",
5
5
  "author": "Abhinav Gupta @abhinav-from-contentstack",
6
6
  "bugs": "https://github.com/contentstack/cli/issues",
7
7
  "dependencies": {
8
- "@contentstack/cli-command": "~1.2.16",
9
- "@contentstack/cli-utilities": "~1.5.9",
8
+ "@contentstack/cli-command": "~1.2.18",
9
+ "@contentstack/cli-utilities": "~1.6.1",
10
10
  "chalk": "^4.1.0",
11
11
  "fast-csv": "^4.3.6",
12
12
  "inquirer": "8.2.4",
@@ -14,7 +14,7 @@
14
14
  "mkdirp": "^3.0.1"
15
15
  },
16
16
  "devDependencies": {
17
- "@oclif/test": "^2.2.10",
17
+ "@oclif/test": "^2.5.6",
18
18
  "@types/chai": "^4.3.6",
19
19
  "@types/mocha": "^10.0.1",
20
20
  "chai": "^4.3.8",
package/src/util/index.js CHANGED
@@ -62,7 +62,6 @@ async function getOrganizations(managementAPIClient) {
62
62
  try {
63
63
  return await getOrganizationList(managementAPIClient, { skip: 0, page: 1, limit: 100 }, []);
64
64
  } catch (error) {
65
- console.log(error);
66
65
  throw error;
67
66
  }
68
67
  }
@@ -461,7 +460,7 @@ function write(command, entries, fileName, message, delimiter, headers) {
461
460
  process.chdir(directory);
462
461
  }
463
462
  // eslint-disable-next-line no-undef
464
- cliux.print(`Writing ${message} to file: ${process.cwd()}${delimeter}${fileName}`);
463
+ cliux.print(`Writing ${message} to file: "${process.cwd()}${delimeter}${fileName}"`);
465
464
  if (headers?.length) fastcsv.writeToPath(fileName, entries, { headers, delimiter });
466
465
  else fastcsv.writeToPath(fileName, entries, { headers: true, delimiter });
467
466
  }
@@ -701,10 +700,10 @@ function handleErrorMsg(err) {
701
700
 
702
701
  /**
703
702
  * This function does the sdk calls to get all the teams in org
704
- * @param {object} managementAPIClient
705
- * @param {object} org
706
- * @param {object} queryParam
707
- * @returns
703
+ * @param {object} managementAPIClient
704
+ * @param {object} org
705
+ * @param {object} queryParam
706
+ * @returns
708
707
  */
709
708
  async function getAllTeams(managementAPIClient, org, queryParam = {}) {
710
709
  try {
@@ -716,8 +715,8 @@ async function getAllTeams(managementAPIClient, org, queryParam = {}) {
716
715
 
717
716
  /**
718
717
  * This function is used to handle the pagination and call the sdk
719
- * @param {object} managementAPIClient
720
- * @param {object} org
718
+ * @param {object} managementAPIClient
719
+ * @param {object} org
721
720
  */
722
721
  async function exportOrgTeams(managementAPIClient, org) {
723
722
  let allTeamsInOrg = [];
@@ -738,9 +737,9 @@ async function exportOrgTeams(managementAPIClient, org) {
738
737
  }
739
738
 
740
739
  /**
741
- * This function will get all the org level roles
742
- * @param {object} managementAPIClient
743
- * @param {object} org
740
+ * This function will get all the org level roles
741
+ * @param {object} managementAPIClient
742
+ * @param {object} org
744
743
  */
745
744
  async function getOrgRolesForTeams(managementAPIClient, org) {
746
745
  let roleMap = {}; // for org level there are two roles only admin and member
@@ -764,9 +763,9 @@ async function getOrgRolesForTeams(managementAPIClient, org) {
764
763
 
765
764
  /**
766
765
  * Removes the unnecessary fields from the objects in the data and assign org level roles to the team based on role uid
767
- * @param {array} data
768
- * @param {object} managementAPIClient
769
- * @param {object} org
766
+ * @param {array} data
767
+ * @param {object} managementAPIClient
768
+ * @param {object} org
770
769
  */
771
770
  async function cleanTeamsData(data, managementAPIClient, org) {
772
771
  const roleMap = await getOrgRolesForTeams(managementAPIClient, org);
@@ -785,7 +784,7 @@ async function cleanTeamsData(data, managementAPIClient, org) {
785
784
  'delete',
786
785
  'fetch',
787
786
  'stackRoleMappings',
788
- 'teamUsers'
787
+ 'teamUsers',
789
788
  ];
790
789
  if (data?.length) {
791
790
  return data.map((team) => {
@@ -807,10 +806,10 @@ async function cleanTeamsData(data, managementAPIClient, org) {
807
806
 
808
807
  /**
809
808
  * This function is used to call all the other teams function to export the required files
810
- * @param {object} managementAPIClient
811
- * @param {object} organization
812
- * @param {string} teamUid
813
- * @param {character} delimiter
809
+ * @param {object} managementAPIClient
810
+ * @param {object} organization
811
+ * @param {string} teamUid
812
+ * @param {character} delimiter
814
813
  */
815
814
  async function exportTeams(managementAPIClient, organization, teamUid, delimiter) {
816
815
  cliux.print(
@@ -853,10 +852,10 @@ async function exportTeams(managementAPIClient, organization, teamUid, delimiter
853
852
 
854
853
  /**
855
854
  * This function is used to get individual team user details and write to file
856
- * @param {array} allTeamsData
857
- * @param {object} organization
855
+ * @param {array} allTeamsData
856
+ * @param {object} organization
858
857
  * @param {string} teamUid optional
859
- * @param {character} delimiter
858
+ * @param {character} delimiter
860
859
  */
861
860
  async function getTeamsDetail(allTeamsData, organization, teamUid, delimiter) {
862
861
  if (!teamUid) {
@@ -884,10 +883,10 @@ async function getTeamsDetail(allTeamsData, organization, teamUid, delimiter) {
884
883
 
885
884
  /**
886
885
  * This will export the role mappings of the team, for which stack the team has which role
887
- * @param {object} managementAPIClient
888
- * @param {array} allTeamsData Data for all the teams in the stack
886
+ * @param {object} managementAPIClient
887
+ * @param {array} allTeamsData Data for all the teams in the stack
889
888
  * @param {string} teamUid for a particular team who's data we want
890
- * @param {character} delimiter
889
+ * @param {character} delimiter
891
890
  */
892
891
  async function exportRoleMappings(managementAPIClient, allTeamsData, teamUid, delimiter) {
893
892
  let stackRoleWithTeamData = [];
@@ -936,7 +935,7 @@ async function exportRoleMappings(managementAPIClient, allTeamsData, teamUid, de
936
935
  ];
937
936
  try {
938
937
  const exportStackRole = await inquirer.prompt(export_stack_role);
939
- if(exportStackRole.chooseExport==='no') {
938
+ if (exportStackRole.chooseExport === 'no') {
940
939
  process.exit(1);
941
940
  }
942
941
  } catch (error) {
@@ -954,10 +953,10 @@ async function exportRoleMappings(managementAPIClient, allTeamsData, teamUid, de
954
953
 
955
954
  /**
956
955
  * Mapping the team stacks with the stack role and returning and array of object
957
- * @param {object} managementAPIClient
958
- * @param {array} stackRoleMapping
959
- * @param {string} teamName
960
- * @param {string} teamUid
956
+ * @param {object} managementAPIClient
957
+ * @param {array} stackRoleMapping
958
+ * @param {string} teamName
959
+ * @param {string} teamUid
961
960
  */
962
961
  async function mapRoleWithTeams(managementAPIClient, stackRoleMapping, teamName, teamUid) {
963
962
  const roles = await getRoleData(managementAPIClient, stackRoleMapping.stackApiKey);
@@ -983,8 +982,8 @@ async function mapRoleWithTeams(managementAPIClient, stackRoleMapping, teamName,
983
982
 
984
983
  /**
985
984
  * Making sdk call to get all the roles in the given stack
986
- * @param {object} managementAPIClient
987
- * @param {string} stackApiKey
985
+ * @param {object} managementAPIClient
986
+ * @param {string} stackApiKey
988
987
  */
989
988
  async function getRoleData(managementAPIClient, stackApiKey) {
990
989
  try {
@@ -996,7 +995,7 @@ async function getRoleData(managementAPIClient, stackApiKey) {
996
995
 
997
996
  /**
998
997
  * Here in the users array we are adding the team-name and team-uid to individual users and returning an array of object of user details only
999
- * @param {array} teams
998
+ * @param {array} teams
1000
999
  */
1001
1000
  async function getTeamsUserDetails(teams) {
1002
1001
  const allTeamUsers = [];
@@ -1081,7 +1080,7 @@ async function getTaxonomy(payload) {
1081
1080
  * @returns {*} Promise<any>
1082
1081
  */
1083
1082
  async function taxonomySDKHandler(payload, skip) {
1084
- const { stackAPIClient, taxonomyUID, type } = payload;
1083
+ const { stackAPIClient, taxonomyUID, type, format } = payload;
1085
1084
 
1086
1085
  const queryParams = { include_count: true, limit: payload.limit };
1087
1086
  if (skip >= 0) queryParams['skip'] = skip || 0;
@@ -1093,13 +1092,13 @@ async function taxonomySDKHandler(payload, skip) {
1093
1092
  .query(queryParams)
1094
1093
  .find()
1095
1094
  .then((data) => data)
1096
- .catch((err) => handleErrorMsg(err));
1095
+ .catch((err) => handleTaxonomyErrorMsg(err));
1097
1096
  case 'taxonomy':
1098
1097
  return await stackAPIClient
1099
1098
  .taxonomy(taxonomyUID)
1100
1099
  .fetch()
1101
1100
  .then((data) => data)
1102
- .catch((err) => handleErrorMsg(err));
1101
+ .catch((err) => handleTaxonomyErrorMsg(err));
1103
1102
  case 'terms':
1104
1103
  queryParams['depth'] = 0;
1105
1104
  return await stackAPIClient
@@ -1108,9 +1107,15 @@ async function taxonomySDKHandler(payload, skip) {
1108
1107
  .query(queryParams)
1109
1108
  .find()
1110
1109
  .then((data) => data)
1111
- .catch((err) => handleErrorMsg(err));
1110
+ .catch((err) => handleTaxonomyErrorMsg(err));
1111
+ case 'export-taxonomies':
1112
+ return await stackAPIClient
1113
+ .taxonomy(taxonomyUID)
1114
+ .export({ format })
1115
+ .then((data) => data)
1116
+ .catch((err) => handleTaxonomyErrorMsg(err));
1112
1117
  default:
1113
- handleErrorMsg({ errorMessage: 'Invalid module!' });
1118
+ handleTaxonomyErrorMsg({ errorMessage: 'Invalid module!' });
1114
1119
  }
1115
1120
  }
1116
1121
 
@@ -1153,11 +1158,9 @@ function formatTermsOfTaxonomyData(terms, taxonomyUID) {
1153
1158
  }
1154
1159
  }
1155
1160
 
1156
- function handleErrorMsg(err) {
1157
- if (err?.errorMessage) {
1158
- cliux.print(`Error: ${err.errorMessage}`, { color: 'red' });
1159
- } else if (err?.message) {
1160
- const errorMsg = err?.errors?.taxonomy || err?.errors?.term || err?.message;
1161
+ function handleTaxonomyErrorMsg(err) {
1162
+ if (err?.errorMessage || err?.message) {
1163
+ const errorMsg = err?.errorMessage || err?.errors?.taxonomy || err?.errors?.term || err?.message;
1161
1164
  cliux.print(`Error: ${errorMsg}`, { color: 'red' });
1162
1165
  } else {
1163
1166
  console.log(err);
@@ -1167,60 +1170,53 @@ function handleErrorMsg(err) {
1167
1170
  }
1168
1171
 
1169
1172
  /**
1170
- * create an importable CSV file, to utilize with the migration script.
1173
+ * Generate a CSV file that can be imported for use with the migration script.
1171
1174
  * @param {*} payload api request payload
1172
1175
  * @param {*} taxonomies taxonomies data
1173
1176
  * @returns
1174
1177
  */
1175
1178
  async function createImportableCSV(payload, taxonomies) {
1176
- let taxonomiesData = [];
1177
- let headers = ['Taxonomy Name', 'Taxonomy UID', 'Taxonomy Description'];
1178
- for (let index = 0; index < taxonomies?.length; index++) {
1179
- const taxonomy = taxonomies[index];
1180
- const taxonomyUID = taxonomy?.uid;
1181
- if (taxonomyUID) {
1182
- const sanitizedTaxonomy = sanitizeData({
1183
- 'Taxonomy Name': taxonomy?.name,
1184
- 'Taxonomy UID': taxonomyUID,
1185
- 'Taxonomy Description': taxonomy?.description,
1186
- });
1187
- taxonomiesData.push(sanitizedTaxonomy);
1188
- payload['taxonomyUID'] = taxonomyUID;
1189
- const terms = await getAllTermsOfTaxonomy(payload);
1190
- //fetch all parent terms
1191
- const parentTerms = terms.filter((term) => term?.parent_uid === null);
1192
- const termsData = getParentAndChildTerms(parentTerms, terms, headers);
1193
- taxonomiesData.push(...termsData);
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
+ }
1194
1190
  }
1195
- }
1196
1191
 
1197
- return { taxonomiesData, headers };
1192
+ return { taxonomiesData, headers };
1198
1193
  }
1199
1194
 
1200
1195
  /**
1201
- * Get the parent and child terms, then arrange them hierarchically in a CSV file.
1202
- * @param {*} parentTerms list of parent terms
1203
- * @param {*} terms respective terms of taxonomies
1204
- * @param {*} headers list of csv headers include taxonomy and terms column
1205
- * @param {*} termsData parent and child terms
1196
+ * Parse the CSV data and segregate the headers from the actual data.
1197
+ * @param {*} data taxonomy csv data with headers
1198
+ * @param {*} headers list of csv headers
1199
+ * @returns taxonomy data without headers
1206
1200
  */
1207
- function getParentAndChildTerms(parentTerms, terms, headers, termsData = []) {
1208
- for (let i = 0; i < parentTerms?.length; i++) {
1209
- const parentTerm = parentTerms[i];
1210
- const levelUID = `Term Level${parentTerm.depth} UID`;
1211
- const levelName = `Term Level${parentTerm.depth} Name`;
1212
- if (headers.indexOf(levelName) === -1) headers.push(levelName);
1213
- if (headers.indexOf(levelUID) === -1) headers.push(levelUID);
1214
- const sanitizedTermData = sanitizeData({ [levelName]: parentTerm.name, [levelUID]: parentTerm.uid });
1215
- termsData.push(sanitizedTermData);
1216
- //fetch all sibling terms
1217
- const newParents = terms.filter((term) => term.parent_uid === parentTerm.uid);
1218
- if (newParents?.length) {
1219
- getParentAndChildTerms(newParents, terms, headers, termsData);
1220
- }
1221
- }
1222
- return termsData;
1223
- }
1201
+ const csvParse = (data, headers) => {
1202
+ return new Promise((resolve, reject) => {
1203
+ const taxonomies = [];
1204
+ const stream = fastcsv.parseStream(fastcsv.parse());
1205
+ stream.write(data);
1206
+ stream.end();
1207
+ stream
1208
+ .on('data', (data) => {
1209
+ taxonomies.push(data);
1210
+ })
1211
+ .on('error', (err) => reject(err))
1212
+ .on('end', () => {
1213
+ taxonomies[0]?.forEach((header) => {
1214
+ if (!headers.includes(header)) headers.push(header);
1215
+ });
1216
+ resolve(taxonomies.splice(1));
1217
+ });
1218
+ });
1219
+ };
1224
1220
 
1225
1221
  module.exports = {
1226
1222
  chooseOrganization: chooseOrganization,