@contentstack/cli-cm-export 0.1.1-beta.9 → 1.1.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.
@@ -0,0 +1,154 @@
1
+ /*!
2
+ * Contentstack Export
3
+ * Copyright (c) 2019 Contentstack LLC
4
+ * MIT Licensed
5
+ */
6
+ const _ = require('lodash')
7
+ const path = require('path');
8
+ const chalk = require('chalk');
9
+ const mkdirp = require('mkdirp');
10
+ const eachOf = require('async/eachOf');
11
+ const { cliux, configHandler, HttpClient, NodeCrypto } = require('@contentstack/cli-utilities');
12
+
13
+ let config = require('../../config/default');
14
+ const { writeFile } = require('../util/helper');
15
+ const { addlogs: log } = require('../util/log');
16
+ let stack = require('../util/contentstack-management-sdk');
17
+ const { getInstalledExtensions } = require('../util/marketplace-app-helper')
18
+
19
+ let client
20
+ let marketplaceAppConfig = config.modules.marketplace_apps;
21
+
22
+ function exportMarketplaceApps() {
23
+ this.marketplaceAppPath = null
24
+ this.developerHuBaseUrl = null
25
+ this.start = async (credentialConfig) => {
26
+ config = credentialConfig;
27
+ client = stack.Client(config);
28
+ this.developerHuBaseUrl = await this.getDeveloperHubUrl()
29
+
30
+ if (!config.auth_token) {
31
+ cliux.print('WARNING!!! To export Marketplace apps, you must be logged in. Please check csdx auth:login --help to log in', { color: 'yellow' })
32
+ return Promise.resolve()
33
+ }
34
+
35
+ log(credentialConfig, 'Starting marketplace app export', 'success');
36
+ this.marketplaceAppPath = path.resolve(
37
+ config.data, config.branchName || '',
38
+ marketplaceAppConfig.dirName
39
+ );
40
+ mkdirp.sync(this.marketplaceAppPath);
41
+
42
+ return this.exportInstalledExtensions()
43
+ }
44
+
45
+ this.exportInstalledExtensions = () => {
46
+ const self = this
47
+
48
+ return new Promise(async function (resolve, reject) {
49
+ getInstalledExtensions(config)
50
+ .then(async (items) => {
51
+ const installedApps = _.map(
52
+ _.filter(items, 'app_uid'),
53
+ ({ uid, title, app_uid, app_installation_uid, type }) => ({ title, uid, app_uid, app_installation_uid, type })
54
+ )
55
+ const headers = {
56
+ authtoken: config.auth_token,
57
+ organization_uid: config.org_uid
58
+ }
59
+ const httpClient = new HttpClient().headers(headers);
60
+ const nodeCrypto = new NodeCrypto()
61
+ const developerHubApps = await httpClient.get(`${self.developerHuBaseUrl}/apps`)
62
+ .then(({ data: { data } }) => data)
63
+ .catch(err => {
64
+ console.log(err)
65
+ }) || []
66
+
67
+ eachOf(_.uniqBy(installedApps, 'app_uid'), (app, _key, cb) => {
68
+ log(config, `Exporting ${app.title} app and it's config.`, 'success')
69
+ const listOfIndexToBeUpdated = _.map(
70
+ installedApps,
71
+ ({ app_uid }, index) => (app_uid === app.app_uid ? index : undefined)
72
+ ).filter(val => val !== undefined)
73
+
74
+ httpClient.get(`${self.developerHuBaseUrl}/installations/${app.app_installation_uid}/installationData`)
75
+ .then(({ data: result }) => {
76
+ const { data, error } = result
77
+ const developerHubApp = _.find(developerHubApps, { uid: app.app_uid })
78
+
79
+ _.forEach(listOfIndexToBeUpdated, (index, i) => {
80
+ if (developerHubApp) {
81
+ installedApps[index]['visibility'] = developerHubApp.visibility
82
+ installedApps[index]['manifest'] = _.pick(
83
+ developerHubApp,
84
+ ['name', 'description', 'icon', 'target_type', 'ui_location', 'webhook', 'oauth'] // NOTE keys can be passed to install new app in the developer hub
85
+ )
86
+ }
87
+
88
+ if (_.has(data, 'configuration') || _.has(data, 'server_configuration')) {
89
+ const { configuration, server_configuration } = data
90
+
91
+ if (!_.isEmpty(configuration)) {
92
+ installedApps[index]['configuration'] = nodeCrypto.encrypt(configuration)
93
+ }
94
+ if (!_.isEmpty(server_configuration)) {
95
+ installedApps[index]['server_configuration'] = nodeCrypto.encrypt(server_configuration)
96
+ }
97
+
98
+ if (i === 0) {
99
+ log(config, `Exported ${app.title} app and it's config.`, 'success')
100
+ }
101
+ } else if (error) {
102
+ console.log(error)
103
+ if (i === 0) {
104
+ log(config, `Error on exporting ${app.title} app and it's config.`, 'error')
105
+ }
106
+ }
107
+ })
108
+
109
+ cb()
110
+ })
111
+ .catch(err => {
112
+ console.log(err)
113
+ cb()
114
+ })
115
+ }, () => {
116
+ if (!_.isEmpty(installedApps)) {
117
+ writeFile(
118
+ path.join(self.marketplaceAppPath, marketplaceAppConfig.fileName),
119
+ installedApps
120
+ )
121
+
122
+ log(config, chalk.green('All the marketplace apps have been exported successfully'), 'success')
123
+ } else {
124
+ log(config, 'No marketplace apps found', 'success')
125
+ }
126
+
127
+ resolve()
128
+ })
129
+ }).catch(reject)
130
+ })
131
+ }
132
+
133
+ this.getDeveloperHubUrl = async () => {
134
+ const { cma, name } = configHandler.get('region') || {}
135
+ let developerHubBaseUrl = config.developerHubUrls[cma]
136
+
137
+ if (!developerHubBaseUrl) {
138
+ developerHubBaseUrl = await cliux.inquire({
139
+ type: 'input',
140
+ name: 'name',
141
+ validate: (url) => {
142
+ if (!url) return 'Developer-hub URL cant be empty.'
143
+
144
+ return true
145
+ },
146
+ message: `Enter the developer-hub base URL for the ${name} region - `,
147
+ })
148
+ }
149
+
150
+ return developerHubBaseUrl.startsWith('http') ? developerHubBaseUrl : `https://${developerHubBaseUrl}`
151
+ }
152
+ }
153
+
154
+ module.exports = new exportMarketplaceApps();
@@ -4,64 +4,104 @@
4
4
  * MIT Licensed
