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

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.6.1",
4
+ "version": "1.6.2",
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.2.16",
9
- "@contentstack/cli-utilities": "~1.5.7",
9
+ "@contentstack/cli-utilities": "~1.5.9",
10
10
  "chalk": "^4.1.0",
11
11
  "fast-csv": "^4.3.6",
12
12
  "inquirer": "8.2.4",
@@ -65,4 +65,4 @@
65
65
  }
66
66
  },
67
67
  "repository": "https://github.com/contentstack/cli"
68
- }
68
+ }
@@ -334,8 +334,8 @@ class ExportToCsvCommand extends Command {
334
334
  const listOfTokens = configHandler.get('tokens');
335
335
  if (managementTokenAlias && listOfTokens[managementTokenAlias]) {
336
336
  const checkManagementTokenValidity = await isManagementTokenValid((listOfTokens[managementTokenAlias].apiKey) ,listOfTokens[managementTokenAlias].token);
337
- if(!checkManagementTokenValidity?.valid) {
338
- throw checkManagementTokenValidity.hasOwnProperty('valid')?(`error: Management token or stack API key is invalid. ${checkManagementTokenValidity?.message||""}`):checkManagementTokenValidity?.message||"";
337
+ if(checkManagementTokenValidity.hasOwnProperty('message')) {
338
+ throw checkManagementTokenValidity.valid==='failedToCheck'?checkManagementTokenValidity.message:(`error: Management token or stack API key is invalid. ${checkManagementTokenValidity.message}`);
339
339
  }
340
340
  apiClient = await managementSDKClient({
341
341
  host: this.cmaHost,
@@ -449,31 +449,31 @@ ExportToCsvCommand.description = `Export entries, taxonomies, terms or organizat
449
449
  ExportToCsvCommand.examples = [
450
450
  'csdx cm:export-to-csv',
451
451
  '',
452
- 'Exporting entries to csv',
452
+ 'Exporting entries to CSV',
453
453
  'csdx cm:export-to-csv --action <entries> --locale <locale> --alias <management-token-alias> --content-type <content-type>',
454
454
  '',
455
- 'Exporting entries to csv with stack name provided and branch name provided',
455
+ 'Exporting entries to CSV with stack name provided and branch name provided',
456
456
  'csdx cm:export-to-csv --action <entries> --locale <locale> --alias <management-token-alias> --content-type <content-type> --stack-name <stack-name> --branch <branch-name>',
457
457
  '',
458
- 'Exporting organization users to csv',
458
+ 'Exporting organization users to CSV',
459
459
  'csdx cm:export-to-csv --action <users> --org <org-uid>',
460
460
  '',
461
- 'Exporting organization users to csv with organization name provided',
461
+ 'Exporting organization users to CSV with organization name provided',
462
462
  'csdx cm:export-to-csv --action <users> --org <org-uid> --org-name <org-name>',
463
463
  '',
464
- 'Exporting Organizations Teams to CSV',
464
+ 'Exporting organization teams to CSV',
465
465
  'csdx cm:export-to-csv --action <teams>',
466
466
  '',
467
- 'Exporting Organizations Teams to CSV with org-uid',
467
+ 'Exporting organization teams to CSV with org UID',
468
468
  'csdx cm:export-to-csv --action <teams> --org <org-uid>',
469
469
  '',
470
- 'Exporting Organizations Teams to CSV with team uid',
470
+ 'Exporting organization teams to CSV with team UID',
471
471
  'csdx cm:export-to-csv --action <teams> --team-uid <team-uid>',
472
472
  '',
473
- 'Exporting Organizations Teams to CSV with org-uid and team uid',
473
+ 'Exporting organization teams to CSV with org UID and team UID',
474
474
  'csdx cm:export-to-csv --action <teams> --org <org-uid> --team-uid <team-uid>',
475
475
  '',
476
- 'Exporting Organizations Teams to CSV with org-uid and team uid',
476
+ 'Exporting organization teams to CSV with org UID and team UID',
477
477
  'csdx cm:export-to-csv --action <teams> --org <org-uid> --team-uid <team-uid> --org-name <org-name>',
478
478
  '',
479
479
  'Exporting taxonomies and related terms to a .CSV file with the provided taxonomy UID',
@@ -2,8 +2,8 @@ module.exports = {
2
2
  limit:100,
3
3
  cancelString: 'Cancel and Exit',
4
4
  exportEntries: 'Export entries to a .CSV file',
5
- exportUsers: "Export organization user's data to a .CSV file",
6
- exportTeams: "Export organization team's data to a .csv file",
5
+ exportUsers: "Export organization users' data to a .CSV file",
6
+ exportTeams: "Export organization teams' data to a .CSV file",
7
7
  exportTaxonomies: 'Export taxonomies to a .CSV file',
8
8
  adminError: "Unable to export data. Make sure you're an admin or owner of this organization",
9
9
  organizationNameRegex: /\'/,
package/src/util/index.js CHANGED
@@ -693,54 +693,59 @@ function wait(time) {
693
693
  }
694
694
 
695
695
  function handleErrorMsg(err) {
696
- cliux.print(`Error: ${(err?.errorMessage || err?.message) ? err?.errorMessage || err?.message : messageHandler.parse('CLI_EXPORT_CSV_API_FAILED')}`, { color: 'red' })
696
+ cliux.print(`Error: ${(err?.errorMessage || err?.message) ?? messageHandler.parse('CLI_EXPORT_CSV_API_FAILED')}`, {
697
+ color: 'red',
698
+ });
697
699
  process.exit(1);
698
700
  }
699
701
 
700
- async function apiRequestHandler(org, queryParam = {}) {
701
- const headers = {
702
- authtoken: configHandler.get('authtoken'),
703
- organization_uid: org.uid,
704
- 'Content-Type': 'application/json',
705
- api_version: 1.1,
706
- };
707
-
708
- return await new HttpClient()
709
- .headers(headers)
710
- .queryParams(queryParam)
711
- .get(`${configHandler.get('region')?.cma}/organizations/${org?.uid}/teams`)
712
- .then((res) => {
713
- const { status, data } = res;
714
- if (status === 200) {
715
- return data;
716
- } else {
717
- cliux.print(`${data?.error_message || data?.message || data?.errorMessage}`, { color: 'red' });
718
- process.exit(1);
719
- }
720
- })
721
- .catch((error) => {
722
- handleErrorMsg(error);
723
- });
702
+ /**
703
+ * 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
708
+ */
709
+ async function getAllTeams(managementAPIClient, org, queryParam = {}) {
710
+ try {
711
+ return await managementAPIClient.organization(org.uid).teams().fetchAll(queryParam);
712
+ } catch (error) {
713
+ handleErrorMsg(error);
714
+ }
724
715
  }
725
716
 
717
+ /**
718
+ * This function is used to handle the pagination and call the sdk
719
+ * @param {object} managementAPIClient
720
+ * @param {object} org
721
+ */
726
722
  async function exportOrgTeams(managementAPIClient, org) {
727
- let teamsObjectArray = [];
723
+ let allTeamsInOrg = [];
728
724
  let skip = 0;
729
725
  let limit = config?.limit || 100;
730
726
  do {
731
- const data = await apiRequestHandler(org, { skip: skip, limit: limit, includeUserDetails: true });
727
+ const data = await getAllTeams(managementAPIClient, org, {
728
+ skip: skip,
729
+ limit: limit,
730
+ includeUserDetails: true,
731
+ });
732
732
  skip += limit;
733
- teamsObjectArray.push(...data?.teams);
733
+ allTeamsInOrg.push(...data.items);
734
734
  if (skip >= data?.count) break;
735
735
  } while (1);
736
- teamsObjectArray = await cleanTeamsData(teamsObjectArray, managementAPIClient, org);
737
- return teamsObjectArray;
736
+ allTeamsInOrg = await cleanTeamsData(allTeamsInOrg, managementAPIClient, org);
737
+ return allTeamsInOrg;
738
738
  }
739
739
 
740
+ /**
741
+ * This function will get all the org level roles
742
+ * @param {object} managementAPIClient
743
+ * @param {object} org
744
+ */
740
745
  async function getOrgRolesForTeams(managementAPIClient, org) {
741
746
  let roleMap = {}; // for org level there are two roles only admin and member
742
747
 
743
- // SDK call to get the role uids
748
+ // SDK call to get the role UIDs
744
749
  await managementAPIClient
745
750
  .organization(org.uid)
746
751
  .roles()
@@ -757,6 +762,12 @@ async function getOrgRolesForTeams(managementAPIClient, org) {
757
762
  return roleMap;
758
763
  }
759
764
 
765
+ /**
766
+ * 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
770
+ */
760
771
  async function cleanTeamsData(data, managementAPIClient, org) {
761
772
  const roleMap = await getOrgRolesForTeams(managementAPIClient, org);
762
773
  const fieldToBeDeleted = [
@@ -769,18 +780,24 @@ async function cleanTeamsData(data, managementAPIClient, org) {
769
780
  'createdByUserName',
770
781
  'updatedByUserName',
771
782
  'organizationUid',
783
+ 'urlPath',
784
+ 'update',
785
+ 'delete',
786
+ 'fetch',
787
+ 'stackRoleMappings',
788
+ 'teamUsers'
772
789
  ];
773
790
  if (data?.length) {
774
791
  return data.map((team) => {
775
792
  team = omit(team, fieldToBeDeleted);
776
-
777
- team.organizationRole = (team.organizationRole === roleMap["member"]) ? "member" : "admin";
778
-
779
- if (!team.hasOwnProperty("description")) {
780
- team.description = "";
793
+
794
+ team.organizationRole = team.organizationRole === roleMap['member'] ? 'member' : 'admin';
795
+
796
+ if (!team.hasOwnProperty('description')) {
797
+ team.description = '';
781
798
  }
782
799
  team.Total_Members = team?.users?.length || 0;
783
-
800
+
784
801
  return team;
785
802
  });
786
803
  } else {
@@ -788,6 +805,13 @@ async function cleanTeamsData(data, managementAPIClient, org) {
788
805
  }
789
806
  }
790
807
 
808
+ /**
809
+ * 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
814
+ */
791
815
  async function exportTeams(managementAPIClient, organization, teamUid, delimiter) {
792
816
  cliux.print(
793
817
  `info: Exporting the ${
@@ -799,7 +823,9 @@ async function exportTeams(managementAPIClient, organization, teamUid, delimiter
799
823
  );
800
824
  const allTeamsData = await exportOrgTeams(managementAPIClient, organization);
801
825
  if (!allTeamsData?.length) {
802
- cliux.print(`info: The organization ${organization?.name} does not have any teams associated with it. Please verify and provide the correct organization name.`);
826
+ cliux.print(
827
+ `info: The organization ${organization?.name} does not have any teams associated with it. Please verify and provide the correct organization name.`,
828
+ );
803
829
  } else {
804
830
  const modifiedTeam = cloneDeep(allTeamsData);
805
831
  modifiedTeam.forEach((team) => {
@@ -825,6 +851,13 @@ async function exportTeams(managementAPIClient, organization, teamUid, delimiter
825
851
  }
826
852
  }
827
853
 
854
+ /**
855
+ * This function is used to get individual team user details and write to file
856
+ * @param {array} allTeamsData
857
+ * @param {object} organization
858
+ * @param {string} teamUid optional
859
+ * @param {character} delimiter
860
+ */
828
861
  async function getTeamsDetail(allTeamsData, organization, teamUid, delimiter) {
829
862
  if (!teamUid) {
830
863
  const userData = await getTeamsUserDetails(allTeamsData);
@@ -835,14 +868,12 @@ async function getTeamsDetail(allTeamsData, organization, teamUid, delimiter) {
835
868
  write(this, userData, fileName, 'Team User details', delimiter);
836
869
  } else {
837
870
  const team = allTeamsData.filter((team) => team.uid === teamUid)[0];
838
-
839
871
  team.users.forEach((user) => {
840
872
  user['team-name'] = team.name;
841
873
  user['team-uid'] = team.uid;
842
874
  delete user['active'];
843
875
  delete user['orgInvitationStatus'];
844
876
  });
845
-
846
877
  const fileName = `${kebabize(
847
878
  organization.name.replace(config.organizationNameRegex, ''),
848
879
  )}_team_${teamUid}_User_Details_export.csv`;
@@ -851,16 +882,25 @@ async function getTeamsDetail(allTeamsData, organization, teamUid, delimiter) {
851
882
  }
852
883
  }
853
884
 
885
+ /**
886
+ * 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
889
+ * @param {string} teamUid for a particular team who's data we want
890
+ * @param {character} delimiter
891
+ */
854
892
  async function exportRoleMappings(managementAPIClient, allTeamsData, teamUid, delimiter) {
855
893
  let stackRoleWithTeamData = [];
856
894
  let flag = false;
857
895
  const stackNotAdmin = [];
858
896
  if (teamUid) {
859
- const team = find(allTeamsData,function(teamObject) { return teamObject?.uid===teamUid });
897
+ const team = find(allTeamsData, function (teamObject) {
898
+ return teamObject?.uid === teamUid;
899
+ });
860
900
  for (const stack of team?.stackRoleMapping) {
861
901
  const roleData = await mapRoleWithTeams(managementAPIClient, stack, team?.name, team?.uid);
862
902
  stackRoleWithTeamData.push(...roleData);
863
- if(roleData[0]['Stack Name']==='') {
903
+ if (roleData[0]['Stack Name'] === '') {
864
904
  flag = true;
865
905
  stackNotAdmin.push(stack.stackApiKey);
866
906
  }
@@ -870,18 +910,21 @@ async function exportRoleMappings(managementAPIClient, allTeamsData, teamUid, de
870
910
  for (const stack of team?.stackRoleMapping ?? []) {
871
911
  const roleData = await mapRoleWithTeams(managementAPIClient, stack, team?.name, team?.uid);
872
912
  stackRoleWithTeamData.push(...roleData);
873
- if(roleData[0]['Stack Name']==='') {
913
+ if (roleData[0]['Stack Name'] === '') {
874
914
  flag = true;
875
915
  stackNotAdmin.push(stack.stackApiKey);
876
916
  }
877
917
  }
878
918
  }
879
919
  }
880
- if(stackNotAdmin?.length) {
881
- cliux.print(`warning: Admin access denied to the following stacks using the provided API keys. Please get in touch with the stack owner to request access.`,{color:"yellow"});
882
- cliux.print(`${stackNotAdmin.join(' , ')}`,{color:"yellow"});
920
+ if (stackNotAdmin?.length) {
921
+ cliux.print(
922
+ `warning: Admin access denied to the following stacks using the provided API keys. Please get in touch with the stack owner to request access.`,
923
+ { color: 'yellow' },
924
+ );
925
+ cliux.print(`${stackNotAdmin.join(' , ')}`, { color: 'yellow' });
883
926
  }
884
- if(flag) {
927
+ if (flag) {
885
928
  let export_stack_role = [
886
929
  {
887
930
  type: 'list',
@@ -889,19 +932,17 @@ async function exportRoleMappings(managementAPIClient, allTeamsData, teamUid, de
889
932
  message: `Access denied: Please confirm if you still want to continue exporting the data without the { Stack Name, Stack Uid, Role Name } fields.`,
890
933
  choices: ['yes', 'no'],
891
934
  loop: false,
892
- }]
893
- const exportStackRole = await inquirer
894
- .prompt(export_stack_role)
895
- .then(( chosenOrg ) => {
896
- return chosenOrg
897
- })
898
- .catch((error) => {
899
- cliux.print(error, {color:'red'});
900
- process.exit(1);
901
- });
902
- if(exportStackRole.chooseExport === 'no') {
935
+ },
936
+ ];
937
+ try {
938
+ const exportStackRole = await inquirer.prompt(export_stack_role);
939
+ if(exportStackRole.chooseExport==='no') {
903
940
  process.exit(1);
904
- }
941
+ }
942
+ } catch (error) {
943
+ cliux.print(error, { color: 'red' });
944
+ process.exit(1);
945
+ }
905
946
  }
906
947
 
907
948
  const fileName = `${kebabize('Stack_Role_Mapping'.replace(config.organizationNameRegex, ''))}${
@@ -911,13 +952,20 @@ async function exportRoleMappings(managementAPIClient, allTeamsData, teamUid, de
911
952
  write(this, stackRoleWithTeamData, fileName, 'Team Stack Role details', delimiter);
912
953
  }
913
954
 
955
+ /**
956
+ * 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
961
+ */
914
962
  async function mapRoleWithTeams(managementAPIClient, stackRoleMapping, teamName, teamUid) {
915
963
  const roles = await getRoleData(managementAPIClient, stackRoleMapping.stackApiKey);
916
964
  const stackRole = {};
917
965
  roles?.items?.forEach((role) => {
918
966
  if (!stackRole.hasOwnProperty(role?.uid)) {
919
967
  stackRole[role?.uid] = role?.name;
920
- stackRole[role?.stack?.api_key] = {name: role?.stack?.name, uid: role?.stack?.uid }
968
+ stackRole[role?.stack?.api_key] = { name: role?.stack?.name, uid: role?.stack?.uid };
921
969
  }
922
970
  });
923
971
  const stackRoleMapOfTeam = stackRoleMapping?.roles.map((role) => {
@@ -933,17 +981,26 @@ async function mapRoleWithTeams(managementAPIClient, stackRoleMapping, teamName,
933
981
  return stackRoleMapOfTeam;
934
982
  }
935
983
 
984
+ /**
985
+ * Making sdk call to get all the roles in the given stack
986
+ * @param {object} managementAPIClient
987
+ * @param {string} stackApiKey
988
+ */
936
989
  async function getRoleData(managementAPIClient, stackApiKey) {
937
990
  try {
938
991
  return await managementAPIClient.stack({ api_key: stackApiKey }).role().fetchAll();
939
992
  } catch (error) {
940
- return {}
993
+ return {};
941
994
  }
942
995
  }
943
996
 
944
- async function getTeamsUserDetails(teamsObject) {
997
+ /**
998
+ * 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
1000
+ */
1001
+ async function getTeamsUserDetails(teams) {
945
1002
  const allTeamUsers = [];
946
- teamsObject.forEach((team) => {
1003
+ teams.forEach((team) => {
947
1004
  if (team?.users?.length) {
948
1005
  team.users.forEach((user) => {
949
1006
  user['team-name'] = team.name;
@@ -1117,7 +1174,7 @@ function handleErrorMsg(err) {
1117
1174
  */
1118
1175
  async function createImportableCSV(payload, taxonomies) {
1119
1176
  let taxonomiesData = [];
1120
- let headers = ['Taxonomy Name','Taxonomy UID','Taxonomy Description'];
1177
+ let headers = ['Taxonomy Name', 'Taxonomy UID', 'Taxonomy Description'];
1121
1178
  for (let index = 0; index < taxonomies?.length; index++) {
1122
1179
  const taxonomy = taxonomies[index];
1123
1180
  const taxonomyUID = taxonomy?.uid;
@@ -1133,11 +1190,11 @@ async function createImportableCSV(payload, taxonomies) {
1133
1190
  //fetch all parent terms
1134
1191
  const parentTerms = terms.filter((term) => term?.parent_uid === null);
1135
1192
  const termsData = getParentAndChildTerms(parentTerms, terms, headers);
1136
- taxonomiesData.push(...termsData)
1193
+ taxonomiesData.push(...termsData);
1137
1194
  }
1138
1195
  }
1139
1196
 
1140
- return {taxonomiesData, headers};
1197
+ return { taxonomiesData, headers };
1141
1198
  }
1142
1199
 
1143
1200
  /**
@@ -1147,7 +1204,7 @@ async function createImportableCSV(payload, taxonomies) {
1147
1204
  * @param {*} headers list of csv headers include taxonomy and terms column
1148
1205
  * @param {*} termsData parent and child terms
1149
1206
  */
1150
- function getParentAndChildTerms(parentTerms, terms, headers, termsData=[]) {
1207
+ function getParentAndChildTerms(parentTerms, terms, headers, termsData = []) {
1151
1208
  for (let i = 0; i < parentTerms?.length; i++) {
1152
1209
  const parentTerm = parentTerms[i];
1153
1210
  const levelUID = `Term Level${parentTerm.depth} UID`;