@contentstack/cli-cm-export-to-csv 0.1.0-beta.3 → 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Contentstack
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1 +1 @@
1
- {"version":"0.1.0-beta.3","commands":{"cm:export-to-csv":{"id":"cm:export-to-csv","description":"Export entries or organization users to csv using this command\n","pluginName":"@contentstack/cli-cm-export-to-csv","pluginType":"core","aliases":[],"flags":{},"args":[]}}}
1
+ {"version":"1.0.0","commands":{"cm:export-to-csv":{"id":"cm:export-to-csv","description":"Export entries or organization users to csv using this command","pluginName":"@contentstack/cli-cm-export-to-csv","pluginType":"core","aliases":[],"examples":["csdx cm:export-to-csv","","Exporting entries to csv","csdx cm:export-to-csv --action <entries> --locale <locale> --alias <management-token-alias> --content-type <content-type>","","Exporting entries to csv with stack name provided","csdx cm:export-to-csv --action <entries> --locale <locale> --alias <management-token-alias> --content-type <content-type> --stack-name <stack-name>","","Exporting organization users to csv","csdx cm:export-to-csv --action <users> --org <org-uid>","","Exporting organization users to csv with organization name provided","csdx cm:export-to-csv --action <users> --org <org-uid> --org-name <org-name>"],"flags":{"action":{"name":"action","type":"option","description":"Option to export data (entries, users)","required":false,"options":["entries","users"]},"alias":{"name":"alias","type":"option","char":"a","description":"Alias of the management token"},"org":{"name":"org","type":"option","description":"Provide organization UID to clone org users","required":false},"stack-name":{"name":"stack-name","type":"option","char":"n","description":"Name of the stack that needs to be created as csv filename.","required":false},"org-name":{"name":"org-name","type":"option","description":"Name of the organization that needs to be created as csv filename.","required":false},"locale":{"name":"locale","type":"option","description":"Locale for which entries need to be exported","required":false},"content-type":{"name":"content-type","type":"option","description":"Content type for which entries needs to be exported","required":false}},"args":[]}}}
package/package.json CHANGED
@@ -1,30 +1,31 @@
1
1
  {
2
2
  "name": "@contentstack/cli-cm-export-to-csv",
3
3
  "description": "Export entities to csv",
4
- "version": "0.1.0-beta.3",
4
+ "version": "1.0.0",
5
5
  "author": "Abhinav Gupta @abhinav-from-contentstack",
6
6
  "bugs": "https://github.com/contentstack/cli/issues",
7
7
  "dependencies": {
8
- "@contentstack/cli-command": "^0.1.1-beta.6",
8
+ "@contentstack/cli-command": "^1.0.0",
9
+ "@contentstack/cli-utilities": "^1.0.0",
9
10
  "@contentstack/management": "^1.3.0",
10
- "@oclif/command": "^1.8.0",
11
- "@oclif/config": "^1.17.0",
12
- "axios": "^0.21.1",
11
+ "@oclif/command": "^1.8.16",
12
+ "@oclif/config": "^1.18.3",
13
13
  "chalk": "^4.1.0",
14
14
  "fast-csv": "^4.3.6",
15
15
  "inquirer": "^7.3.3",
16
+ "inquirer-checkbox-plus-prompt": "^1.0.1",
16
17
  "mkdirp": "^1.0.4"
17
18
  },
18
19
  "devDependencies": {
19
20
  "@oclif/dev-cli": "^1.26.0",
20
- "@oclif/plugin-help": "^3.2.1",
21
+ "@oclif/plugin-help": "^5.1.12",
21
22
  "@oclif/test": "^1.2.8",
22
23
  "chai": "^4.2.0",
23
24
  "debug": "^4.3.1",
24
- "eslint": "^5.16.0",
25
- "eslint-config-oclif": "^3.1.0",
25
+ "eslint": "^8.18.0",
26
+ "eslint-config-oclif": "^4.0.0",
26
27
  "globby": "^10.0.2",
27
- "mocha": "^8.3.1",
28
+ "mocha": "^10.0.0",
28
29
  "nyc": "^14.1.1"
29
30
  },
30
31
  "engines": {
@@ -50,6 +51,9 @@
50
51
  "@oclif/plugin-help"
51
52
  ]
52
53
  },
54
+ "csdxConfig": {
55
+ "expiredCommands": {}
56
+ },
53
57
  "repository": "contentstack/cli",