5
5
  */
6
6
 
7
- var chalk = require('chalk')
8
- var mkdirp = require('mkdirp')
9
- var path = require('path')
7
+ var chalk = require('chalk');
8
+ var mkdirp = require('mkdirp');
9
+ var path = require('path');
10
10
 
11
- var app = require('../../app')
12
- var helper = require('../util/helper')
13
- var {addlogs} = require('../util/log')
14
- const stack = require('../util/contentstack-management-sdk')
11
+ var app = require('../../app');
12
+ var helper = require('../util/helper');
13
+ var { addlogs } = require('../util/log');
14
+ const stack = require('../util/contentstack-management-sdk');
15
15
 
16
+ let config = require('../../config/default');
16
17
 
17
- let config = require('../../config/default')
18
+ var stackConfig = config.modules.stack;
19
+ let client;
18
20
 
19
- var stackConfig = config.modules.stack
20
- let client
21
-
22
- function ExportStack () {
21
+ function ExportStack() {
23
22
  this.requestOption = {
24
23
  uri: config.host + config.apis.stacks,
25
24
  headers: config.headers,
26
- json: true
27
- }
25
+ json: true,
26
+ };
28
27
  }
29
28
 
30
- ExportStack.prototype.start = function (credentialConfig) {
31
- config = credentialConfig
32
- client = stack.Client(config)
33
- if (!config.preserveStackVersion && !config.hasOwnProperty("master_locale")) {
34
- return new Promise((resolve, reject) => {
35
- var result = client.stack({ api_key: credentialConfig.source_stack, management_token: credentialConfig.management_token }).locale().query()
36
- result.find()
37
- .then(response => {
38
- var masterLocalObj = response.items.filter(obj => {
39
- if (obj.fallback_locale === null) {
40
- return obj
41
- }
42
- });
43
- return resolve(masterLocalObj[0])
44
- }).catch(error => {
45
- return reject(error)
29
+ ExportStack.prototype.start = async function (credentialConfig) {
30
+ config = credentialConfig;
31
+ client = stack.Client(config);
32
+ const self = this
33
+
34
+ // NOTE get org uid
35
+ if (config.auth_token) {
36
+ const stack = await client
37
+ .stack({ api_key: config.source_stack, authtoken: config.auth_token })
38
+ .fetch()
39
+ .catch((error) => {
40
+ console.log(error)
46
41
  })
47
- })
48
- } else if(config.preserveStackVersion) {
49
- addlogs(config, 'Exporting stack details', 'success')
50
- let stackFolderPath = path.resolve(config.data, stackConfig.dirName)
51
- let stackContentsFile = path.resolve(stackFolderPath, stackConfig.fileName)
52
-
53
- mkdirp.sync(stackFolderPath)
54
-
42
+
43
+ if (stack && stack.org_uid) {
44
+ config.org_uid = stack.org_uid
45
+ }
46
+ }
47
+
48
+ if (!config.preserveStackVersion && !config.hasOwnProperty('master_locale')) {
49
+ const apiDetails = {
50
+ limit: 100,
51
+ skip: 0,
52
+ include_count: true,
53
+ }
54
+ return self.getLocales(apiDetails)
55
+ } else if (config.preserveStackVersion) {
56
+ addlogs(config, 'Exporting stack details', 'success');
57
+ let stackFolderPath = path.resolve(config.data, stackConfig.dirName);
58
+ let stackContentsFile = path.resolve(stackFolderPath, stackConfig.fileName);
59
+
60
+ mkdirp.sync(stackFolderPath);
61
+
55
62
  return new Promise((resolve, reject) => {
56
- return client.stack({api_key: config.source_stack}).fetch()
57
- .then(response => {
58
- helper.writeFile(stackContentsFile, response)
59
- addlogs(config, 'Exported stack details successfully!', 'success')
60
- return resolve(response)
61
- })
62
- .catch(reject)
63
- })
63
+ return client
64
+ .stack({ api_key: config.source_stack })
65
+ .fetch()
66
+ .then((response) => {
67
+ helper.writeFile(stackContentsFile, response);
68
+ addlogs(config, 'Exported stack details successfully!', 'success');
69
+ return resolve(response);
70
+ })
71
+ .catch(reject);
72
+ });
64
73
  }
74
+ };
75
+
76
+ ExportStack.prototype.getLocales = function (apiDetails) {
77
+ let self = this
78
+ return new Promise((resolve, reject) => {
79
+ const result = client
80
+ .stack({ api_key: config.source_stack, management_token: config.management_token })
81
+ .locale()
82
+ .query(apiDetails)
83
+
84
+ result
85
+ .find()
86
+ .then((response) => {
87
+ const masterLocalObj = response.items.find((obj) => {
88
+ if (obj.fallback_locale === null) {
89
+ return obj;
90
+ }
91
+ });
92
+ apiDetails.skip += apiDetails.limit;
93
+ if (masterLocalObj) { return resolve(masterLocalObj); }
94
+ else if (apiDetails.skip <= response.count) {
95
+ return resolve(self.getLocales(apiDetails))
96
+ }
97
+ else {
98
+ return reject('Master locale not found');
99
+ }
100
+ })
101
+ .catch((error) => {
102
+ return reject(error);
103
+ });
104
+ });
65
105
  }
66
106
 
67
- module.exports = new ExportStack()
107
+ module.exports = new ExportStack();
@@ -1,20 +1,20 @@
1
1
  /*!
2
- * Contentstack Export
3
- * Copyright (c) 2019 Contentstack LLC
4
- * MIT Licensed
5
- */
2
+ * Contentstack Export
3
+ * Copyright (c) 2019 Contentstack LLC
4
+ * MIT Licensed
5
+ */
6
6
 
7
- const mkdirp = require('mkdirp')
8
- const path = require('path')
9
- const chalk = require('chalk')
7
+ const mkdirp = require('mkdirp');
8
+ const path = require('path');
9
+ const chalk = require('chalk');
10
10
 
11
- const helper = require('../util/helper')
12
- const {addlogs} = require('../util/log')
11
+ const helper = require('../util/helper');
12
+ const { addlogs } = require('../util/log');
13
13
 
14
- let config = require('../../config/default')
15
- const stack = require('../util/contentstack-management-sdk')
16
- const webhooksConfig = config.modules.webhooks
17
- let client
14
+ let config = require('../../config/default');
15
+ const stack = require('../util/contentstack-management-sdk');
16
+ const webhooksConfig = config.modules.webhooks;
17
+ let client;
18
18
 
19
19
  // Create folder for environments
20
20
 
@@ -22,46 +22,54 @@ function ExportWebhooks() {
22
22
  this.requestOptions = {
23
23
  include_count: true,
24
24
  asc: 'updated_at',
25
- }
26
- this.master = {}
27
- this.webhooks = {}
25
+ };
26
+ this.master = {};
27
+ this.webhooks = {};
28
28
  }
29
29
 
30
30
  ExportWebhooks.prototype.start = function (credentialConfig) {
31
- addlogs(config, 'Starting webhooks export', 'success')
32
- this.master = {}
33
- this.webhooks = {}
34
- let self = this
35
- config = credentialConfig
36
- client = stack.Client(config)
37
- const webhooksFolderPath = path.resolve(config.data, (config.branchName || ""), webhooksConfig.dirName)
38
- mkdirp.sync(webhooksFolderPath)
31
+ addlogs(config, 'Starting webhooks export', 'success');
32
+ this.master = {};
33
+ this.webhooks = {};
34
+ let self = this;
35
+ config = credentialConfig;
36
+ client = stack.Client(config);
37
+ const webhooksFolderPath = path.resolve(config.data, config.branchName || '', webhooksConfig.dirName);
38
+ mkdirp.sync(webhooksFolderPath);
39
39
  return new Promise(function (resolve, reject) {
40
- client.stack({api_key: config.source_stack, management_token: config.management_token}).webhook().fetchAll(self.requestOptions)
41
- .then(webhooks => {
42
- if (webhooks.items.length !== 0) {
43
- for (var i = 0, total = webhooks.count; i < total; i++) {
44
- const webUid = webhooks.items[i].uid
45
- self.master[webUid] = ''
46
- self.webhooks[webUid] = webhooks.items[i]
47
- delete self.webhooks[webUid].uid
48
- delete self.webhooks[webUid].SYS_ACL
40
+ client
41
+ .stack({ api_key: config.source_stack, management_token: config.management_token })
42
+ .webhook()
43
+ .fetchAll(self.requestOptions)
44
+ .then((webhooks) => {
45
+ if (webhooks.items.length !== 0) {
46
+ for (var i = 0, total = webhooks.count; i < total; i++) {
47
+ const webUid = webhooks.items[i].uid;
48
+ self.master[webUid] = '';
49
+ self.webhooks[webUid] = webhooks.items[i];
50
+ delete self.webhooks[webUid].uid;
51
+ delete self.webhooks[webUid].SYS_ACL;
52
+ }
53
+ helper.writeFile(path.join(webhooksFolderPath, webhooksConfig.fileName), self.webhooks);
54
+ addlogs(config, chalk.green('All the webhooks have been exported successfully'), 'success');
55
+ return resolve();
49
56
  }
50
- helper.writeFile(path.join(webhooksFolderPath, webhooksConfig.fileName), self.webhooks)
51
- addlogs(config, chalk.green('All the webhooks have been exported successfully'), 'success')
52
- return resolve()
53
- }
54
- addlogs(config, 'No webhooks found', 'success')
55
- return resolve()
56
- }).catch(function (error) {
57
- if (error.statusCode === 401) {
58
- addlogs(config, chalk.red('You are not allowed to export webhooks, Unless you provide email and password in config'), 'error')
59
- return resolve()
60
- }
61
- addlogs(config, error, 'error')
62
- return reject()
63
- })
64
- })
65
- }
57
+ addlogs(config, 'No webhooks found', 'success');
58
+ return resolve();
59
+ })
60
+ .catch(function (error) {
61
+ if (error.statusCode === 401) {
62
+ addlogs(
63
+ config,
64
+ chalk.red('You are not allowed to export webhooks, Unless you provide email and password in config'),
65
+ 'error',
66
+ );
67
+ return resolve();
68
+ }
69
+ addlogs(config, error, 'error');
70
+ return reject();
71
+ });
72
+ });
73
+ };
66
74
 
