@contentstack/cli-cm-export 1.1.0 → 1.2.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/README.md +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +14 -5
- package/src/app.js +52 -45
- package/src/commands/cm/stacks/export.js +3 -3
- package/src/config/default.js +8 -6
- package/src/lib/export/assets.js +254 -228
- package/src/lib/export/content-types.js +72 -101
- package/src/lib/export/custom-roles.js +83 -68
- package/src/lib/export/entries.js +168 -225
- package/src/lib/export/environments.js +57 -52
- package/src/lib/export/extensions.js +50 -46
- package/src/lib/export/global-fields.js +64 -61
- package/src/lib/export/labels.js +57 -57
- package/src/lib/export/locales.js +57 -91
- package/src/lib/export/marketplace-apps.js +120 -121
- package/src/lib/export/stack.js +22 -24
- package/src/lib/export/webhooks.js +59 -54
- package/src/lib/export/workflows.js +86 -78
- package/src/lib/util/export-flags.js +7 -8
- package/src/lib/util/helper.js +83 -2
- package/src/lib/util/index.js +36 -1
- package/src/lib/util/log.js +6 -3
- package/src/lib/util/marketplace-app-helper.js +25 -6
- package/src/lib/util/setup-branches.js +5 -7
|
@@ -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
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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:
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
self.requestOptions.qs.uid = { $in: config.contentTypes };
|
|
45
|
+
async getContentTypes(skip = 0) {
|
|
46
|
+
if (skip) {
|
|
47
|
+
this.qs.skip = skip;
|
|
46
48
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
61
|
+
console.log('No content types returned for the given query');
|
|
73
62
|
}
|
|
63
|
+
}
|
|
74
64
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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 =
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
25
|
+
constructor(credentialConfig) {
|
|
26
|
+
this.config = merge(config, credentialConfig);
|
|
27
|
+
this.client = stack.Client(this.config);
|
|
28
|
+
}
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.roles = {};
|
|
30
|
-
config = credentialConfig;
|
|
31
|
-
this.client = stack.Client(config);
|
|
30
|
+
async start() {
|
|
31
|
+
try {
|
|
32
|
+
const self = this;
|
|
33
|
+
addlogs(this.config, 'Starting roles export', 'success');
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
const rolesFolderPath = path.resolve(this.config.data, this.config.branchName || '', this.rolesConfig.dirName);
|
|
36
|
+
mkdirp.sync(rolesFolderPath);
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
43
|
+
const customRoles = roles.items.filter((role) => !self.EXISTING_ROLES[role.name]);
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
if (!customRoles.length) {
|
|
46
|
+
addlogs(self.config, 'No custom roles were found in the Stack', 'success');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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 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
|
-
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
+
};
|