54
58
  "scripts": {
55
59
  "postpack": "rm -f oclif.manifest.json",
@@ -1,68 +1,246 @@
1
- const {Command} = require('@contentstack/cli-command')
2
- const util = require('../../util/index')
3
- const ContentstackManagementSDK = require('@contentstack/management')
4
- const config = require('../../util/config.js')
1
+ const { Command, flags } = require('@contentstack/cli-command');
2
+ const { configHandler } = require('@contentstack/cli-utilities');
3
+ const ContentstackManagementSDK = require('@contentstack/management');
4
+ const util = require('../../util');
5
+ const config = require('../../util/config');
5
6
 
6
7
  class ExportToCsvCommand extends Command {
8
+ static flags = {
9
+ action: flags.string({
10
+ required: false,
11
+ multiple: false,
12
+ options: ['entries', 'users'],
13
+ description: `Option to export data (entries, users)`,
14
+ }),
15
+ alias: flags.string({
16
+ char: 'a',
17
+ description: 'Alias of the management token',
18
+ }),
19
+ org: flags.string({
20
+ multiple: false,
21
+ required: false,
22
+ description: 'Provide organization UID to clone org users',
23
+ }),
24
+ 'stack-name': flags.string({
25
+ char: 'n',
26
+ multiple: false,
27
+ required: false,
28
+ description: 'Name of the stack that needs to be created as csv filename.',
29
+ }),
30
+ 'org-name': flags.string({
31
+ multiple: false,
32
+ required: false,
33
+ description: 'Name of the organization that needs to be created as csv filename.',
34
+ }),
35
+ locale: flags.string({
36
+ required: false,
37
+ multiple: false,
38
+ description: 'Locale for which entries need to be exported',
39
+ }),
40
+ 'content-type': flags.string({
41
+ description: 'Content type for which entries needs to be exported',
42
+ required: false,
43
+ multiple: false,
44
+ }),
45
+ };
7
46
 
8
- get managementAPIClient() {
9
- // this._managementAPIClient = ContentstackManagementSDK.client({host:this.cmaHost, authtoken: this.authToken})
10
- this._managementAPIClient = ContentstackManagementSDK.client({host:this.cmaHost, authtoken: this.authToken})
11
- return this._managementAPIClient
12
- }
47
+ get getAuthToken() {
48
+ try {
49
+ return this.authToken;
50
+ } catch (error) {
51
+ return undefined;
52
+ }
53
+ }
54
+
55
+ get managementAPIClient() {
56
+ debugger;
57
+ this._managementAPIClient = ContentstackManagementSDK.client({ host: this.cmaHost, authtoken: this.getAuthToken });
58
+ return this._managementAPIClient;
59
+ }
13
60
 
14
61
  async run() {
15
-
16
- const action = await util.startupQuestions()
17
-
18
- switch(action) {
19
- case config.exportEntries: {
20
- const organization = await util.chooseOrganization(this.managementAPIClient) // prompt for organization
21
- const stack = await util.chooseStack(this.managementAPIClient, organization.uid) // prompt for stack
22
- const contentTypes = await util.chooseContentType(this.managementAPIClient, stack.apiKey) // prompt for content Type
23
- const language = await util.chooseLanguage(this.managementAPIClient, stack.apiKey) // prompt for language
24
- const environments = await util.getEnvironments(this.managementAPIClient, stack.apiKey) // fetch environments, because in publish details only env uid are available and we need env names
25
- while(contentTypes.length > 0) {
26
- let contentType = contentTypes.shift()
27
-
28
- const entriesCount = await util.getEntriesCount(this.managementAPIClient, stack.apiKey, contentType, language.code);
29
- let flatEntries = [];
30
- for (let index = 0; index < entriesCount / 100; index++) {
31
- const entriesResult = await util.getEntries(this.managementAPIClient, stack.apiKey, contentType, language.code, index);
32
- const flatEntriesResult = util.cleanEntries(entriesResult.items, language.code, environments, contentType);
33
- flatEntries = flatEntries.concat(flatEntriesResult);
62
+ try {
63
+ let action;
64
+ const {
65
+ flags: {
66
+ org,
67
+ action: actionFlag,
68
+ 'org-name': orgName,
69
+ 'stack-name': stackName,
70
+ locale: locale,
71
+ 'content-type': contentTypesFlag,
72
+ alias: managementTokenAlias,
73
+ },
74
+ } = this.parse(ExportToCsvCommand);
75
+
76
+ if (actionFlag) {
77
+ action = actionFlag;
78
+ } else {
79
+ action = await util.startupQuestions();
80
+ }
81
+
82
+ switch (action) {
83
+ case config.exportEntries:
84
+ case 'entries': {
85
+ try {
86
+ let stack;
87
+ let stackClient;
88
+ let language;
89
+ let contentTypes = [];
90
+ const listOfTokens = configHandler.get('tokens');
91
+
92
+ if (managementTokenAlias && listOfTokens[managementTokenAlias]) {
93
+ stack = {
94
+ name: stackName || managementTokenAlias,
95
+ apiKey: listOfTokens[managementTokenAlias].apiKey,
96
+ token: listOfTokens[managementTokenAlias].token,
97
+ };
98
+ } else if (managementTokenAlias) {
99
+ this.error('Provided management token alias not found in your config.!');
100
+ } else {
101
+ let organization;
102
+
103
+ if (!this.getAuthToken) {
104
+ this.error(config.CLI_EXPORT_CSV_ENTRIES_ERROR, {
105
+ exit: 2,
106
+ suggestions: ['https://www.contentstack.com/docs/developers/cli/authentication/'],
107
+ });
108
+ }
109
+
110
+ if (org) {
111
+ organization = { uid: org };
112
+ } else {
113
+ organization = await util.chooseOrganization(this.managementAPIClient); // prompt for organization
114
+ }
115
+
116
+ stack = await util.chooseStack(this.managementAPIClient, organization.uid); // prompt for stack
117
+ }
118
+
119
+ stackClient = this.getStackClient(stack);
120
+ const contentTypeCount = await util.getContentTypeCount(stackClient);
121
+ const environments = await util.getEnvironments(stackClient); // fetch environments, because in publish details only env uid are available and we need env names
122
+
123
+ if (contentTypesFlag) {
124
+ contentTypes = contentTypesFlag.split(',').map(this.snakeCase);
125
+ } else {
126
+ for (let index = 0; index <= contentTypeCount / 100; index++) {
127
+ const contentTypesMap = await util.getContentTypes(stackClient, index);
128
+ contentTypes = contentTypes.concat(Object.values(contentTypesMap)); // prompt for content Type
129
+ }
130
+ }
131
+
132
+ if (contentTypes.length <= 0) {
133
+ this.log('No content types found for the given stack');
134
+ this.exit();
135
+ }
136
+
137
+ if (!contentTypesFlag) {
138
+ contentTypes = await util.chooseInMemContentTypes(contentTypes);
139
+ }
140
+
141
+ if (locale) {
142
+ language = { code: locale };
143
+ } else {
144
+ language = await util.chooseLanguage(stackClient); // prompt for language
145
+ }
146
+
147
+ while (contentTypes.length > 0) {
148
+ let contentType = contentTypes.pop();
149
+
150
+ const entriesCount = await util.getEntriesCount(stackClient, contentType, language.code);
151
+ let flatEntries = [];
152
+ for (let index = 0; index < entriesCount / 100; index++) {
153
+ const entriesResult = await util.getEntries(stackClient, contentType, language.code, index);
154
+ const flatEntriesResult = util.cleanEntries(
155
+ entriesResult.items,
156
+ language.code,
157
+ environments,
158
+ contentType,
159
+ );
160
+ flatEntries = flatEntries.concat(flatEntriesResult);
161
+ }
162
+ let fileName = `${stack.name}_${contentType}_${language.code}_entries_export.csv`;
163
+
164
+ util.write(this, flatEntries, fileName, 'entries'); // write to file
165
+ }
166
+ } catch (error) {
167
+ this.log(util.formatError(error));
34
168
  }
35
- // let dateTime = util.getDateTime()
36
- // let fileName = `${contentType}_${language.code}_entries_export_${dateTime}.csv`
37
- let fileName = `${stack.name}_${contentType}_${language.code}_entries_export.csv`
169
+ break;
170
+ }
171
+ case config.exportUsers:
172
+ case 'users': {
173
+ try {
174
+ if (!this.getAuthToken) {
175
+ this.error(config.CLI_EXPORT_CSV_LOGIN_FAILED, {
176
+ exit: 2,
177
+ suggestions: ['https://www.contentstack.com/docs/developers/cli/authentication/'],
178
+ });
179
+ }
180
+ let organization;
181
+
182
+ if (org) {
183
+ organization = { uid: org, name: orgName || org };
184
+ } else {
185
+ organization = await util.chooseOrganization(this.managementAPIClient, action); // prompt for organization
186
+ }
38
187
 
39
- util.write(this, flatEntries, fileName) // write to file
188
+ const orgUsers = await util.getOrgUsers(this.managementAPIClient, organization.uid, this);
189
+ const orgRoles = await util.getOrgRoles(this.managementAPIClient, organization.uid, this);
190
+ const mappedUsers = util.getMappedUsers(orgUsers);
191
+ const mappedRoles = util.getMappedRoles(orgRoles);
192
+ const listOfUsers = util.cleanOrgUsers(orgUsers, mappedUsers, mappedRoles);
193
+ const fileName = `${util.kebabize(
194
+ organization.name.replace(config.organizationNameRegex, ''),
195
+ )}_users_export.csv`;
196
+
197
+ util.write(this, listOfUsers, fileName, 'organization details');
198
+ } catch (error) {
199
+ if (error.message) {
200
+ this.log(util.formatError(error));
201
+ }
202
+ }
203
+ break;
40
204
  }
41
- break;
42
205
  }
43
- case config.exportUsers: {
44
- try {
45
- const organization = await util.chooseOrganization(this.managementAPIClient, action) // prompt for organization
46
- const orgUsers = await util.getOrgUsers(this.managementAPIClient, organization.uid, this)
47
- const orgRoles = await util.getOrgRoles(this.managementAPIClient, organization.uid, this)
48
- const mappedUsers = util.getMappedUsers(orgUsers)
49
- const mappedRoles = util.getMappedRoles(orgRoles)
50
- const listOfUsers = util.cleanOrgUsers(orgUsers, mappedUsers, mappedRoles)
51
- // const dateTime = util.getDateTime()
52
- // const fileName = `${util.kebabize(organization.name.replace(config.organizationNameRegex, ''))}_users_export_${dateTime}.csv`
53
- const fileName = `${util.kebabize(organization.name.replace(config.organizationNameRegex, ''))}_users_export.csv`
54
-
55
- util.write(this, listOfUsers, fileName)
56
- } catch(error) {
57
- this.error(error)
58
- }
59
- break;
206
+ } catch (error) {
207
+ if (error.message) {
208
+ this.log(util.formatError(error));
60
209
  }
61
210
  }
62
211
  }
212
+
213
+ snakeCase(string) {
214
+ return (string || '').split(' ').join('_').toLowerCase();
215
+ }
216
+
217
+ getStackClient(stack) {
218
+ if (stack.token) {
219
+ return ContentstackManagementSDK.client({ host: this.cmaHost }).stack({
220
+ api_key: stack.apiKey,
221
+ management_token: stack.token,
222
+ });
223
+ }
224
+ return this.managementAPIClient.stack({ api_key: stack.apiKey });
225
+ }
63
226
  }
64
227
 
65
- ExportToCsvCommand.description = `Export entries or organization users to csv using this command
66
- `
228
+ ExportToCsvCommand.description = `Export entries or organization users to csv using this command`;
229
+
230
+ ExportToCsvCommand.examples = [
231
+ 'csdx cm:export-to-csv',
232
+ '',
233
+ 'Exporting entries to csv',
234
+ 'csdx cm:export-to-csv --action <entries> --locale <locale> --alias <management-token-alias> --content-type <content-type>',
235
+ '',
236
+ 'Exporting entries to csv with stack name provided',
237
+ 'csdx cm:export-to-csv --action <entries> --locale <locale> --alias <management-token-alias> --content-type <content-type> --stack-name <stack-name>',
238
+ '',
239
+ 'Exporting organization users to csv',
240
+ 'csdx cm:export-to-csv --action <users> --org <org-uid>',
241
+ '',
242
+ 'Exporting organization users to csv with organization name provided',
243
+ 'csdx cm:export-to-csv --action <users> --org <org-uid> --org-name <org-name>',
244
+ ];
67
245
 
68
- module.exports = ExportToCsvCommand
246
+ module.exports = ExportToCsvCommand;
@@ -1,7 +1,10 @@
1
1
  module.exports = {
2
- cancelString: 'Cancel and Exit',
3
- exportEntries: 'Export entries to a .CSV file',
4
- exportUsers: 'Export organization users\' data to a .CSV file',
5
- adminError: 'Unable to export data. Make sure you\'re an admin or owner of this organization',
6
- organizationNameRegex: /\'/
7
- }
2
+ cancelString: 'Cancel and Exit',
3
+ exportEntries: 'Export entries to a .CSV file',
4
+ exportUsers: "Export organization users' data to a .CSV file",
5
+ adminError: "Unable to export data. Make sure you're an admin or owner of this organization",
6
+ organizationNameRegex: /\'/,
7
+ CLI_EXPORT_CSV_LOGIN_FAILED: "You need to login to execute this command. See: auth:login --help",
8
+ CLI_EXPORT_CSV_ENTRIES_ERROR: "You need to either login or provide a management token to execute this command"
9
+
10
+ };
package/src/util/index.js CHANGED
@@ -1,413 +1,536 @@
1
- const inquirer = require('inquirer')
2
- const axios = require('axios')
3
- const os = require('os')
4
- const config = require('./config.js')
5
- const fastcsv = require('fast-csv')
6
- const mkdirp = require('mkdirp')
7
- const fs = require('fs')
8
- const debug = require('debug')("export-to-csv")
9
- const directory = './data'
10
- const delimeter = (os.platform() === 'win32') ? '\\' : '/'
1
+ const inquirer = require('inquirer');
2
+ const { HttpClient } = require('@contentstack/cli-utilities');
3
+ const os = require('os');
4
+ const checkboxPlus = require('inquirer-checkbox-plus-prompt');
5
+ const config = require('./config.js');
6
+ const fastcsv = require('fast-csv');
7
+ const mkdirp = require('mkdirp');
8
+ const fs = require('fs');
9
+ const debug = require('debug')('export-to-csv');
10
+ const directory = './data';
11
+ const delimeter = os.platform() === 'win32' ? '\\' : '/';
12
+
13
+ // Register checkbox-plus here.
14
+ inquirer.registerPrompt('checkbox-plus', checkboxPlus);
11
15
 
12
16
  function chooseOrganization(managementAPIClient, action) {
13
- return new Promise(async resolve => {
14
- let organizations
15
- if (action === config.exportUsers) {
16
- organizations = await getOrganizationsWhereUserIsAdmin(managementAPIClient)
17
- } else {
18
- organizations = await getOrganizations(managementAPIClient)
19
- }
20
- let orgList = Object.keys(organizations)
21
- orgList.push(config.cancelString)
22
- let chooseOrganization = [{
23
- type: 'list',
24
- name: 'chosenOrg',
25
- message: 'Choose an Organization',
26
- choices: orgList,
27
- loop: false
28
- }]
29
- inquirer.prompt(chooseOrganization).then(({chosenOrg}) => {
30
- if (chosenOrg === config.cancelString)
31
- exitProgram()
32
- resolve({ name: chosenOrg, uid: organizations[chosenOrg] })
33
- })
34
- })
17
+ return new Promise(async (resolve, reject) => {
18
+ try {
19
+ let organizations;
20
+ if (action === config.exportUsers) {
21
+ organizations = await getOrganizationsWhereUserIsAdmin(managementAPIClient);
22
+ } else {
23
+ organizations = await getOrganizations(managementAPIClient);
24
+ }
25
+ let orgList = Object.keys(organizations);
26
+ orgList.push(config.cancelString);
27
+ let _chooseOrganization = [
28
+ {
29
+ type: 'list',
30
+ name: 'chosenOrg',
31
+ message: 'Choose an Organization',
32
+ choices: orgList,
33
+ loop: false,
34
+ },
35
+ ];
36
+ inquirer.prompt(_chooseOrganization).then(({ chosenOrg }) => {
37
+ if (chosenOrg === config.cancelString) exitProgram();
38
+ resolve({ name: chosenOrg, uid: organizations[chosenOrg] });
39
+ });
40
+ } catch (error) {
41
+ reject(error);
42
+ }
43
+ });
35
44
  }
36
45
 
37
46
  function getOrganizations(managementAPIClient) {
38
- return new Promise(resolve => {
39
- let result = {}
40
-
41
- managementAPIClient.organization().fetchAll().then(organizations => {
42
- organizations.items.forEach(org => {
43
- result[org.name] = org.uid
44
- })
45
- resolve(result)
46
- })
47
- })
47
+ return new Promise((resolve, reject) => {
48
+ let result = {};
49
+
50
+ managementAPIClient
51
+ .organization()
52
+ .fetchAll()
53
+ .then((organizations) => {
54
+ organizations.items.forEach((org) => {
55
+ result[org.name] = org.uid;
56
+ });
57
+ resolve(result);
58
+ })
59
+ .catch((error) => reject(error));
60
+ });
48
61
  }
49
62
 
50
63
  function getOrganizationsWhereUserIsAdmin(managementAPIClient) {
51
- return new Promise(resolve => {
52
- let result = {}
53
- managementAPIClient
54
- .getUser({include_orgs_roles: true})
55
- .then(response => {
56
- let organizations = response.organizations.filter(org => {
57
- if (org.org_roles) {
58
- const org_role = org.org_roles.shift()
59
- return org_role.admin
60
- }
61
- if (org.is_owner === true)
62
- return true
63
- return false
64
- })
65
- organizations.forEach(org => {
66
- result[org.name] = org.uid
67
- })
68
- resolve(result)
69
- })
70
- })
64
+ return new Promise((resolve, reject) => {
65
+ let result = {};
66
+ managementAPIClient
67
+ .getUser({ include_orgs_roles: true })
68
+ .then((response) => {
69
+ let organizations = response.organizations.filter((org) => {
70
+ if (org.org_roles) {
71
+ const org_role = org.org_roles.shift();
72
+ return org_role.admin;
73
+ }
74
+ return org.is_owner === true;
75
+ });
76
+ organizations.forEach((org) => {
77
+ result[org.name] = org.uid;
78
+ });
79
+ resolve(result);
80
+ })
81
+ .catch((error) => reject(error));
82
+ });
71
83
  }
72
84
 
73
85
  function chooseStack(managementAPIClient, orgUid) {
74
- return new Promise(async resolve => {
75
- let stacks = await getStacks(managementAPIClient, orgUid)
76
- let stackList = Object.keys(stacks)
77
- stackList.push(config.cancelString)
78
-
79
- let chooseStack = [{
80
- type: 'list',
81
- name: 'chosenStack',
82
- message: 'Choose a Stack',
83
- choices: stackList
84
- }]
85
-
86
- inquirer.prompt(chooseStack).then(({chosenStack}) => {
87
- if (chosenStack === config.cancelString)
88
- exitProgram()
89
- resolve({ name: chosenStack, apiKey: stacks[chosenStack] })
90
- })
91
- })
86
+ return new Promise(async (resolve, reject) => {
87
+ try {
88
+ let stacks = await getStacks(managementAPIClient, orgUid);
89
+ let stackList = Object.keys(stacks);
90
+ stackList.push(config.cancelString);
91
+
92
+ let _chooseStack = [
93
+ {
94
+ type: 'list',
95
+ name: 'chosenStack',
96
+ message: 'Choose a Stack',
97
+ choices: stackList,
98
+ },
99
+ ];
100
+
101
+ inquirer.prompt(_chooseStack).then(({ chosenStack }) => {
102
+ if (chosenStack === config.cancelString) exitProgram();
103
+ resolve({ name: chosenStack, apiKey: stacks[chosenStack] });
104
+ });
105
+ } catch (error) {
106
+ reject(error);
107
+ }
108
+ });
92
109
  }
93
110
 
94
111
  function getStacks(managementAPIClient, orgUid) {
95
- // Adding a query object in query, because it throws an error
96
- // the error is coming from query function lib/entity.js, @contentstack/management pacakge
97
- // where params.query is being set
98
- return new Promise(resolve => {
99
- let result = {}
100
- managementAPIClient.stack({organization_uid: orgUid}).query({query: {}}).find().then(stacks => {
101
- stacks.items.forEach(stack => {
102
- result[stack.name] = stack.api_key
103
- })
104
- resolve(result)
105
- })
106
- })
112
+ // Adding a query object in query, because it throws an error
113
+ // the error is coming from query function lib/entity.js, @contentstack/management pacakge
114
+ // where params.query is being set
115
+ return new Promise((resolve, reject) => {
116
+ let result = {};
117
+ managementAPIClient
118
+ .stack({ organization_uid: orgUid })
119
+ .query({ query: {} })
120
+ .find()
121
+ .then((stacks) => {
122
+ stacks.items.forEach((stack) => {
123
+ result[stack.name] = stack.api_key;
124
+ });
125
+ resolve(result);
126
+ })
127
+ .catch((error) => {
128
+ reject(error);
129
+ });
130
+ });
107
131
  }
108
132
 
109
- function chooseContentType(managementAPIClient, stackApiKey) {
110
- return new Promise(async resolve => {
111
- let contentTypes = await getContentTypes(managementAPIClient, stackApiKey)
112
- let contentTypesList = Object.values(contentTypes)
113
- // contentTypesList.push(config.cancelString)
114
-
115
- let chooseContentType = [{
116
- type: 'checkbox',
117
- message: 'Choose Content Type',
118
- choices: contentTypesList,
119
- name: 'chosenContentTypes',
120
- loop: false
121
- }]
122
-
123
- inquirer.prompt(chooseContentType).then(({chosenContentTypes}) => {
124
- // if (chosenContentType === config.cancelString)
125
- // exitProgram()
126
- resolve(chosenContentTypes)
127
- })
128
- })
133
+ function chooseContentType(stack, skip) {
134
+ return new Promise(async (resolve) => {
135
+ let contentTypes = await getContentTypes(stack, skip);
136
+ let contentTypesList = Object.values(contentTypes);
137
+ // contentTypesList.push(config.cancelString)
138
+
139
+ let _chooseContentType = [
140
+ {
141
+ type: 'checkbox',
142
+ message: 'Choose Content Type',
143
+ choices: contentTypesList,
144
+ name: 'chosenContentTypes',
145
+ loop: false,
146
+ },
147
+ ];
148
+
149
+ inquirer.prompt(_chooseContentType).then(({ chosenContentTypes }) => {
150
+ resolve(chosenContentTypes);
151
+ });
152
+ });
129
153
  }
130
154
 
131
- function getContentTypes(managementAPIClient, stackApiKey) {
132
- return new Promise(resolve => {
133
- let result = {}
134
- managementAPIClient.stack({api_key: stackApiKey}).contentType().query().find().then(contentTypes => {
135
- contentTypes.items.forEach(contentType => {
136
- result[contentType.title] = contentType.uid
137
- })
138
- resolve(result)
139
- })
140
- })
155
+ function chooseInMemContentTypes(contentTypesList) {
156
+ return new Promise(async (resolve, reject) => {
157
+ let _chooseContentType = [
158
+ {
159
+ type: 'checkbox-plus',
160
+ message: 'Choose Content Type',
161
+ choices: contentTypesList,
162
+ name: 'chosenContentTypes',
163
+ loop: false,
164
+ highlight: true,
165
+ searchable: true,
166
+ source: (_, input) => {
167
+ input = input || '';
168
+ const inputArray = input.split(' ');
169
+ return new Promise((resolveSource) => {
170
+ const contentTypes = contentTypesList.filter((contentType) => {
171
+ let shouldInclude = true;
172
+ inputArray.forEach((inputChunk) => {
173
+ // if any term to filter by doesn't exist, exclude
174
+ if (!contentType.toLowerCase().includes(inputChunk.toLowerCase())) {
175
+ shouldInclude = false;
176
+ }
177
+ });
178
+ return shouldInclude;
179
+ });
180
+ resolveSource(contentTypes);
181
+ });
182
+ },
183
+ },
184
+ ];
185
+ inquirer.prompt(_chooseContentType).then(({ chosenContentTypes }) => {
186
+ if (chosenContentTypes.length === 0) {
187
+ reject('Please select atleast one content type.');
188
+ }
189
+ resolve(chosenContentTypes);
190
+ });
191
+ });
141
192
  }
142
193
 
143
- function chooseLanguage(managementAPIClient, stackApiKey) {
144
- return new Promise(async resolve => {
145
- let languages = await getLanguages(managementAPIClient, stackApiKey)
146
- let languagesList = Object.keys(languages)
147
- languagesList.push(config.cancelString)
148
-
149
- let chooseLanguage = [{
150
- type: 'list',
151
- message: 'Choose Language',
152
- choices: languagesList,
153
- name: 'chosenLanguage'
154
- }]
155
-
156
- inquirer.prompt(chooseLanguage).then(({chosenLanguage}) => {
157
- if (chosenLanguage === config.cancelString)
158
- exitProgram()
159
- resolve({ name: chosenLanguage, code: languages[chosenLanguage] })
160
- })
161
- })
194
+ function getContentTypes(stack, skip) {
195
+ return new Promise((resolve, reject) => {
196
+ let result = {};
197
+ stack
198
+ .contentType()
199
+ .query({ skip: skip * 100 })
200
+ .find()
201
+ .then((contentTypes) => {
202
+ contentTypes.items.forEach((contentType) => {
203
+ result[contentType.title] = contentType.uid;
204
+ });
205
+ resolve(result);
206
+ })
207
+ .catch((error) => reject(error));
208
+ });
162
209
  }
163
210
 
164
- function getLanguages(managementAPIClient, stackApiKey) {
165
- return new Promise(resolve => {
166
- let result = {}
167
- managementAPIClient.stack({api_key: stackApiKey}).locale().query().find().then(languages => {
168
- languages.items.forEach(language => {
169
- result[language.name] = language.code
170
- })
171
- resolve(result)
172
- })
173
- })
211
+ function chooseLanguage(stack) {
212
+ return new Promise(async (resolve) => {
213
+ let languages = await getLanguages(stack);
214
+ let languagesList = Object.keys(languages);
215
+ languagesList.push(config.cancelString);
216
+
217
+ let _chooseLanguage = [
218
+ {
219
+ type: 'list',
220
+ message: 'Choose Language',
221
+ choices: languagesList,
222
+ name: 'chosenLanguage',
223
+ },
224
+ ];
225
+
226
+ inquirer.prompt(_chooseLanguage).then(({ chosenLanguage }) => {
227
+ if (chosenLanguage === config.cancelString) exitProgram();
228
+ resolve({ name: chosenLanguage, code: languages[chosenLanguage] });
229
+ });
230
+ });
174
231
  }
175
232
 
176
- function getEntries(managementAPIClient, stackApiKey, contentType, language, skip) {
177
- return new Promise(resolve => {
178
- managementAPIClient
179
- .stack({api_key: stackApiKey})
180
- .contentType(contentType)
181
- .entry()
182
- .query({include_publish_details: true, locale: language, skip: skip * 100})
183
- .find()
184
- .then(entries => resolve(entries))
185
- })
233
+ function getLanguages(stack) {
234
+ return new Promise((resolve, reject) => {
235
+ let result = {};
236
+ stack
237
+ .locale()
238
+ .query()
239
+ .find()
240
+ .then((languages) => {
241
+ languages.items.forEach((language) => {
242
+ result[language.name] = language.code;
243
+ });
244
+ resolve(result);
245
+ })
246
+ .catch((error) => reject(error));
247
+ });
186
248
  }
187
249
 
188
- function getEntriesCount(managementAPIClient, stackApiKey, contentType, language) {
189
- return new Promise((resolve) => {
190
- managementAPIClient
191
- .stack({ api_key: stackApiKey })
192
- .contentType(contentType)
193
- .entry()
194
- .query({ include_publish_details: true, locale: language })
195
- .count()
196
- .then((entriesData) => resolve(entriesData.entries));
197
- });
198
- }
250
+ function getEntries(stack, contentType, language, skip) {
251
+ return new Promise((resolve, reject) => {
252
+ stack
253
+ .contentType(contentType)
254
+ .entry()
255
+ .query({ include_publish_details: true, locale: language, skip: skip * 100 })
256
+ .find()
257
+ .then((entries) => resolve(entries))
258
+ .catch((error) => reject(error));
259
+ });
260
+ }
199
261
 
200
- function getEnvironments(managementAPIClient, stackApiKey) {
201
- let result = {}
202
- return managementAPIClient.stack({api_key: stackApiKey}).environment().query().find().then(environments => {
203
- environments.items.forEach(env => { result[env['uid']] = env['name']})
204
- return result
205
- })
262
+ function getEntriesCount(stack, contentType, language) {
263
+ return new Promise((resolve, reject) => {
264
+ stack
265
+ .contentType(contentType)
266
+ .entry()
267
+ .query({ include_publish_details: true, locale: language })
268
+ .count()
269
+ .then((entriesData) => resolve(entriesData.entries))
270
+ .catch((error) => reject(formatError(error)));
271
+ });
272
+ }
273
+
274
+ function getEnvironments(stack) {
275
+ let result = {};
276
+ return stack
277
+ .environment()
278
+ .query()
279
+ .find()
280
+ .then((environments) => {
281
+ environments.items.forEach((env) => {
282
+ result[env['uid']] = env['name'];
283
+ });
284
+ return result;
285
+ });
286
+ }
287
+
288
+ function getContentTypeCount(stack) {
289
+ return new Promise((resolve, reject) => {
290
+ stack
291
+ .contentType()
292
+ .query()
293
+ .count()
294
+ .then((contentTypes) => {
295
+ resolve(contentTypes.content_types);
296
+ })
297
+ .catch((error) => reject(error));
298
+ });
206
299
  }
207
300
 
208
301
  function exitProgram() {
209
- debug("Exiting")
210
- // eslint-disable-next-line no-undef
211
- process.exit()
302
+ debug('Exiting');
303
+ // eslint-disable-next-line no-undef
304
+ process.exit();
212
305
  }
213
306
 
214
307
  function cleanEntries(entries, language, environments, contentTypeUid) {
215
- const filteredEntries = entries.filter(entry => {
216
- return (entry['locale'] === language && entry.publish_details.length > 0)
217
- })
218
-
219
- const flatEntries = filteredEntries.map(entry => {
220
- let workflow = ''
221
- const envArr = []
222
- entry.publish_details.forEach(env => {
223
- envArr.push(JSON.stringify([environments[env['environment']], env['locale']]))
224
- })
225
- delete entry.publish_details
308
+ const filteredEntries = entries.filter((entry) => {
309
+ return entry['locale'] === language && entry.publish_details.length > 0;
310
+ });
311
+
312
+ return filteredEntries.map((entry) => {
313
+ let workflow = '';
314
+ const envArr = [];
315
+ entry.publish_details.forEach((env) => {
316
+ envArr.push(JSON.stringify([environments[env['environment']], env['locale']]));
317
+ });
318
+ delete entry.publish_details;
226
319
  if ('_workflow' in entry) {
227
- workflow = entry['_workflow']['name']
228
- delete entry['_workflow']
320
+ workflow = entry['_workflow']['name'];
321
+ delete entry['_workflow'];
229
322
  }
230
- entry = flatten(entry)
231
- entry['publish_details'] = envArr
232
- entry['_workflow'] = workflow
233
- entry['ACL'] = JSON.stringify({}) // setting ACL to empty obj
234
- entry['content_type_uid'] = contentTypeUid // content_type_uid is being returned as 'uid' from the sdk for some reason
323
+ entry = flatten(entry);
324
+ entry['publish_details'] = envArr;
325
+ entry['_workflow'] = workflow;
326
+ entry['ACL'] = JSON.stringify({}); // setting ACL to empty obj
327
+ entry['content_type_uid'] = contentTypeUid; // content_type_uid is being returned as 'uid' from the sdk for some reason
235
328
  // entry['url'] might also be wrong
236
- delete entry.stackHeaders
237
- delete entry.update
238
- delete entry.delete
239
- delete entry.fetch
240
- delete entry.publish
241
- delete entry.unpublish
242
- delete entry.import
243
- delete entry.publishRequest
244
- return entry
245
- })
246
-
247
- return flatEntries
329
+ delete entry.stackHeaders;
330
+ delete entry.update;
331
+ delete entry.delete;
332
+ delete entry.fetch;
333
+ delete entry.publish;
334
+ delete entry.unpublish;
335
+ delete entry.import;
336
+ delete entry.publishRequest;
337
+ return entry;
338
+ });
248
339
  }
249
340
 
250
341
  function getDateTime() {
251
- let date = new Date()
252
- let dateTime = date.toLocaleString().split(',')
253
- dateTime[0] = dateTime[0].split('/').join('-')
254
- dateTime[1] = dateTime[1].trim() // trim the space before time
255
- dateTime[1] = dateTime[1].split(' ').join('')
256
- return dateTime.join('_')
342
+ let date = new Date();
343
+ let dateTime = date.toLocaleString().split(',');
344
+ dateTime[0] = dateTime[0].split('/').join('-');
345
+ dateTime[1] = dateTime[1].trim(); // trim the space before time
346
+ dateTime[1] = dateTime[1].split(' ').join('');
347
+ return dateTime.join('_');
257
348
  }
258
349
 
259
- function write(command, entries, fileName) {
260
- // eslint-disable-next-line no-undef
261
- if (process.cwd().split(delimeter).pop() !== 'data' && !fs.existsSync(directory)) {
262
- mkdirp.sync(directory)
263
- }
264
- // eslint-disable-next-line no-undef
265
- if (process.cwd().split(delimeter).pop() !== 'data') {
266
- // eslint-disable-next-line no-undef
267
- process.chdir(directory)
268
- }
350
+ function write(command, entries, fileName, message) {
351
+ // eslint-disable-next-line no-undef
352
+ if (process.cwd().split(delimeter).pop() !== 'data' && !fs.existsSync(directory)) {
353
+ mkdirp.sync(directory);
354
+ }
355
+ // eslint-disable-next-line no-undef
356
+ if (process.cwd().split(delimeter).pop() !== 'data') {
357
+ // eslint-disable-next-line no-undef
358
+ process.chdir(directory);
359
+ }
269
360
  // eslint-disable-next-line no-undef
270
- command.log(`Writing entries to file: ${process.cwd()}${delimeter}${fileName}`)
271
- fastcsv
272
- .writeToPath(fileName, entries, {headers: true})
361
+ command.log(`Writing ${message} to file: ${process.cwd()}${delimeter}${fileName}`);
362
+ fastcsv.writeToPath(fileName, entries, { headers: true });
273
363
  }
274
364
 
275
365
  function startupQuestions() {
276
- return new Promise(resolve => {
277
- let actions = [{
278
- type: 'list',
279
- name: 'action',
280
- message: 'Choose Action',
281
- choices: [config.exportEntries, config.exportUsers, 'Exit'],
282
- }]
283
- inquirer.prompt(actions).then(answers => {
284
- if(answers.action === 'Exit')
285
- exitProgram()
286
- resolve(answers.action)
287
- })
288
- })
366
+ return new Promise((resolve) => {
367
+ let actions = [
368
+ {
369
+ type: 'list',
370
+ name: 'action',
371
+ message: 'Choose Action',
372
+ choices: [config.exportEntries, config.exportUsers, 'Exit'],
373
+ },
374
+ ];
375
+ inquirer.prompt(actions).then((answers) => {
376
+ if (answers.action === 'Exit') exitProgram();
377
+ resolve(answers.action);
378
+ });
379
+ });
289
380
  }
290
381
 
291
382
  function getOrgUsers(managementAPIClient, orgUid, ecsv) {
292
- return new Promise((resolve, reject) => {
293
- managementAPIClient
294
- .getUser({include_orgs_roles: true})
295
- .then(response => {
296
- let organization = response.organizations.filter(org => org.uid === orgUid).pop()
297
- if (organization.is_owner === true) {
298
- let cma = ecsv.region.cma
299
- let authtoken = ecsv.authToken
300
- return axios.get(`${cma}/v3/organizations/${organization.uid}/share`, { headers: { 'authtoken': authtoken }})
301
- .then(response => resolve({ items: response.data.shares }))
302
- }
303
- if (!organization.getInvitations) {
304
- return reject(new Error(config.adminError))
305
- }
306
- organization.getInvitations().then(users => resolve(users))
307
- })
308
- })
383
+ return new Promise((resolve, reject) => {
384
+ managementAPIClient
385
+ .getUser({ include_orgs_roles: true })
386
+ .then((response) => {
387
+ let organization = response.organizations.filter((org) => org.uid === orgUid).pop();
388
+ if (organization.is_owner === true) {
389
+ let cma = ecsv.region.cma;
390
+ let authtoken = ecsv.authToken;
391
+ return HttpClient.create()
392
+ .headers({ authtoken: authtoken })
393
+ .get(`${cma}/v3/organizations/${organization.uid}/share`)
394
+ .then((_response) => resolve({ items: _response.data.shares }));
395
+ }
396
+ if (!organization.getInvitations) {
397
+ return reject(new Error(config.adminError));
398
+ }
399
+ organization.getInvitations().then((users) => resolve(users));
400
+ })
401
+ .catch((error) => reject(error));
402
+ });
309
403
  }
310
404
 
311
405
  function getMappedUsers(users) {
312
- let mappedUsers = {}
313
- users.items.forEach(user => { mappedUsers[user.user_uid] = user.email })
314
- mappedUsers['System'] = 'System'
315
- return mappedUsers
406
+ let mappedUsers = {};
407
+ users.items.forEach((user) => {
408
+ mappedUsers[user.user_uid] = user.email;
409
+ });
410
+ mappedUsers['System'] = 'System';
411
+ return mappedUsers;
316
412
  }
317
413
 
318
414
  function getMappedRoles(roles) {
319
- let mappedRoles = {}
320
- roles.items.forEach(role => { mappedRoles[role.uid] = role.name })
321
- return mappedRoles
415
+ let mappedRoles = {};
416
+ roles.items.forEach((role) => {
417
+ mappedRoles[role.uid] = role.name;
418
+ });
419
+ return mappedRoles;
322
420
  }
323
421
 
324
422
  function getOrgRoles(managementAPIClient, orgUid, ecsv) {
325
- return new Promise((resolve, reject) => {
326
- managementAPIClient
327
- .getUser({include_orgs_roles: true})
328
- .then(response => {
329
- let organization = response.organizations.filter(org => org.uid === orgUid).pop()
330
- if (organization.is_owner === true) {
331
- let cma = ecsv.region.cma
332
- let authtoken = ecsv.authToken
333
- return axios.get(`${cma}/v3/organizations/${organization.uid}/roles`, { headers: { 'authtoken': authtoken }})
334
- .then(response => resolve({ items: response.data.roles }))
335
- }
336
- if (!organization.roles) {
337
- return reject(new Error(config.adminError))
338
- }
339
- organization.roles().then(roles => resolve(roles))
340
- })
341
- })
423
+ return new Promise((resolve, reject) => {
424
+ managementAPIClient
425
+ .getUser({ include_orgs_roles: true })
426
+ .then((response) => {
427
+ let organization = response.organizations.filter((org) => org.uid === orgUid).pop();
428
+ if (organization.is_owner === true) {
429
+ let cma = ecsv.region.cma;
430
+ let authtoken = ecsv.authToken;
431
+ return axios
432
+ .get(`${cma}/v3/organizations/${organization.uid}/roles`, { headers: { authtoken: authtoken } })
433
+ .then((_response) => resolve({ items: _response.data.roles }));
434
+ }
435
+ if (!organization.roles) {
436
+ return reject(new Error(config.adminError));
437
+ }
438
+ organization.roles().then((roles) => resolve(roles));
439
+ })
440
+ .catch((error) => reject(error));
441
+ });
342
442
  }
343
443
 
344
444
  function determineUserOrgRole(user, roles) {
345
- let roleName = 'No Role'
346
- let roleUid = user.org_roles || []
347
- if (roleUid.length > 0) {
348
- roleUid = roleUid.shift()
349
- roleName = roles[roleUid]
350
- }
351
- if (user.is_owner) {
352
- roleName = 'Owner'
353
- }
354
- return roleName
445
+ let roleName = 'No Role';
446
+ let roleUid = user.org_roles || [];
447
+ if (roleUid.length > 0) {
448
+ roleUid = roleUid.shift();
449
+ roleName = roles[roleUid];
450
+ }
451
+ if (user.is_owner) {
452
+ roleName = 'Owner';
453
+ }
454
+ return roleName;
355
455
  }
356
456
 
357
457
  function cleanOrgUsers(orgUsers, mappedUsers, mappedRoles) {
358
- const userList = []
359
- orgUsers.items.forEach(user => {
360
- let invitedBy
361
- let formattedUser = {}
362
- try {
363
- invitedBy = mappedUsers[user['invited_by']]
364
- } catch (error) {
365
- invitedBy = 'System'
366
- }
367
- formattedUser['Email'] = user['email']
368
- formattedUser['User UID'] = user['user_uid']
369
- formattedUser['Organization Role'] = determineUserOrgRole(user, mappedRoles)
370
- formattedUser['Status'] = user['status']
371
- formattedUser['Invited By'] = invitedBy
372
- formattedUser['Created Time'] = user['created_at']
373
- formattedUser['Updated Time'] = user['updated_at']
374
- userList.push(formattedUser)
375
- })
376
- return userList
458
+ const userList = [];
459
+ orgUsers.items.forEach((user) => {
460
+ let invitedBy;
461
+ let formattedUser = {};
462
+ try {
463
+ invitedBy = mappedUsers[user['invited_by']];
464
+ } catch (error) {
465
+ invitedBy = 'System';
466
+ }
467
+ formattedUser['Email'] = user['email'];
468
+ formattedUser['User UID'] = user['user_uid'];
469
+ formattedUser['Organization Role'] = determineUserOrgRole(user, mappedRoles);
470
+ formattedUser['Status'] = user['status'];
471
+ formattedUser['Invited By'] = invitedBy;
472
+ formattedUser['Created Time'] = user['created_at'];
473
+ formattedUser['Updated Time'] = user['updated_at'];
474
+ userList.push(formattedUser);
475
+ });
476
+ return userList;
377
477
  }
378
478
 
379
479
  function kebabize(str) {
380
- return str.split(' ').map((word) => word.toLowerCase()).join('-')
480
+ return str
481
+ .split(' ')
482
+ .map((word) => word.toLowerCase())
483
+ .join('-');
381
484
  }
382
485
 
383
486
  // https://stackoverflow.com/questions/19098797/fastest-way-to-flatten-un-flatten-nested-json-objects
384
487
  function flatten(data) {
385
- var result = {};
386
- function recurse (cur, prop) {
387
- if (Object(cur) !== cur) {
388
- result[prop] = cur;
389
- } else if (Array.isArray(cur)) {
390
- for(var i=0, l=cur.length; i<l; i++)
391
- recurse(cur[i], prop + "[" + i + "]");
392
- if (l == 0)
393
- result[prop] = [];
394
- } else {
395
- var isEmpty = true;
396
- for (var p in cur) {
397
- isEmpty = false;
398
- recurse(cur[p], prop ? prop+"."+p : p);
399
- }
400
- if (isEmpty && prop)
401
- result[prop] = {};
402
- }
488
+ let result = {};
489
+ function recurse(cur, prop) {
490
+ if (Object(cur) !== cur) {
491
+ result[prop] = cur;
492
+ } else if (Array.isArray(cur)) {
493
+ let i, l;
494
+ for (i = 0, l = cur.length; i < l; i++) recurse(cur[i], prop + '[' + i + ']');
495
+ if (l == 0) result[prop] = [];
496
+ } else {
497
+ let isEmpty = true;
498
+ for (let p in cur) {
499
+ isEmpty = false;
500
+ recurse(cur[p], prop ? prop + '.' + p : p);
501
+ }
502
+ if (isEmpty && prop) result[prop] = {};
503
+ }
504
+ }
505
+ recurse(data, '');
506
+ return result;
507
+ }
508
+
509
+ function formatError(error) {
510
+ try {
511
+ if (typeof error === 'string') {
512
+ error = JSON.parse(error);
513
+ } else {
514
+ error = JSON.parse(error.message);
403
515
  }
404
- recurse(data, "");
405
- return result;
516
+ } catch (e) {}
517
+ let message = error.errorMessage || error.error_message || error.message || error;
518
+ if (error.errors && Object.keys(error.errors).length > 0) {
519
+ Object.keys(error.errors).forEach((e) => {
520
+ let entity = e;
521
+ if (e === 'authorization') entity = 'Management Token';
522
+ if (e === 'api_key') entity = 'Stack API key';
523
+ if (e === 'uid') entity = 'Content Type';
524
+ if (e === 'access_token') entity = 'Delivery Token';
525
+ message += ' ' + [entity, error.errors[e]].join(' ');
526
+ });
527
+ }
528
+ return message;
406
529
  }
407
530
 
408
531
  module.exports = {
409
- chooseOrganization: chooseOrganization,
410
- chooseStack: chooseStack,
532
+ chooseOrganization: chooseOrganization,
533
+ chooseStack: chooseStack,
411
534
  chooseContentType: chooseContentType,
412
535
  chooseLanguage: chooseLanguage,
413
536
  getEntries: getEntries,
@@ -425,5 +548,9 @@ module.exports = {
425
548
  getOrganizationsWhereUserIsAdmin: getOrganizationsWhereUserIsAdmin,
426
549
  kebabize: kebabize,
427
550
  flatten: flatten,
551
+ getContentTypeCount: getContentTypeCount,
552
+ getContentTypes: getContentTypes,
553
+ chooseInMemContentTypes: chooseInMemContentTypes,
428
554
  getEntriesCount: getEntriesCount,
429
- }
555
+ formatError: formatError,
556
+ };