67
- module.exports = new ExportWebhooks()
75
+ module.exports = new ExportWebhooks();
@@ -4,57 +4,98 @@
4
4
  * MIT Licensed
5
5
  */
6
6
 
7
- const mkdirp = require('mkdirp')
8
- const path = require('path')
9
- const chalk = require('chalk')
7
+ const mkdirp = require('mkdirp');
8
+ const path = require('path');
9
+ const chalk = require('chalk');
10
10
 
11
- let helper = require('../util/helper')
12
- let {addlogs} = require('../util/log')
11
+ let helper = require('../util/helper');
12
+ let { addlogs } = require('../util/log');
13
13
 
14
- const stack = require('../util/contentstack-management-sdk')
15
- let config = require('../../config/default')
16
- let workFlowConfig = config.modules.workflows
17
- let client
14
+ const stack = require('../util/contentstack-management-sdk');
15
+ let config = require('../../config/default');
16
+ let workFlowConfig = config.modules.workflows;
17
+ let client;
18
18
 
19
19
  function ExportWorkFlows() {
20
- this.workflows = {}
20
+ this.workflows = {};
21
21
  }
22
22
 
23
23
  ExportWorkFlows.prototype.start = function (credentialConfig) {
24
- addlogs(config, 'Starting workflow export', 'success')
25
- this.workflows = {}
26
- let self = this
27
- config = credentialConfig
28
- client = stack.Client(config)
29
-
30
- let workflowsFolderPath = path.resolve(config.data, (config.branchName || ""), workFlowConfig.dirName)
31
- mkdirp.sync(workflowsFolderPath)
24
+ addlogs(config, 'Starting workflow export', 'success');
25
+ this.workflows = {};
26
+ let self = this;
27
+ config = credentialConfig;
28
+ client = stack.Client(config);
29
+
30
+ let workflowsFolderPath = path.resolve(config.data, config.branchName || '', workFlowConfig.dirName);
31
+ mkdirp.sync(workflowsFolderPath);
32
32
  return new Promise(function (resolve, reject) {
33
- return client.stack({api_key: config.source_stack, management_token: config.management_token}).workflow().fetchAll()
34
- .then(response => {
35
- if (response.items.length !== 0) {
36
- response.items.forEach(function (workflow) {
37
- addlogs(config, workflow.name + ' workflow was exported successfully', 'success')
38
- self.workflows[workflow.uid] = workflow
39
- let deleteItems = config.modules.workflows.invalidKeys
40
- deleteItems.forEach(e => delete workflow[e])
41
- })
42
- addlogs(config, chalk.green('All the workflow have been exported successfully'), 'success')
43
- }
44
- if (response.items.length === 0) {
45
- addlogs(config, 'No workflow were found in the Stack', 'success')
33
+ return client
34
+ .stack({ api_key: config.source_stack, management_token: config.management_token })
35
+ .workflow()
36
+ .fetchAll()
37
+ .then(async (response) => {
38
+ try {
39
+ if (response.items.length) {
40
+ await getWorkflowsData(self, response.items);
41
+ addlogs(config, chalk.green('All the workflow have been exported successfully'), 'success');
42
+ }
43
+ if (!response.items.length) {
44
+ addlogs(config, 'No workflow were found in the Stack', 'success');
45
+ }
46
+ helper.writeFile(path.join(workflowsFolderPath, workFlowConfig.fileName), self.workflows);
47
+ return resolve();
48
+ } catch (error) {
49
+ return reject(error);
50
+ }
51
+ })
52
+ .catch(function (error) {
53
+ if (error.statusCode === 401) {
54
+ addlogs(
55
+ config,
56
+ chalk.red(
57
+ 'You are not allowed to export workflow, Unless you provide email and password in config',
58
+ 'error',
59
+ ),
60
+ );
61
+ return resolve();
62
+ }
63
+ addlogs(config, error, 'error');
64
+ return resolve();
65
+ });
66
+ });
67
+ };
68
+
69
+ const getWorkflowsData = async (self, workflows) => {
70
+ try {
71
+ for (const workflow of workflows) {
72
+ addlogs(config, workflow.name + ' workflow was exported successfully', 'success');
73
+ await getWorkflowRoles(workflow);
74
+ self.workflows[workflow.uid] = workflow;
75
+ let deleteItems = config.modules.workflows.invalidKeys;
76
+ deleteItems.forEach((e) => delete workflow[e]);
77
+ }
78
+ } catch (error) {
79
+ throw error;
80
+ }
81
+ };
82
+
83
+ const getWorkflowRoles = async (workflow) => {
84
+ try {
85
+ for (const stage of workflow.workflow_stages) {
86
+ if (stage.SYS_ACL.roles.uids.length) {
87
+ for (let i = 0; i < stage.SYS_ACL.roles.uids.length; i++) {
88
+ const roleUid = stage.SYS_ACL.roles.uids[i];
89
+ const roleData = await client.stack({ api_key: config.source_stack, management_token: config.management_token }).role(roleUid).fetch({ include_rules: true, include_permissions: true });
90
+ stage.SYS_ACL.roles.uids[i] = roleData;
91
+ }
46
92
  }
47
- helper.writeFile(path.join(workflowsFolderPath, workFlowConfig.fileName), self.workflows)
48
- return resolve()
49
- }).catch(function (error) {
50
- if (error.statusCode === 401) {
51
- addlogs(config, chalk.red('You are not allowed to export workflow, Unless you provide email and password in config', 'error'))
52
- return resolve()
53
- }
54
- addlogs(config, error, 'error')
55
- return resolve()
56
- })
57
- })
58
- }
93
+ }
94
+ } catch (error) {
95
+ console.log('Error getting roles', error && error.message);
96
+ addlogs(config, 'Error fetching roles in export workflows task.', 'error');
97
+ throw { message: 'Error fetching roles in export workflows task.' };
98
+ }
99
+ };
59
100
 
