@contentstack/cli-cm-import 0.1.1-beta.7 → 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 +21 -0
- package/README.md +54 -23
- package/oclif.manifest.json +1 -1
- package/package.json +70 -66
- 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 -281
- package/src/lib/import/content-types.js +203 -172
- package/src/lib/import/entries.js +1274 -681
- 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/import/.DS_Store +0 -0
- package/src/lib/util/request.js +0 -82
|
@@ -4,217 +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
|
|
10
|
-
let
|
|
11
|
-
let
|
|
12
|
-
let
|
|
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');
|
|
13
14
|
|
|
14
|
-
let helper = require('../util/fs')
|
|
15
|
-
let
|
|
16
|
-
let
|
|
17
|
-
let
|
|
18
|
-
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');
|
|
19
19
|
|
|
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 = {}
|
|
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 = {};
|
|
36
36
|
|
|
37
37
|
function importContentTypes() {
|
|
38
|
-
|
|
39
|
-
this.
|
|
40
|
-
this.schemaTemplate = require('../util/schemaTemplate')
|
|
38
|
+
this.contentTypes = [];
|
|
39
|
+
this.schemaTemplate = require('../util/schemaTemplate');
|
|
41
40
|
this.requestOptions = {
|
|
42
41
|
json: {},
|
|
43
|
-
}
|
|
42
|
+
};
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
importContentTypes.prototype = {
|
|
47
46
|
start: function (credentialConfig) {
|
|
48
|
-
addlogs(config, 'Migrating contenttypes', 'success')
|
|
49
|
-
let self = this
|
|
50
|
-
config = credentialConfig
|
|
51
|
-
client = sdkInstance.Client(config)
|
|
52
|
-
stack = client.stack({api_key: config.target_stack, management_token: config.management_token})
|
|
53
|
-
globalFieldsFolderPath = path.resolve(config.data, globalFieldConfig.dirName)
|
|
54
|
-
contentTypesFolderPath = path.resolve(config.data, contentTypeConfig.dirName)
|
|
55
|
-
mapperFolderPath = path.join(config.data, 'mapper', 'content_types')
|
|
56
|
-
globalFieldMapperFolderPath =
|
|
57
|
-
globalFieldPendingPath =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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));
|
|
61
62
|
for (let index in fileNames) {
|
|
62
63
|
if (skipFiles.indexOf(fileNames[index]) === -1) {
|
|
63
|
-
self.contentTypes.push(helper.readFile(path.join(contentTypesFolderPath, fileNames[index])))
|
|
64
|
+
self.contentTypes.push(helper.readFile(path.join(contentTypesFolderPath, fileNames[index])));
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
self.contentTypeUids = _.map(self.contentTypes, 'uid')
|
|
68
|
-
self.createdContentTypeUids = []
|
|
68
|
+
self.contentTypeUids = _.map(self.contentTypes, 'uid').filter(val => val);
|
|
69
|
+
self.createdContentTypeUids = [];
|
|
69
70
|
if (!fs.existsSync(mapperFolderPath)) {
|
|
70
|
-
mkdirp.sync(mapperFolderPath)
|
|
71
|
+
mkdirp.sync(mapperFolderPath);
|
|
71
72
|
}
|
|
72
73
|
// avoid re-creating content types that already exists in the stack
|
|
73
74
|
if (fs.existsSync(path.join(mapperFolderPath, 'success.json'))) {
|
|
74
|
-
self.createdContentTypeUids = helper.readFile(path.join(mapperFolderPath, 'success.json')) || []
|
|
75
|
+
self.createdContentTypeUids = helper.readFile(path.join(mapperFolderPath, 'success.json')) || [];
|
|
75
76
|
}
|
|
76
|
-
self.contentTypeUids = _.difference(self.contentTypeUids, self.createdContentTypeUids)
|
|
77
|
-
|
|
77
|
+
self.contentTypeUids = _.difference(self.contentTypeUids, self.createdContentTypeUids);
|
|
78
|
+
self.uidToTitleMap = self.mapUidToTitle(self.contentTypes);
|
|
79
|
+
// remove content types, already created
|
|
78
80
|
_.remove(this.contentTypes, function (contentType) {
|
|
79
|
-
return self.contentTypeUids.indexOf(contentType.uid) === -1
|
|
80
|
-
})
|
|
81
|
-
return new Promise(function (resolve, reject) {
|
|
82
|
-
return Promise.map(self.contentTypeUids, function (contentTypeUid) {
|
|
83
|
-
return self.seedContentTypes(contentTypeUid).then(function () {
|
|
84
|
-
|
|
85
|
-
}).catch(function (error) {
|
|
86
|
-
return reject(error)
|
|
87
|
-
})
|
|
88
|
-
}, {
|
|
89
|
-
// seed 3 content types at a time
|
|
90
|
-
concurrency: reqConcurrency,
|
|
91
|
-
}).then(function () {
|
|
92
|
-
let batches = []
|
|
93
|
-
let lenObj = self.contentTypes
|
|
94
|
-
// var a = Math.round(2.60);
|
|
95
|
-
for (let i = 0; i < lenObj.length; i += Math.round(requestLimit / 3)) {
|
|
96
|
-
batches.push(lenObj.slice(i, i + Math.round(requestLimit / 3)))
|
|
97
|
-
}
|
|
81
|
+
return self.contentTypeUids.indexOf(contentType.uid) === -1;
|
|
82
|
+
});
|
|
98
83
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
},
|
|
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
|
|
111
98
|
concurrency: reqConcurrency,
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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)));
|
|
118
106
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
107
|
+
|
|
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();
|
|
124
150
|
}).catch((error) => {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}).catch(error => {
|
|
133
|
-
return reject(error)
|
|
134
|
-
})
|
|
135
|
-
}).catch(error => {
|
|
136
|
-
return reject(error)
|
|
137
|
-
})
|
|
138
|
-
})
|
|
151
|
+
return reject(error);
|
|
152
|
+
});
|
|
153
|
+
})
|
|
154
|
+
.catch((error) => {
|
|
155
|
+
return reject(error);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
139
158
|
},
|
|
140
|
-
seedContentTypes: function (uid) {
|
|
141
|
-
let self = this
|
|
159
|
+
seedContentTypes: function (uid, title) {
|
|
160
|
+
let self = this;
|
|
142
161
|
return new Promise(function (resolve, reject) {
|
|
143
|
-
let body = _.cloneDeep(self.schemaTemplate)
|
|
144
|
-
body.content_type.uid = uid
|
|
145
|
-
body.content_type.title =
|
|
146
|
-
let requestObject = _.cloneDeep(self.requestOptions)
|
|
147
|
-
requestObject.json = body
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
+
});
|
|
161
181
|
},
|
|
162
182
|
updateContentTypes: function (contentType) {
|
|
163
|
-
let self = this
|
|
183
|
+
let self = this;
|
|
164
184
|
return new Promise(function (resolve, reject) {
|
|
165
185
|
setTimeout(function () {
|
|
166
|
-
let requestObject = _.cloneDeep(self.requestOptions)
|
|
186
|
+
let requestObject = _.cloneDeep(self.requestOptions);
|
|
167
187
|
if (contentType.field_rules) {
|
|
168
|
-
field_rules_ct.push(contentType.uid)
|
|
169
|
-
delete contentType.field_rules
|
|
188
|
+
field_rules_ct.push(contentType.uid);
|
|
189
|
+
delete contentType.field_rules;
|
|
170
190
|
}
|
|
171
|
-
supress(contentType.schema)
|
|
172
|
-
requestObject.json.content_type = contentType
|
|
173
|
-
let contentTypeResponse = stack.contentType(contentType.uid)
|
|
174
|
-
Object.assign(contentTypeResponse, _.cloneDeep(contentType))
|
|
175
|
-
contentTypeResponse
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
+
});
|
|
184
206
|
},
|
|
185
207
|
|
|
186
208
|
updateGlobalfields: function () {
|
|
187
|
-
let self = this
|
|
209
|
+
let self = this;
|
|
188
210
|
return new Promise(function (resolve, reject) {
|
|
189
211
|
// eslint-disable-next-line no-undef
|
|
190
212
|
return Promise.map(globalFieldPendingPath, function (globalfield) {
|
|
191
|
-
let
|
|
192
|
-
|
|
193
|
-
let globalFieldObj = stack.globalField(globalfield)
|
|
194
|
-
Object.assign(globalFieldObj, _.cloneDeep(Obj))
|
|
195
|
-
return globalFieldObj
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
let
|
|
199
|
-
|
|
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)
|
|
200
228
|
})
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
}).catch(function (error) {
|
|
213
|
-
// failed to update modified schemas back to their original form
|
|
214
|
-
return reject(error)
|
|
215
|
-
})
|
|
216
|
-
})
|
|
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
|
+
});
|
|
217
240
|
},
|
|
218
|
-
}
|
|
219
241
|
|
|
220
|
-
|
|
242
|
+
mapUidToTitle: function (contentTypes) {
|
|
243
|
+
let result = {};
|
|
244
|
+
contentTypes.forEach((ct) => {
|
|
245
|
+
result[ct.uid] = ct.title;
|
|
246
|
+
});
|
|
247
|
+
return result;
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
module.exports = new importContentTypes();
|