@contentstack/cli-cm-export 1.1.0 → 1.2.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.
@@ -1,116 +1,87 @@
1
- /*!
2
- * Contentstack Export
3
- * Copyright (c) 2019 Contentstack LLC
4
- * MIT Licensed
5
- */
6
-
7
- const mkdirp = require('mkdirp');
8
1
  const path = require('path');
2
+ const fileHelper = require('../util/helper');
9
3
  const chalk = require('chalk');
10
- const Promise = require('bluebird');
11
-
12
- const helper = require('../util/helper');
13
- const stack = require('../util/contentstack-management-sdk');
4
+ const { executeTask, formatError } = require('../util');
14
5
  const { addlogs } = require('../util/log');
15
6
 
16
- let config = require('../../config/default');
17
- const contentTypeConfig = config.modules.content_types;
18
- const validKeys = contentTypeConfig.validKeys;
19
- let client;
20
- let contentTypesFolderPath;
21
-
22
- function ExportContentTypes() {
23
- this.content_types = [];
24
-
25
- this.requestOptions = {
26
- qs: {
7
+ class ContentTypesExport {
8
+ constructor(exportConfig, stackAPIClient) {
9
+ this.stackAPIClient = stackAPIClient;
10
+ this.exportConfig = exportConfig;
11
+ this.contentTypesConfig = exportConfig.modules.content_types;
12
+ this.qs = {
27
13
  include_count: true,
28
14
  asc: 'updated_at',
29
- limit: config.modules.content_types.limit,
15
+ limit: this.contentTypesConfig.limit,
30
16
  include_global_field_schema: true,
31
- },
32
- };
33
- }
17
+ };
18
+ // If content type id is provided then use it as part of query
19
+ if (Array.isArray(this.exportConfig.contentTypes) && this.exportConfig.length > 0) {
20
+ this.qs.uid = { $in: this.exportConfig.contentTypes };
21
+ }
22
+ this.contentTypesPath = path.resolve(
23
+ exportConfig.data,
24
+ exportConfig.branchName || '',
25
+ this.contentTypesConfig.dirName,
26
+ );
27
+ this.contentTypes = [];
28
+ this.fetchConcurrency = this.contentTypesConfig.fetchConcurrency || this.exportConfig.fetchConcurrency;
29
+ this.writeConcurrency = this.contentTypesConfig.writeConcurrency || this.exportConfig.writeConcurrency;
30
+ }
34
31
 
35
- ExportContentTypes.prototype = {
36
- start: function (credentialConfig) {
37
- this.content_types = [];
38
- let self = this;
39
- config = credentialConfig;
40
- contentTypesFolderPath = path.resolve(config.data, config.branchName || '', contentTypeConfig.dirName);
32
+ async start() {
33
+ try {
34
+ addlogs(this.exportConfig, 'Starting content type export', 'success');
35
+ await fileHelper.makeDirectory(this.contentTypesPath);
36
+ await this.getContentTypes();
37
+ await this.writeContentTypes(this.contentTypes);
38
+ addlogs(this.exportConfig, chalk.green('Content type(s) exported successfully'), 'success');
39
+ } catch (error) {
40
+ addlogs(this.exportConfig, chalk.red(`Failed to export content types ${formatError(error)}`), 'error');
41
+ throw new Error('Failed to export content types');
42
+ }
43
+ }
41
44
 
42
- client = stack.Client(config);
43
- // If content type id is provided then use it as part of query
44
- if (Array.isArray(config.contentTypes) && config.contentTypes.length > 0) {
45
- self.requestOptions.qs.uid = { $in: config.contentTypes };
45
+ async getContentTypes(skip = 0) {
46
+ if (skip) {
47
+ this.qs.skip = skip;
46
48
  }
47
- // Create folder for content types
48
- mkdirp.sync(contentTypesFolderPath);
49
- addlogs(config, 'Starting content type export', 'success');
50
- return new Promise(function (resolve, reject) {
51
- return self
52
- .getContentTypes()
53
- .then(function () {
54
- return self
55
- .writeContentTypes()
56
- .then(() => {
57
- return resolve();
58
- })
59
- .catch((error) => {
60
- return reject(error);
61
- });
62
- })
63
- .catch(reject);
64
- });
65
- },
66
- getContentTypes: function (skip) {
67
- let self = this;
68
- if (typeof skip !== 'number') {
69
- skip = 0;
70
- self.requestOptions.qs.skip = skip;
49
+
50
+ const contentTypeSearchResponse = await this.stackAPIClient.contentType().query(this.qs).find();
51
+ if (Array.isArray(contentTypeSearchResponse.items) && contentTypeSearchResponse.items.length > 0) {
52
+ let updatedContentTypes = this.sanitizeAttribs(contentTypeSearchResponse.items);
53
+ this.contentTypes.push(...updatedContentTypes);
54
+
55
+ skip += this.contentTypesConfig.limit;
56
+ if (skip > contentTypeSearchResponse.count) {
57
+ return;
58
+ }
59
+ return await this.getContentTypes(skip);
71
60
  } else {
72
- self.requestOptions.qs.skip = skip;
61
+ console.log('No content types returned for the given query');
73
62
  }
63
+ }
74
64
 
75
- return new Promise(function (resolve, reject) {
76
- client
77
- .stack({ api_key: config.source_stack, management_token: config.management_token })
78
- .contentType()
79
- .query(self.requestOptions.qs)
80
- .find()
81
- .then((contenttypeResponse) => {
82
- if (contenttypeResponse.items.length === 0) {
83
- addlogs(config, 'No content types were found in the Stack', 'success');
84
- return resolve();
85
- }
86
- contenttypeResponse.items.forEach(function (content_type) {
87
- for (let key in content_type) {
88
- if (validKeys.indexOf(key) === -1) {
89
- delete content_type[key];
90
- }
91
- }
92
- self.content_types.push(content_type);
93
- });
94
-
95
- skip += config.modules.content_types.limit;
96
- if (skip > contenttypeResponse.count) {
97
- return resolve();
98
- }
99
- return self.getContentTypes(skip).then(resolve).catch(reject);
100
- });
65
+ sanitizeAttribs(contentTypes) {
66
+ let updatedContentTypes = [];
67
+ contentTypes.forEach((contentType) => {
68
+ for (let key in contentType) {
69
+ if (this.contentTypesConfig.validKeys.indexOf(key) === -1) {
70
+ delete contentType[key];
71
+ }
72
+ }
73
+ updatedContentTypes.push(contentType);
101
74
  });
102
- },
103
- writeContentTypes: function () {
104
- let self = this;
105
- return new Promise(function (resolve) {
106
- helper.writeFile(path.join(contentTypesFolderPath, 'schema.json'), self.content_types);
107
- self.content_types.forEach(function (content_type) {
108
- helper.writeFile(path.join(contentTypesFolderPath, content_type.uid + '.json'), content_type);
109
- });
110
- addlogs(config, chalk.green('Content type(s) exported successfully'), 'success');
111
- return resolve();
112
- });
113
- },
114
- };
75
+ return updatedContentTypes;
76
+ }
77
+
78
+ async writeContentTypes(contentTypes) {
79
+ function write(contentType) {
80
+ return fileHelper.writeFile(path.join(this.contentTypesPath, contentType.uid + '.json'), contentType);
81
+ }
82
+ await executeTask(contentTypes, write.bind(this), { concurrency: this.writeConcurrency });
83
+ return fileHelper.writeFile(path.join(this.contentTypesPath, 'schema.json'), contentTypes);
84
+ }
85
+ }
115
86
 
116
- module.exports = new ExportContentTypes();
87
+ module.exports = ContentTypesExport;
@@ -1,91 +1,106 @@
1
1
  'use strict';
2
2
 
3
- const mkdirp = require('mkdirp');
4
3
  const path = require('path');
5
4
  const chalk = require('chalk');
5
+ const mkdirp = require('mkdirp');
6
+ const { merge } = require('lodash');
6
7
 
7
8
  const helper = require('../util/helper');
8
9
  const { addlogs } = require('../util/log');
9
-
10
+ const { formatError } = require('../util');
11
+ const config = require('../../config/default');
10
12
  const stack = require('../util/contentstack-management-sdk');
11
- let config = require('../../config/default');
12
- const rolesConfig = config.modules.customRoles;
13
13
 
14
- const EXISTING_ROLES = {
15
- Admin: 1,
16
- Developer: 1,
17
- 'Content Manager': 1,
18
- };
14
+ module.exports = class ExportCustomRoles {
15
+ roles = {};
16
+ client = null;
17
+ customRoles = {};
18
+ EXISTING_ROLES = {
19
+ Admin: 1,
20
+ Developer: 1,
21
+ 'Content Manager': 1,
22
+ };
23
+ rolesConfig = config.modules.customRoles;
19
24
 
20
- function ExportCustomRoles() {
21
- this.client = null;
22
- this.roles = {};
23
- }
25
+ constructor(credentialConfig) {
26
+ this.config = merge(config, credentialConfig);
27
+ this.client = stack.Client(this.config);
28
+ }
24
29
 
25
- ExportCustomRoles.prototype.start = async function (credentialConfig) {
26
- try {
27
- addlogs(config, 'Starting roles export', 'success');
28
- let self = this;
29
- this.roles = {};
30
- config = credentialConfig;
31
- this.client = stack.Client(config);
30
+ async start() {
31
+ const self = this;
32
+ try {
33
+ addlogs(this.config, 'Starting roles export', 'success');
32
34
 
33
- const rolesFolderPath = path.resolve(config.data, config.branchName || '', rolesConfig.dirName);
34
- mkdirp.sync(rolesFolderPath);
35
+ const rolesFolderPath = path.resolve(this.config.data, this.config.branchName || '', this.rolesConfig.dirName);
36
+ mkdirp.sync(rolesFolderPath);
35
37
 
36
- const roles = await this.client
37
- .stack({ api_key: config.source_stack, management_token: config.management_token })
38
- .role()
39
- .fetchAll({ include_rules: true, include_permissions: true });
38
+ const roles = await this.client
39
+ .stack({ api_key: self.config.source_stack, management_token: self.config.management_token })
40
+ .role()
41
+ .fetchAll({ include_rules: true, include_permissions: true });
40
42
 
41
- const customRoles = roles.items.filter(role => !EXISTING_ROLES[role.name]);
43
+ const customRoles = roles.items.filter((role) => !self.EXISTING_ROLES[role.name]);
42
44
 
43
- if (!customRoles.length) {
44
- addlogs(config, 'No custom roles were found in the Stack', 'success');
45
- return;
46
- }
45
+ if (!customRoles.length) {
46
+ addlogs(self.config, 'No custom roles were found in the Stack', 'success');
47
+ return;
48
+ }
47
49
 
48
- await getCustomRolesLocales(customRoles, path.join(rolesFolderPath, rolesConfig.customRolesLocalesFileName), this.client, config);
49
- self.customRoles = {};
50
- customRoles.forEach(role => {
51
- addlogs(config, role.name + ' role was exported successfully', 'success');
52
- self.customRoles[role.uid] = role;
53
- });
54
- helper.writeFile(path.join(rolesFolderPath, rolesConfig.fileName), self.customRoles);
55
- addlogs(config, chalk.green('All the custom roles have been exported successfully'), 'success');
56
- } catch (error) {
57
- if (error.statusCode === 401) {
58
- addlogs(config, chalk.red('You are not allowed to export roles, Unless you provide email and password in config'), 'error');
59
- return;
60
- }
61
- addlogs(config, 'Error occurred in exporting roles. ' + error && error.message, 'error');
62
- throw error;
63
- }
64
- };
65
-
66
- async function getCustomRolesLocales(customRoles, customRolesLocalesFilepath, client, config) {
67
- const localesMap = {};
68
- for (const role of customRoles) {
69
- const rulesLocales = role.rules.find(rule => rule.module === 'locale');
70
- if (rulesLocales.locales && rulesLocales.locales.length) {
71
- rulesLocales.locales.forEach(locale => {
72
- localesMap[locale] = 1;
50
+ await self.getCustomRolesLocales(
51
+ customRoles,
52
+ path.join(rolesFolderPath, self.rolesConfig.customRolesLocalesFileName),
53
+ this.client,
54
+ self.config,
55
+ );
56
+ self.customRoles = {};
57
+ customRoles.forEach((role) => {
58
+ addlogs(self.config, role.name + ' role was exported successfully', 'success');
59
+ self.customRoles[role.uid] = role;
73
60
  });
61
+ helper.writeFileSync(path.join(rolesFolderPath, self.rolesConfig.fileName), self.customRoles);
62
+ addlogs(self.config, chalk.green('All the custom roles have been exported successfully'), 'success');
63
+ } catch (error) {
64
+ if (error.statusCode === 401) {
65
+ addlogs(
66
+ self.config,
67
+ chalk.red('You are not allowed to export roles, Unless you provide email and password in config'),
68
+ 'error',
69
+ );
70
+ return;
71
+ }
72
+ addlogs(self.config, 'Error occurred in exporting roles. ' + error && error.message, 'error');
73
+ addlogs(self.config, formatError(error), 'error');
74
+ throw error;
74
75
  }
75
76
  }
76
77
 
77
- if (Object.keys(localesMap).length) {
78
- const locales = await client.stack({ api_key: config.source_stack, management_token: config.management_token }).locale().query({}).find();
79
- const sourceLocalesMap = {};
80
- for (const locale of locales.items) {
81
- sourceLocalesMap[locale.uid] = locale;
78
+ async getCustomRolesLocales(customRoles, customRolesLocalesFilepath, client, config) {
79
+ const localesMap = {};
80
+ for (const role of customRoles) {
81
+ const rulesLocales = role.rules.find((rule) => rule.module === 'locale');
82
+ if (rulesLocales.locales && rulesLocales.locales.length) {
83
+ rulesLocales.locales.forEach((locale) => {
84
+ localesMap[locale] = 1;
85
+ });
86
+ }
82
87
  }
83
- for (const locale in localesMap) {
84
- delete sourceLocalesMap[locale]['stackHeaders'];
85
- localesMap[locale] = sourceLocalesMap[locale];
88
+
89
+ if (Object.keys(localesMap).length) {
90
+ const locales = await client
91
+ .stack({ api_key: config.source_stack, management_token: config.management_token })
92
+ .locale()
93
+ .query({})
94
+ .find();
95
+ const sourceLocalesMap = {};
96
+ for (const locale of locales.items) {
97
+ sourceLocalesMap[locale.uid] = locale;
98
+ }
99
+ for (const locale in localesMap) {
100
+ delete sourceLocalesMap[locale]['stackHeaders'];
101
+ localesMap[locale] = sourceLocalesMap[locale];
102
+ }
103
+ helper.writeFileSync(customRolesLocalesFilepath, localesMap);
86
104
  }
87
- helper.writeFile(customRolesLocalesFilepath, localesMap);
88
105
  }
89
- }
90
-
91
- module.exports = new ExportCustomRoles();
106
+ };