60
- module.exports = new ExportWorkFlows()
101
+ module.exports = new ExportWorkFlows();
@@ -1,19 +1,39 @@
1
- const contentstacksdk = require('@contentstack/management')
1
+ const contentstacksdk = require('@contentstack/management');
2
+ const https = require('https');
3
+
4
+ const { addlogs } = require('./log');
2
5
 
3
6
  exports.Client = function (config) {
4
7
  const option = {
5
8
  host: config.host,
6
9
  authtoken: config.auth_token,
7
10
  api_key: config.source_stack,
11
+ maxRequests: 10,
12
+ retryLimit: 5,
13
+ timeout: 60000,
14
+ httpsAgent: new https.Agent({
15
+ maxSockets: 100,
16
+ maxFreeSockets: 10,
17
+ keepAlive: true,
18
+ timeout: 60000, // active socket keepalive for 60 seconds
19
+ freeSocketTimeout: 30000, // free socket keepalive for 30 seconds
20
+ }),
21
+ retryDelay: Math.floor(Math.random() * (8000 - 3000 + 1) + 3000),
8
22
  logHandler: (level, data) => {},
9
- }
23
+ retryCondition: (error) => {
24
+ if (error.response.status === 408) {
25
+ addlogs({ data: error.response }, 'Timeout error', 'error');
26
+ }
27
+ return false;
28
+ },
29
+ };
10
30
 
11
31
  if (typeof config.branchName === 'string') {
12
32
  option.headers = {
13
33
  branch: config.branchName,
14
- }
34
+ };
15
35
  }
16
36
 
17
- const client = contentstacksdk.client(option)
18
- return client
19
- }
37
+ const client = contentstacksdk.client(option);
38
+ return client;
39
+ };