@contentstack/cli-cm-import 0.1.1-beta.9 → 1.0.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.
- package/LICENSE +21 -0
- package/README.md +54 -23
- package/oclif.manifest.json +1 -1
- package/package.json +69 -65
- package/src/app.js +170 -103
- package/src/commands/cm/stacks/import.js +186 -0
- package/src/config/default.js +17 -57
- package/src/lib/import/assets.js +347 -284
- package/src/lib/import/content-types.js +198 -178
- package/src/lib/import/entries.js +1274 -684
- package/src/lib/import/environments.js +97 -82
- package/src/lib/import/extensions.js +95 -77
- package/src/lib/import/global-fields.js +114 -103
- package/src/lib/import/labels.js +118 -98
- package/src/lib/import/locales.js +124 -111
- package/src/lib/import/webhooks.js +76 -59
- package/src/lib/import/workflows.js +88 -68
- package/src/lib/util/contentstack-management-sdk.js +21 -8
- package/src/lib/util/extensionsUidReplace.js +34 -22
- package/src/lib/util/fs.js +3 -4
- package/src/lib/util/import-flags.js +150 -111
- package/src/lib/util/index.js +134 -130
- package/src/lib/util/log.js +73 -35
- package/src/lib/util/login.js +37 -36
- package/src/lib/util/lookupReplaceAssets.js +167 -61
- package/src/lib/util/lookupReplaceEntries.js +144 -66
- package/src/lib/util/removeReferenceFields.js +29 -26
- package/src/lib/util/schemaTemplate.js +31 -33
- package/src/lib/util/supress-mandatory-fields.js +14 -8
- package/src/lib/util/upload.js +29 -28
- package/src/commands/cm/import.js +0 -159
- package/src/lib/util/request.js +0 -82
|
@@ -4,228 +4,248 @@
|
|
|
4
4
|
* Copyright (c) 2019 Contentstack LLC
|
|
5
5
|
* MIT Licensed
|
|
6
6
|
*/
|
|
7
|
-
let mkdirp = require('mkdirp')
|
|
8
|
-
let fs = require('fs')
|
|
9
|
-
let fsPromises = require('fs').promises
|
|
10
|
-
let path = require('path')
|
|
11
|
-
let _ = require('lodash')
|
|
12
|
-
let Promise = require('bluebird')
|
|
13
|
-
let chalk = require('chalk')
|
|
7
|
+
let mkdirp = require('mkdirp');
|
|
8
|
+
let fs = require('fs');
|
|
9
|
+
let fsPromises = require('fs').promises;
|
|
10
|
+
let path = require('path');
|
|
11
|
+
let _ = require('lodash');
|
|
12
|
+
let Promise = require('bluebird');
|
|
13
|
+
let chalk = require('chalk');
|
|
14
14
|
|
|
15
|
-
let helper = require('../util/fs')
|
|
16
|
-
let
|
|
17
|
-
let
|
|
18
|
-
let
|
|
19
|
-
let sdkInstance = require('../util/contentstack-management-sdk')
|
|
15
|
+
let helper = require('../util/fs');
|
|
16
|
+
let { addlogs } = require('../util/log');
|
|
17
|
+
let supress = require('../util/extensionsUidReplace');
|
|
18
|
+
let sdkInstance = require('../util/contentstack-management-sdk');
|
|
20
19
|
|
|
21
|
-
let config = require('../../config/default')
|
|
22
|
-
let reqConcurrency = config.concurrency
|
|
23
|
-
let requestLimit = config.rateLimit
|
|
24
|
-
let contentTypeConfig = config.modules.content_types
|
|
25
|
-
let globalFieldConfig = config.modules.globalfields
|
|
26
|
-
let globalFieldsFolderPath
|
|
27
|
-
let contentTypesFolderPath
|
|
28
|
-
let mapperFolderPath
|
|
29
|
-
let globalFieldMapperFolderPath
|
|
30
|
-
let globalFieldUpdateFile
|
|
31
|
-
let globalFieldPendingPath
|
|
32
|
-
let skipFiles = ['__master.json', '__priority.json', 'schema.json', '.DS_Store']
|
|
33
|
-
let fileNames
|
|
34
|
-
let field_rules_ct = []
|
|
35
|
-
let client
|
|
36
|
-
let stack = {}
|
|
20
|
+
let config = require('../../config/default');
|
|
21
|
+
let reqConcurrency = config.concurrency;
|
|
22
|
+
let requestLimit = config.rateLimit;
|
|
23
|
+
let contentTypeConfig = config.modules.content_types;
|
|
24
|
+
let globalFieldConfig = config.modules.globalfields;
|
|
25
|
+
let globalFieldsFolderPath;
|
|
26
|
+
let contentTypesFolderPath;
|
|
27
|
+
let mapperFolderPath;
|
|
28
|
+
let globalFieldMapperFolderPath;
|
|
29
|
+
let globalFieldUpdateFile;
|
|
30
|
+
let globalFieldPendingPath;
|
|
31
|
+
let skipFiles = ['__master.json', '__priority.json', 'schema.json', '.DS_Store'];
|
|
32
|
+
let fileNames;
|
|
33
|
+
let field_rules_ct = [];
|
|
34
|
+
let client;
|
|
35
|
+
let stack = {};
|
|
37
36
|
|
|
38
37
|
function importContentTypes() {
|
|
39
|
-
|
|
40
|
-
this.
|
|
41
|
-
this.schemaTemplate = require('../util/schemaTemplate')
|
|
38
|
+
this.contentTypes = [];
|
|
39
|
+
this.schemaTemplate = require('../util/schemaTemplate');
|
|
42
40
|
this.requestOptions = {
|
|
43
41
|
json: {},
|
|
44
|
-
}
|
|
42
|
+
};
|
|
45
43
|
}
|
|
46
44
|
|
|
47
45
|
importContentTypes.prototype = {
|
|
48
46
|
start: function (credentialConfig) {
|
|
49
|
-
addlogs(config, 'Migrating contenttypes', 'success')
|
|
50
|
-
let self = this
|
|
51
|
-
config = credentialConfig
|
|
52
|
-
client = sdkInstance.Client(config)
|
|
53
|
-
stack = client.stack({api_key: config.target_stack, management_token: config.management_token})
|
|
54
|
-
globalFieldsFolderPath = path.resolve(config.data, globalFieldConfig.dirName)
|
|
55
|
-
contentTypesFolderPath = path.resolve(config.data, contentTypeConfig.dirName)
|
|
56
|
-
mapperFolderPath = path.join(config.data, 'mapper', 'content_types')
|
|
57
|
-
globalFieldMapperFolderPath =
|
|
58
|
-
globalFieldPendingPath =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
47
|
+
addlogs(config, 'Migrating contenttypes', 'success');
|
|
48
|
+
let self = this;
|
|
49
|
+
config = credentialConfig;
|
|
50
|
+
client = sdkInstance.Client(config);
|
|
51
|
+
stack = client.stack({ api_key: config.target_stack, management_token: config.management_token });
|
|
52
|
+
globalFieldsFolderPath = path.resolve(config.data, globalFieldConfig.dirName);
|
|
53
|
+
contentTypesFolderPath = path.resolve(config.data, contentTypeConfig.dirName);
|
|
54
|
+
mapperFolderPath = path.join(config.data, 'mapper', 'content_types');
|
|
55
|
+
globalFieldMapperFolderPath = helper.readFile(path.join(config.data, 'mapper', 'global_fields', 'success.json'));
|
|
56
|
+
globalFieldPendingPath = helper.readFile(
|
|
57
|
+
path.join(config.data, 'mapper', 'global_fields', 'pending_global_fields.js'),
|
|
58
|
+
);
|
|
59
|
+
globalFieldUpdateFile = path.join(config.data, 'mapper', 'global_fields', 'success.json');
|
|
60
|
+
fileNames = fs.readdirSync(path.join(contentTypesFolderPath));
|
|
61
|
+
self.globalfields = helper.readFile(path.resolve(globalFieldsFolderPath, globalFieldConfig.fileName));
|
|
62
62
|
for (let index in fileNames) {
|
|
63
63
|
if (skipFiles.indexOf(fileNames[index]) === -1) {
|
|
64
|
-
self.contentTypes.push(helper.readFile(path.join(contentTypesFolderPath, fileNames[index])))
|
|
64
|
+
self.contentTypes.push(helper.readFile(path.join(contentTypesFolderPath, fileNames[index])));
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
self.contentTypeUids = _.map(self.contentTypes, 'uid')
|
|
69
|
-
self.createdContentTypeUids = []
|
|
68
|
+
self.contentTypeUids = _.map(self.contentTypes, 'uid').filter(val => val);
|
|
69
|
+
self.createdContentTypeUids = [];
|
|
70
70
|
if (!fs.existsSync(mapperFolderPath)) {
|
|
71
|
-
mkdirp.sync(mapperFolderPath)
|
|
71
|
+
mkdirp.sync(mapperFolderPath);
|
|
72
72
|
}
|
|
73
73
|
// avoid re-creating content types that already exists in the stack
|
|
74
74
|
if (fs.existsSync(path.join(mapperFolderPath, 'success.json'))) {
|
|
75
|
-
self.createdContentTypeUids = helper.readFile(path.join(mapperFolderPath, 'success.json')) || []
|
|
75
|
+
self.createdContentTypeUids = helper.readFile(path.join(mapperFolderPath, 'success.json')) || [];
|
|
76
76
|
}
|
|
77
|
-
self.contentTypeUids = _.difference(self.contentTypeUids, self.createdContentTypeUids)
|
|
78
|
-
self.uidToTitleMap = self.mapUidToTitle(self.contentTypes)
|
|
79
|
-
// remove
|
|
77
|
+
self.contentTypeUids = _.difference(self.contentTypeUids, self.createdContentTypeUids);
|
|
78
|
+
self.uidToTitleMap = self.mapUidToTitle(self.contentTypes);
|
|
79
|
+
// remove content types, already created
|
|
80
80
|
_.remove(this.contentTypes, function (contentType) {
|
|
81
|
-
return self.contentTypeUids.indexOf(contentType.uid) === -1
|
|
82
|
-
})
|
|
83
|
-
return new Promise(function (resolve, reject) {
|
|
84
|
-
return Promise.map(self.contentTypeUids, function (contentTypeUid) {
|
|
85
|
-
return self.seedContentTypes(contentTypeUid, self.uidToTitleMap[contentTypeUid]).then(function () {
|
|
81
|
+
return self.contentTypeUids.indexOf(contentType.uid) === -1;
|
|
82
|
+
});
|
|
86
83
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return Promise.map(batches, async function (batch) {
|
|
102
|
-
return Promise.map(batch, async function (contentType) {
|
|
103
|
-
await self.updateContentTypes(contentType)
|
|
104
|
-
addlogs(config, contentType.uid + ' was updated successfully!', 'success')
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
concurrency: reqConcurrency,
|
|
108
|
-
}).then(function () {
|
|
109
|
-
}).catch(e => {
|
|
110
|
-
console.log('Something went wrong while migrating content type batch', e)
|
|
111
|
-
})
|
|
112
|
-
}, {
|
|
84
|
+
return new Promise(function (resolve, reject) {
|
|
85
|
+
if (self.contentTypes === undefined || _.isEmpty(self.contentTypes)) {
|
|
86
|
+
addlogs(config, chalk.yellow('No Content types found'), 'success');
|
|
87
|
+
return resolve({ empty: true })
|
|
88
|
+
}
|
|
89
|
+
return Promise.map(
|
|
90
|
+
self.contentTypeUids,
|
|
91
|
+
function (contentTypeUid) {
|
|
92
|
+
return self
|
|
93
|
+
.seedContentTypes(contentTypeUid, self.uidToTitleMap[contentTypeUid])
|
|
94
|
+
.catch(reject);
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
// seed 3 content types at a time
|
|
113
98
|
concurrency: reqConcurrency,
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
await fsPromises.writeFile(contentTypesFolderPath + '/field_rules_uid.json', JSON.stringify(field_rules_ct))
|
|
99
|
+
},
|
|
100
|
+
)
|
|
101
|
+
.then(function () {
|
|
102
|
+
let batches = [];
|
|
103
|
+
let lenObj = self.contentTypes;
|
|
104
|
+
for (let i = 0; i < lenObj.length; i += Math.round(requestLimit / 3)) {
|
|
105
|
+
batches.push(lenObj.slice(i, i + Math.round(requestLimit / 3)));
|
|
122
106
|
}
|
|
123
107
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
108
|
+
return Promise.map(
|
|
109
|
+
batches,
|
|
110
|
+
async function (batch) {
|
|
111
|
+
return Promise.map(
|
|
112
|
+
batch,
|
|
113
|
+
async function (contentType) {
|
|
114
|
+
await self.updateContentTypes(contentType)
|
|
115
|
+
addlogs(config, contentType.uid + ' was updated successfully!', 'success');
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
concurrency: reqConcurrency,
|
|
119
|
+
},
|
|
120
|
+
).catch((e) => {
|
|
121
|
+
console.log('Something went wrong while migrating content type batch', e);
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
concurrency: reqConcurrency
|
|
126
|
+
}
|
|
127
|
+
).then(async function () {
|
|
128
|
+
// eslint-disable-next-line quotes
|
|
129
|
+
if (field_rules_ct.length > 0) {
|
|
130
|
+
await fsPromises.writeFile(
|
|
131
|
+
contentTypesFolderPath + '/field_rules_uid.json',
|
|
132
|
+
JSON.stringify(field_rules_ct),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (globalFieldPendingPath && globalFieldPendingPath.length !== 0) {
|
|
137
|
+
return self
|
|
138
|
+
.updateGlobalfields()
|
|
139
|
+
.then(function () {
|
|
140
|
+
addlogs(config, chalk.green('Content types have been imported successfully!'), 'success');
|
|
141
|
+
return resolve();
|
|
142
|
+
})
|
|
143
|
+
.catch((_error) => {
|
|
144
|
+
addlogs(config, chalk.green('Error in GlobalFields'), 'success');
|
|
145
|
+
return reject();
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
addlogs(config, chalk.green('Content types have been imported successfully!'), 'success');
|
|
149
|
+
return resolve();
|
|
150
|
+
}).catch((error) => {
|
|
151
|
+
return reject(error);
|
|
152
|
+
});
|
|
137
153
|
})
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
})
|
|
154
|
+
.catch((error) => {
|
|
155
|
+
return reject(error);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
142
158
|
},
|
|
143
159
|
seedContentTypes: function (uid, title) {
|
|
144
|
-
let self = this
|
|
160
|
+
let self = this;
|
|
145
161
|
return new Promise(function (resolve, reject) {
|
|
146
|
-
let body = _.cloneDeep(self.schemaTemplate)
|
|
147
|
-
body.content_type.uid = uid
|
|
148
|
-
body.content_type.title = title
|
|
149
|
-
let requestObject = _.cloneDeep(self.requestOptions)
|
|
150
|
-
requestObject.json = body
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
let body = _.cloneDeep(self.schemaTemplate);
|
|
163
|
+
body.content_type.uid = uid;
|
|
164
|
+
body.content_type.title = title;
|
|
165
|
+
let requestObject = _.cloneDeep(self.requestOptions);
|
|
166
|
+
requestObject.json = body;
|
|
167
|
+
|
|
168
|
+
return stack
|
|
169
|
+
.contentType()
|
|
170
|
+
.create(requestObject.json)
|
|
171
|
+
.then(resolve)
|
|
172
|
+
.catch(function (err) {
|
|
173
|
+
let error = JSON.parse(err.message);
|
|
174
|
+
if (error.error_code === 115 && (error.errors.uid || error.errors.title)) {
|
|
175
|
+
// content type uid already exists
|
|
176
|
+
return resolve();
|
|
177
|
+
}
|
|
178
|
+
return reject(error);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
164
181
|
},
|
|
165
182
|
updateContentTypes: function (contentType) {
|
|
166
|
-
let self = this
|
|
183
|
+
let self = this;
|
|
167
184
|
return new Promise(function (resolve, reject) {
|
|
168
185
|
setTimeout(function () {
|
|
169
|
-
let requestObject = _.cloneDeep(self.requestOptions)
|
|
186
|
+
let requestObject = _.cloneDeep(self.requestOptions);
|
|
170
187
|
if (contentType.field_rules) {
|
|
171
|
-
field_rules_ct.push(contentType.uid)
|
|
172
|
-
delete contentType.field_rules
|
|
188
|
+
field_rules_ct.push(contentType.uid);
|
|
189
|
+
delete contentType.field_rules;
|
|
173
190
|
}
|
|
174
|
-
supress(contentType.schema)
|
|
175
|
-
requestObject.json.content_type = contentType
|
|
176
|
-
let contentTypeResponse = stack.contentType(contentType.uid)
|
|
177
|
-
Object.assign(contentTypeResponse, _.cloneDeep(contentType))
|
|
178
|
-
contentTypeResponse
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
191
|
+
supress(contentType.schema);
|
|
192
|
+
requestObject.json.content_type = contentType;
|
|
193
|
+
let contentTypeResponse = stack.contentType(contentType.uid);
|
|
194
|
+
Object.assign(contentTypeResponse, _.cloneDeep(contentType));
|
|
195
|
+
contentTypeResponse
|
|
196
|
+
.update()
|
|
197
|
+
.then((_updatedcontentType) => {
|
|
198
|
+
return resolve();
|
|
199
|
+
})
|
|
200
|
+
.catch((err) => {
|
|
201
|
+
addlogs(config, err, 'error');
|
|
202
|
+
return reject(err);
|
|
203
|
+
});
|
|
204
|
+
}, 1000);
|
|
205
|
+
});
|
|
187
206
|
},
|
|
188
207
|
|
|
189
208
|
updateGlobalfields: function () {
|
|
190
|
-
let self = this
|
|
209
|
+
let self = this;
|
|
191
210
|
return new Promise(function (resolve, reject) {
|
|
192
211
|
// eslint-disable-next-line no-undef
|
|
193
212
|
return Promise.map(globalFieldPendingPath, function (globalfield) {
|
|
194
|
-
let
|
|
195
|
-
|
|
196
|
-
let globalFieldObj = stack.globalField(globalfield)
|
|
197
|
-
Object.assign(globalFieldObj, _.cloneDeep(Obj))
|
|
198
|
-
return globalFieldObj
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
let
|
|
202
|
-
|
|
213
|
+
let Obj = _.find(self.globalfields, { uid: globalfield });
|
|
214
|
+
supress(Obj.schema);
|
|
215
|
+
let globalFieldObj = stack.globalField(globalfield);
|
|
216
|
+
Object.assign(globalFieldObj, _.cloneDeep(Obj));
|
|
217
|
+
return globalFieldObj
|
|
218
|
+
.update()
|
|
219
|
+
.then((globalFieldResponse) => {
|
|
220
|
+
let updateObjpos = _.findIndex(globalFieldMapperFolderPath, function (successobj) {
|
|
221
|
+
let global_field_uid = globalFieldResponse.uid;
|
|
222
|
+
return global_field_uid === successobj;
|
|
223
|
+
});
|
|
224
|
+
globalFieldMapperFolderPath.splice(updateObjpos, 1, Obj);
|
|
225
|
+
helper.writeFile(globalFieldUpdateFile, globalFieldMapperFolderPath);
|
|
226
|
+
|
|
227
|
+
resolve(globalFieldResponse)
|
|
203
228
|
})
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
}).catch(function (error) {
|
|
216
|
-
// failed to update modified schemas back to their original form
|
|
217
|
-
return reject(error)
|
|
218
|
-
})
|
|
219
|
-
})
|
|
229
|
+
.catch(function (err) {
|
|
230
|
+
let error = JSON.parse(err.message);
|
|
231
|
+
// eslint-disable-next-line no-console
|
|
232
|
+
addlogs(config, chalk.red('Global Field failed to update ' + JSON.stringify(error.errors)), 'error');
|
|
233
|
+
})
|
|
234
|
+
.catch(function (error) {
|
|
235
|
+
// failed to update modified schemas back to their original form
|
|
236
|
+
return reject(error);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
});
|
|
220
240
|
},
|
|
221
241
|
|
|
222
242
|
mapUidToTitle: function (contentTypes) {
|
|
223
|
-
let result = {}
|
|
224
|
-
contentTypes.forEach(ct => {
|
|
225
|
-
result[ct.uid] = ct.title
|
|
226
|
-
})
|
|
227
|
-
return result
|
|
243
|
+
let result = {};
|
|
244
|
+
contentTypes.forEach((ct) => {
|
|
245
|
+
result[ct.uid] = ct.title;
|
|
246
|
+
});
|
|
247
|
+
return result;
|
|
228
248
|
},
|
|
229
|
-
}
|
|
249
|
+
};
|
|
230
250
|
|
|
231
|
-
module.exports = new importContentTypes()
|
|
251
|
+
module.exports = new importContentTypes();
|