@contentstack/cli-cm-import 1.7.1 → 1.8.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/README.md +1 -1
- package/lib/config/index.js +2 -0
- package/lib/import/module-importer.js +15 -0
- package/lib/import/modules/base-class.d.ts +1 -1
- package/lib/import/modules/base-class.js +31 -1
- package/lib/import/modules/content-types.js +1 -1
- package/lib/import/modules/entries.d.ts +93 -0
- package/lib/import/modules/entries.js +536 -0
- package/lib/import/modules/marketplace-apps.d.ts +5 -2
- package/lib/import/modules/marketplace-apps.js +21 -16
- package/lib/import/modules/workflows.d.ts +41 -0
- package/lib/import/modules/workflows.js +209 -0
- package/lib/import/modules-js/custom-roles.js +1 -1
- package/lib/import/modules-js/entries.js +20 -7
- package/lib/import/modules-js/environments.js +1 -1
- package/lib/import/modules-js/extensions.js +1 -1
- package/lib/import/modules-js/global-fields.js +2 -4
- package/lib/import/modules-js/marketplace-apps.d.ts +6 -4
- package/lib/import/modules-js/marketplace-apps.js +19 -15
- package/lib/import/modules-js/workflows.js +2 -2
- package/lib/types/default-config.d.ts +1 -0
- package/lib/utils/asset-helper.d.ts +1 -1
- package/lib/utils/asset-helper.js +13 -1
- package/lib/utils/backup-handler.js +10 -1
- package/lib/utils/content-type-helper.js +10 -3
- package/lib/utils/entries-helper.d.ts +4 -1
- package/lib/utils/entries-helper.js +329 -2
- package/lib/utils/file-helper.d.ts +1 -0
- package/lib/utils/file-helper.js +5 -1
- package/lib/utils/import-config-handler.js +1 -1
- package/lib/utils/index.d.ts +2 -2
- package/lib/utils/index.js +4 -1
- package/oclif.manifest.json +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable no-prototype-builtins */
|
|
3
|
+
/*!
|
|
4
|
+
* Contentstack Import
|
|
5
|
+
* Copyright (c) 2019 Contentstack LLC
|
|
6
|
+
* MIT Licensed
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
const tslib_1 = require("tslib");
|
|
10
|
+
const path = tslib_1.__importStar(require("path"));
|
|
11
|
+
const lodash_1 = require("lodash");
|
|
12
|
+
const cli_utilities_1 = require("@contentstack/cli-utilities");
|
|
13
|
+
const utils_1 = require("../../utils");
|
|
14
|
+
const base_class_1 = tslib_1.__importDefault(require("./base-class"));
|
|
15
|
+
class EntriesImport extends base_class_1.default {
|
|
16
|
+
constructor({ importConfig, stackAPIClient }) {
|
|
17
|
+
super({ importConfig, stackAPIClient });
|
|
18
|
+
this.assetUidMapperPath = path.resolve(importConfig.data, 'mapper', 'assets', 'uid-mapping.json');
|
|
19
|
+
this.assetUrlMapperPath = path.resolve(importConfig.data, 'mapper', 'assets', 'url-mapping.json');
|
|
20
|
+
this.entriesMapperPath = path.resolve(importConfig.data, 'mapper', 'entries');
|
|
21
|
+
this.envPath = path.resolve(importConfig.data, 'environments', 'environments.json');
|
|
22
|
+
this.entriesUIDMapperPath = path.join(this.entriesMapperPath, 'uid-mapping.json');
|
|
23
|
+
this.uniqueUidMapperPath = path.join(this.entriesMapperPath, 'unique-mapping.json');
|
|
24
|
+
this.modifiedCTsPath = path.join(this.entriesMapperPath, 'modified-schemas.json');
|
|
25
|
+
this.marketplaceAppMapperPath = path.join(this.importConfig.data, 'mapper', 'marketplace_apps', 'uid-mapping.json');
|
|
26
|
+
this.entriesConfig = importConfig.modules.entries;
|
|
27
|
+
this.entriesPath = path.resolve(importConfig.data, this.entriesConfig.dirName);
|
|
28
|
+
this.cTsPath = path.resolve(importConfig.data, importConfig.modules['content-types'].dirName);
|
|
29
|
+
this.localesPath = path.resolve(importConfig.data, importConfig.modules.locales.dirName, importConfig.modules.locales.fileName);
|
|
30
|
+
this.importConcurrency = this.entriesConfig.importConcurrency || importConfig.importConcurrency;
|
|
31
|
+
this.entriesUidMapper = {};
|
|
32
|
+
this.modifiedCTs = [];
|
|
33
|
+
this.refCTs = [];
|
|
34
|
+
this.jsonRteCTs = [];
|
|
35
|
+
this.jsonRteCTsWithRef = [];
|
|
36
|
+
this.envs = {};
|
|
37
|
+
this.autoCreatedEntries = [];
|
|
38
|
+
}
|
|
39
|
+
async start() {
|
|
40
|
+
try {
|
|
41
|
+
this.cTs = utils_1.fsUtil.readFile(path.join(this.cTsPath, 'schema.json'));
|
|
42
|
+
if (!this.cTs || (0, lodash_1.isEmpty)(this.cTs)) {
|
|
43
|
+
(0, utils_1.log)(this.importConfig, 'No content type found', 'info');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
this.installedExtensions = ((await utils_1.fsUtil.readFile(this.marketplaceAppMapperPath)) || { extension_uid: {} }).extension_uid;
|
|
47
|
+
this.assetUidMapper = utils_1.fsUtil.readFile(this.assetUidMapperPath) || {};
|
|
48
|
+
this.assetUrlMapper = utils_1.fsUtil.readFile(this.assetUrlMapperPath) || {};
|
|
49
|
+
utils_1.fsUtil.makeDirectory(this.entriesMapperPath);
|
|
50
|
+
await this.disableMandatoryCTReferences();
|
|
51
|
+
this.locales = (0, lodash_1.values)(utils_1.fsUtil.readFile(this.localesPath));
|
|
52
|
+
this.locales.unshift(this.importConfig.master_locale); // adds master locale to the list
|
|
53
|
+
//Create Entries
|
|
54
|
+
const entryRequestOptions = this.populateEntryCreatePayload();
|
|
55
|
+
for (let entryRequestOption of entryRequestOptions) {
|
|
56
|
+
await this.createEntries(entryRequestOption);
|
|
57
|
+
}
|
|
58
|
+
await utils_1.fileHelper.writeLargeFile(path.join(this.entriesMapperPath, 'uid-mapping.json'), this.entriesUidMapper); // TBD: manages mapper in one file, should find an alternative
|
|
59
|
+
utils_1.fsUtil.writeFile(path.join(this.entriesMapperPath, 'failed-entries.json'), this.failedEntries);
|
|
60
|
+
// Update entries with references
|
|
61
|
+
const entryUpdateRequestOptions = this.populateEntryUpdatePayload();
|
|
62
|
+
for (let entryUpdateRequestOption of entryUpdateRequestOptions) {
|
|
63
|
+
await this.updateEntriesWithReferences(entryUpdateRequestOption).catch((error) => {
|
|
64
|
+
(0, utils_1.log)(this.importConfig, `Error while updating entries references of ${entryUpdateRequestOption.cTUid} in locale ${entryUpdateRequestOption.locale}`, 'error');
|
|
65
|
+
(0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
utils_1.fsUtil.writeFile(path.join(this.entriesMapperPath, 'failed-entries.json'), this.failedEntries);
|
|
69
|
+
(0, utils_1.log)(this.importConfig, 'Restoring content type changes', 'info');
|
|
70
|
+
await this.enableMandatoryCTReferences().catch((error) => {
|
|
71
|
+
(0, utils_1.log)(this.importConfig, `Error while updating content type references ${(0, utils_1.formatError)(error)}`, 'error');
|
|
72
|
+
});
|
|
73
|
+
if (this.autoCreatedEntries.length > 0) {
|
|
74
|
+
(0, utils_1.log)(this.importConfig, 'Removing entries from master language which got created by default', 'info');
|
|
75
|
+
await this.removeAutoCreatedEntries().catch((error) => {
|
|
76
|
+
(0, utils_1.log)(this.importConfig, `Error while removing auto created entries in master locale ${(0, utils_1.formatError)(error)}`, 'error');
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// Update field rule of content types which are got removed earlier
|
|
80
|
+
(0, utils_1.log)(this.importConfig, 'Updating the field rules of content type', 'info');
|
|
81
|
+
await this.updateFieldRules().catch((error) => {
|
|
82
|
+
(0, utils_1.log)(this.importConfig, `Error while updating field rules of content type ${(0, utils_1.formatError)(error)}`, 'error');
|
|
83
|
+
});
|
|
84
|
+
(0, utils_1.log)(this.importConfig, 'Entries imported successfully', 'success');
|
|
85
|
+
// Publishing entries
|
|
86
|
+
if (this.importConfig.entriesPublish) {
|
|
87
|
+
(0, utils_1.log)(this.importConfig, 'Publishing entries', 'info');
|
|
88
|
+
this.envs = utils_1.fileHelper.readFileSync(this.envPath);
|
|
89
|
+
for (let entryRequestOption of entryRequestOptions) {
|
|
90
|
+
await this.publishEntries(entryRequestOption).catch((error) => {
|
|
91
|
+
(0, utils_1.log)(this.importConfig, `Error in publishing entries of ${entryRequestOption.cTUid} in locale ${entryRequestOption.locale} ${(0, utils_1.formatError)(error)}`, 'error');
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
(0, utils_1.log)(this.importConfig, 'All the entries have been published successfully', 'success');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
(0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
|
|
99
|
+
throw new Error('Error while importing entries');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async disableMandatoryCTReferences() {
|
|
103
|
+
const onSuccess = ({ response: contentType, apiData: { uid } }) => {
|
|
104
|
+
(0, utils_1.log)(this.importConfig, `${uid} content type references removed temporarily`, 'success');
|
|
105
|
+
};
|
|
106
|
+
const onReject = ({ error, apiData: { uid } }) => {
|
|
107
|
+
(0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
|
|
108
|
+
throw new Error(`${uid} content type references removal failed`);
|
|
109
|
+
};
|
|
110
|
+
return await this.makeConcurrentCall({
|
|
111
|
+
processName: 'Update content types (removing mandatory references temporarily)',
|
|
112
|
+
apiContent: this.cTs,
|
|
113
|
+
apiParams: {
|
|
114
|
+
serializeData: this.serializeUpdateCTs.bind(this),
|
|
115
|
+
reject: onReject.bind(this),
|
|
116
|
+
resolve: onSuccess.bind(this),
|
|
117
|
+
entity: 'update-cts',
|
|
118
|
+
includeParamOnCompletion: true,
|
|
119
|
+
},
|
|
120
|
+
concurrencyLimit: this.importConcurrency,
|
|
121
|
+
}).then(() => {
|
|
122
|
+
utils_1.fsUtil.writeFile(this.modifiedCTsPath, this.modifiedCTs);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* @method serializeUpdateCTs
|
|
127
|
+
* @param {ApiOptions} apiOptions ApiOptions
|
|
128
|
+
* @returns {ApiOptions} ApiOptions
|
|
129
|
+
*/
|
|
130
|
+
serializeUpdateCTs(apiOptions) {
|
|
131
|
+
const { apiData: contentType } = apiOptions;
|
|
132
|
+
if (contentType.field_rules) {
|
|
133
|
+
delete contentType.field_rules;
|
|
134
|
+
}
|
|
135
|
+
const flag = {
|
|
136
|
+
suppressed: false,
|
|
137
|
+
references: false,
|
|
138
|
+
jsonRte: false,
|
|
139
|
+
jsonRteEmbeddedEntries: false,
|
|
140
|
+
};
|
|
141
|
+
(0, utils_1.suppressSchemaReference)(contentType.schema, flag);
|
|
142
|
+
// Check if suppress modified flag
|
|
143
|
+
if (flag.suppressed) {
|
|
144
|
+
this.modifiedCTs.push((0, lodash_1.find)(this.cTs, { uid: contentType.uid }));
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
// Note: Skips the content type from update if no reference found
|
|
148
|
+
apiOptions.additionalInfo = { skip: true };
|
|
149
|
+
return apiOptions;
|
|
150
|
+
}
|
|
151
|
+
if (flag.references) {
|
|
152
|
+
this.refCTs.push(contentType.uid);
|
|
153
|
+
}
|
|
154
|
+
if (flag.jsonRte) {
|
|
155
|
+
this.jsonRteCTs.push(contentType.uid);
|
|
156
|
+
if (flag.jsonRteEmbeddedEntries) {
|
|
157
|
+
this.jsonRteCTsWithRef.push(contentType.uid);
|
|
158
|
+
if (this.refCTs.indexOf(contentType.uid) === -1) {
|
|
159
|
+
this.refCTs.push(contentType.uid);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
(0, utils_1.lookupExtension)(this.importConfig, contentType.schema, this.importConfig.preserveStackVersion, this.installedExtensions);
|
|
164
|
+
const contentTypePayload = this.stack.contentType(contentType.uid);
|
|
165
|
+
Object.assign(contentTypePayload, (0, lodash_1.cloneDeep)(contentType));
|
|
166
|
+
apiOptions.apiData = contentTypePayload;
|
|
167
|
+
return apiOptions;
|
|
168
|
+
}
|
|
169
|
+
populateEntryCreatePayload() {
|
|
170
|
+
const requestOptions = [];
|
|
171
|
+
for (let locale of this.locales) {
|
|
172
|
+
for (let contentType of this.cTs) {
|
|
173
|
+
requestOptions.push({
|
|
174
|
+
cTUid: contentType.uid,
|
|
175
|
+
locale: locale.code,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return requestOptions;
|
|
180
|
+
}
|
|
181
|
+
async createEntries({ cTUid, locale }) {
|
|
182
|
+
var _a, _b;
|
|
183
|
+
const processName = 'Create Entries';
|
|
184
|
+
const indexFileName = 'index.json';
|
|
185
|
+
const basePath = path.join(this.entriesPath, cTUid, locale);
|
|
186
|
+
const fs = new cli_utilities_1.FsUtility({ basePath, indexFileName });
|
|
187
|
+
const indexer = fs.indexFileContent;
|
|
188
|
+
const indexerCount = (0, lodash_1.values)(indexer).length;
|
|
189
|
+
if (indexerCount === 0) {
|
|
190
|
+
return Promise.resolve();
|
|
191
|
+
}
|
|
192
|
+
(0, utils_1.log)(this.importConfig, `Starting to create entries for ${cTUid} in locale ${locale}`, 'info');
|
|
193
|
+
const isMasterLocale = locale === ((_b = (_a = this.importConfig) === null || _a === void 0 ? void 0 : _a.master_locale) === null || _b === void 0 ? void 0 : _b.code);
|
|
194
|
+
// Write created entries
|
|
195
|
+
const entriesCreateFileHelper = new cli_utilities_1.FsUtility({
|
|
196
|
+
moduleName: 'created-entries',
|
|
197
|
+
indexFileName: 'index.json',
|
|
198
|
+
basePath: path.join(this.entriesMapperPath, cTUid, locale),
|
|
199
|
+
chunkFileSize: this.entriesConfig.chunkFileSize,
|
|
200
|
+
keepMetadata: false,
|
|
201
|
+
omitKeys: this.entriesConfig.invalidKeys,
|
|
202
|
+
});
|
|
203
|
+
const contentType = (0, lodash_1.find)(this.cTs, { uid: cTUid });
|
|
204
|
+
const onSuccess = ({ response, apiData: entry, additionalInfo: { entryFileName } }) => {
|
|
205
|
+
(0, utils_1.log)(this.importConfig, `Created entry: '${entry.title}' of content type ${cTUid} in locale ${locale}`, 'info');
|
|
206
|
+
this.entriesUidMapper[entry.uid] = response.uid;
|
|
207
|
+
entry.sourceEntryFilePath = path.join(basePath, entryFileName); // stores source file path temporarily
|
|
208
|
+
entry.entryOldUid = entry.uid; // stores old uid temporarily
|
|
209
|
+
if (!isMasterLocale) {
|
|
210
|
+
this.autoCreatedEntries.push({ cTUid, locale, entryUid: response.uid });
|
|
211
|
+
}
|
|
212
|
+
entriesCreateFileHelper.writeIntoFile({ [response.uid]: entry }, { mapKeyVal: true });
|
|
213
|
+
};
|
|
214
|
+
const onReject = ({ error, apiData: { uid, title } }) => {
|
|
215
|
+
(0, utils_1.log)(this.importConfig, `${title} entry of content type ${cTUid} in locale ${locale} failed to create`, 'error');
|
|
216
|
+
(0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
|
|
217
|
+
this.failedEntries.push({ content_type: cTUid, locale, entry: { uid, title } });
|
|
218
|
+
};
|
|
219
|
+
for (const index in indexer) {
|
|
220
|
+
const chunk = await fs.readChunkFiles.next().catch((error) => {
|
|
221
|
+
(0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
|
|
222
|
+
});
|
|
223
|
+
if (chunk) {
|
|
224
|
+
let apiContent = (0, lodash_1.values)(chunk);
|
|
225
|
+
await this.makeConcurrentCall({
|
|
226
|
+
apiContent,
|
|
227
|
+
processName,
|
|
228
|
+
indexerCount,
|
|
229
|
+
currentIndexer: +index,
|
|
230
|
+
apiParams: {
|
|
231
|
+
reject: onReject,
|
|
232
|
+
resolve: onSuccess,
|
|
233
|
+
entity: 'create-entries',
|
|
234
|
+
includeParamOnCompletion: true,
|
|
235
|
+
serializeData: this.serializeEntries.bind(this),
|
|
236
|
+
additionalInfo: { contentType, locale, cTUid, entryFileName: indexer[index] },
|
|
237
|
+
},
|
|
238
|
+
concurrencyLimit: this.importConcurrency,
|
|
239
|
+
}).then(() => {
|
|
240
|
+
entriesCreateFileHelper === null || entriesCreateFileHelper === void 0 ? void 0 : entriesCreateFileHelper.completeFile(true);
|
|
241
|
+
(0, utils_1.log)(this.importConfig, `Created entries for content type ${cTUid} in locale ${locale}`, 'success');
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* @method serializeEntries
|
|
248
|
+
* @param {ApiOptions} apiOptions ApiOptions
|
|
249
|
+
* @returns {ApiOptions} ApiOptions
|
|
250
|
+
*/
|
|
251
|
+
serializeEntries(apiOptions) {
|
|
252
|
+
let { apiData: entry, additionalInfo: { cTUid, locale, contentType }, } = apiOptions;
|
|
253
|
+
if (this.jsonRteCTs.indexOf(cTUid) > -1) {
|
|
254
|
+
entry = (0, utils_1.removeUidsFromJsonRteFields)(entry, contentType.schema);
|
|
255
|
+
}
|
|
256
|
+
// remove entry references from json-rte fields
|
|
257
|
+
if (this.jsonRteCTsWithRef.indexOf(cTUid) > -1) {
|
|
258
|
+
entry = (0, utils_1.removeEntryRefsFromJSONRTE)(entry, contentType.schema);
|
|
259
|
+
}
|
|
260
|
+
// will replace all old asset uid/urls with new ones
|
|
261
|
+
entry = (0, utils_1.lookupAssets)({
|
|
262
|
+
content_type: contentType,
|
|
263
|
+
entry: entry,
|
|
264
|
+
}, this.assetUidMapper, this.assetUrlMapper, path.join(this.entriesPath, cTUid), this.installedExtensions);
|
|
265
|
+
delete entry.publish_details;
|
|
266
|
+
apiOptions.apiData = entry;
|
|
267
|
+
return apiOptions;
|
|
268
|
+
}
|
|
269
|
+
populateEntryUpdatePayload() {
|
|
270
|
+
const requestOptions = [];
|
|
271
|
+
for (let locale of this.locales) {
|
|
272
|
+
for (let cTUid of this.refCTs) {
|
|
273
|
+
requestOptions.push({
|
|
274
|
+
cTUid,
|
|
275
|
+
locale: locale.code,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return requestOptions;
|
|
280
|
+
}
|
|
281
|
+
async updateEntriesWithReferences({ cTUid, locale }) {
|
|
282
|
+
const processName = 'Update Entries';
|
|
283
|
+
const indexFileName = 'index.json';
|
|
284
|
+
const basePath = path.join(this.entriesMapperPath, cTUid, locale);
|
|
285
|
+
const fs = new cli_utilities_1.FsUtility({ basePath, indexFileName });
|
|
286
|
+
const indexer = fs.indexFileContent;
|
|
287
|
+
const indexerCount = (0, lodash_1.values)(indexer).length;
|
|
288
|
+
if (indexerCount === 0) {
|
|
289
|
+
return Promise.resolve();
|
|
290
|
+
}
|
|
291
|
+
(0, utils_1.log)(this.importConfig, `Starting to update entries with references for ${cTUid} in locale ${locale}`, 'info');
|
|
292
|
+
const contentType = (0, lodash_1.find)(this.cTs, { uid: cTUid });
|
|
293
|
+
const onSuccess = ({ response, apiData: { uid, url, title } }) => {
|
|
294
|
+
(0, utils_1.log)(this.importConfig, `Updated entry: '${title}' of content type ${cTUid} in locale ${locale}`, 'info');
|
|
295
|
+
};
|
|
296
|
+
const onReject = ({ error, apiData: { uid, title } }) => {
|
|
297
|
+
(0, utils_1.log)(this.importConfig, `${title} entry of content type ${cTUid} in locale ${locale} failed to update`, 'error');
|
|
298
|
+
(0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
|
|
299
|
+
this.failedEntries.push({ content_type: cTUid, locale, entry: { uid: this.entriesUidMapper[uid], title } });
|
|
300
|
+
};
|
|
301
|
+
for (const index in indexer) {
|
|
302
|
+
const chunk = await fs.readChunkFiles.next().catch((error) => {
|
|
303
|
+
(0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
|
|
304
|
+
});
|
|
305
|
+
if (chunk) {
|
|
306
|
+
let apiContent = (0, lodash_1.values)(chunk);
|
|
307
|
+
await this.makeConcurrentCall({
|
|
308
|
+
apiContent,
|
|
309
|
+
processName,
|
|
310
|
+
indexerCount,
|
|
311
|
+
currentIndexer: +index,
|
|
312
|
+
apiParams: {
|
|
313
|
+
reject: onReject,
|
|
314
|
+
resolve: onSuccess,
|
|
315
|
+
entity: 'update-entries',
|
|
316
|
+
includeParamOnCompletion: true,
|
|
317
|
+
serializeData: this.serializeUpdateEntries.bind(this),
|
|
318
|
+
additionalInfo: { contentType, locale, cTUid },
|
|
319
|
+
},
|
|
320
|
+
concurrencyLimit: this.importConcurrency,
|
|
321
|
+
}).then(() => {
|
|
322
|
+
(0, utils_1.log)(this.importConfig, `Updated entries for content type ${cTUid} in locale ${locale}`, 'success');
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* @method serializeUpdateEntries
|
|
329
|
+
* @param {ApiOptions} apiOptions ApiOptions
|
|
330
|
+
* @returns {ApiOptions} ApiOptions
|
|
331
|
+
*/
|
|
332
|
+
serializeUpdateEntries(apiOptions) {
|
|
333
|
+
let { apiData: entry, additionalInfo: { cTUid, locale, contentType }, } = apiOptions;
|
|
334
|
+
const sourceEntryFilePath = entry.sourceEntryFilePath;
|
|
335
|
+
const sourceEntry = (utils_1.fsUtil.readFile(sourceEntryFilePath) || {})[entry.entryOldUid];
|
|
336
|
+
// Removing temp values
|
|
337
|
+
delete entry.sourceEntryFilePath;
|
|
338
|
+
delete entry.entryOldUid;
|
|
339
|
+
if (this.jsonRteCTs.indexOf(cTUid) > -1) {
|
|
340
|
+
// the entries stored in eSuccessFilePath, have the same uids as the entries from source data
|
|
341
|
+
entry = (0, utils_1.restoreJsonRteEntryRefs)(entry, sourceEntry, contentType.schema, {
|
|
342
|
+
mappedAssetUids: this.assetUidMapper,
|
|
343
|
+
mappedAssetUrls: this.assetUrlMapper,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
entry = (0, utils_1.lookupEntries)({
|
|
347
|
+
content_type: contentType,
|
|
348
|
+
entry,
|
|
349
|
+
}, this.entriesUidMapper, path.join(this.entriesMapperPath, cTUid, locale));
|
|
350
|
+
const entryResponse = this.stack.contentType(contentType.uid).entry(this.entriesUidMapper[entry.uid]);
|
|
351
|
+
Object.assign(entryResponse, (0, lodash_1.cloneDeep)(entry));
|
|
352
|
+
delete entryResponse.publish_details;
|
|
353
|
+
apiOptions.apiData = entryResponse;
|
|
354
|
+
return apiOptions;
|
|
355
|
+
}
|
|
356
|
+
async enableMandatoryCTReferences() {
|
|
357
|
+
const onSuccess = ({ response: contentType, apiData: { uid } }) => {
|
|
358
|
+
(0, utils_1.log)(this.importConfig, `${uid} content type references updated`, 'success');
|
|
359
|
+
};
|
|
360
|
+
const onReject = ({ error, apiData: { uid } }) => {
|
|
361
|
+
(0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
|
|
362
|
+
throw new Error(`Failed to update references of content type ${uid}`);
|
|
363
|
+
};
|
|
364
|
+
return await this.makeConcurrentCall({
|
|
365
|
+
processName: 'Update content type references',
|
|
366
|
+
apiContent: this.modifiedCTs,
|
|
367
|
+
apiParams: {
|
|
368
|
+
serializeData: this.serializeUpdateCTsWithRef.bind(this),
|
|
369
|
+
reject: onReject.bind(this),
|
|
370
|
+
resolve: onSuccess.bind(this),
|
|
371
|
+
entity: 'update-cts',
|
|
372
|
+
includeParamOnCompletion: true,
|
|
373
|
+
},
|
|
374
|
+
concurrencyLimit: this.importConcurrency,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* @method serializeUpdateCTsWithRef
|
|
379
|
+
* @param {ApiOptions} apiOptions ApiOptions
|
|
380
|
+
* @returns {ApiOptions} ApiOptions
|
|
381
|
+
*/
|
|
382
|
+
serializeUpdateCTsWithRef(apiOptions) {
|
|
383
|
+
const { apiData: contentType } = apiOptions;
|
|
384
|
+
if (contentType.field_rules) {
|
|
385
|
+
delete contentType.field_rules;
|
|
386
|
+
}
|
|
387
|
+
(0, utils_1.lookupExtension)(this.importConfig, contentType.schema, this.importConfig.preserveStackVersion, this.installedExtensions);
|
|
388
|
+
const contentTypePayload = this.stack.contentType(contentType.uid);
|
|
389
|
+
Object.assign(contentTypePayload, (0, lodash_1.cloneDeep)(contentType));
|
|
390
|
+
apiOptions.apiData = contentTypePayload;
|
|
391
|
+
return apiOptions;
|
|
392
|
+
}
|
|
393
|
+
async removeAutoCreatedEntries() {
|
|
394
|
+
const onSuccess = ({ response, apiData: { entryUid } }) => {
|
|
395
|
+
(0, utils_1.log)(this.importConfig, `Auto created entry in master locale removed - entry uid ${entryUid} `, 'success');
|
|
396
|
+
};
|
|
397
|
+
const onReject = ({ error, apiData: { entryUid } }) => {
|
|
398
|
+
(0, utils_1.log)(this.importConfig, `Failed to remove auto created entry in master locale - entry uid ${entryUid} \n ${(0, utils_1.formatError)(error)}`, 'error');
|
|
399
|
+
};
|
|
400
|
+
return await this.makeConcurrentCall({
|
|
401
|
+
processName: 'Remove auto created entry in master locale',
|
|
402
|
+
apiContent: this.autoCreatedEntries,
|
|
403
|
+
apiParams: {
|
|
404
|
+
reject: onReject.bind(this),
|
|
405
|
+
resolve: onSuccess.bind(this),
|
|
406
|
+
entity: 'delete-entries',
|
|
407
|
+
includeParamOnCompletion: true,
|
|
408
|
+
},
|
|
409
|
+
concurrencyLimit: this.importConcurrency,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
async updateFieldRules() {
|
|
413
|
+
let cTsWithFieldRules = utils_1.fsUtil.readFile(path.join(this.cTsPath + '/field_rules_uid.json'));
|
|
414
|
+
if (!cTsWithFieldRules || (cTsWithFieldRules === null || cTsWithFieldRules === void 0 ? void 0 : cTsWithFieldRules.length) === 0) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
for (let cTUid of cTsWithFieldRules) {
|
|
418
|
+
const contentType = (0, lodash_1.find)(this.cTs, { uid: cTUid });
|
|
419
|
+
if (contentType.field_rules) {
|
|
420
|
+
let fieldRuleLength = contentType.field_rules.length;
|
|
421
|
+
for (let k = 0; k < fieldRuleLength; k++) {
|
|
422
|
+
let fieldRuleConditionLength = contentType.field_rules[k].conditions.length;
|
|
423
|
+
for (let i = 0; i < fieldRuleConditionLength; i++) {
|
|
424
|
+
if (contentType.field_rules[k].conditions[i].operand_field === 'reference') {
|
|
425
|
+
let fieldRulesValue = contentType.field_rules[k].conditions[i].value;
|
|
426
|
+
let fieldRulesArray = fieldRulesValue.split('.');
|
|
427
|
+
let updatedValue = [];
|
|
428
|
+
for (const element of fieldRulesArray) {
|
|
429
|
+
let splittedFieldRulesValue = element;
|
|
430
|
+
if (this.entriesUidMapper.hasOwnProperty(splittedFieldRulesValue)) {
|
|
431
|
+
updatedValue.push(this.entriesUidMapper[splittedFieldRulesValue]);
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
updatedValue.push(element);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
contentType.field_rules[k].conditions[i].value = updatedValue.join('.');
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
const contentTypeResponse = await this.stack
|
|
442
|
+
.contentType(contentType.uid)
|
|
443
|
+
.fetch()
|
|
444
|
+
.catch((error) => {
|
|
445
|
+
(0, utils_1.log)(this.importConfig, `failed to update the field rules of ${cTUid} ${(0, utils_1.formatError)(error)}`, 'error');
|
|
446
|
+
});
|
|
447
|
+
if (!contentTypeResponse) {
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
contentTypeResponse.field_rules = contentType.field_rules;
|
|
451
|
+
await contentTypeResponse.update().catch((error) => {
|
|
452
|
+
(0, utils_1.log)(this.importConfig, `failed to update the field rules of ${cTUid} ${(0, utils_1.formatError)(error)}`, 'error');
|
|
453
|
+
});
|
|
454
|
+
(0, utils_1.log)(this.importConfig, `Updated the field rules of ${cTUid}`, 'info');
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
(0, utils_1.log)(this.importConfig, `No field rules found in content type ${cTUid} to update`, 'error');
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
async publishEntries({ cTUid, locale }) {
|
|
462
|
+
const processName = 'Publish Entries';
|
|
463
|
+
const indexFileName = 'index.json';
|
|
464
|
+
const basePath = path.join(this.entriesPath, cTUid, locale);
|
|
465
|
+
const fs = new cli_utilities_1.FsUtility({ basePath, indexFileName });
|
|
466
|
+
const indexer = fs.indexFileContent;
|
|
467
|
+
const indexerCount = (0, lodash_1.values)(indexer).length;
|
|
468
|
+
const contentType = (0, lodash_1.find)(this.cTs, { uid: cTUid });
|
|
469
|
+
if (indexerCount === 0) {
|
|
470
|
+
return Promise.resolve();
|
|
471
|
+
}
|
|
472
|
+
(0, utils_1.log)(this.importConfig, `Starting publish entries for ${cTUid} in locale ${locale}`, 'info');
|
|
473
|
+
const onSuccess = ({ response, apiData: { environments }, additionalInfo: { entryUid } }) => {
|
|
474
|
+
(0, utils_1.log)(this.importConfig, `Published entry: '${entryUid}' of content type ${cTUid} and locale ${locale} in ${environments === null || environments === void 0 ? void 0 : environments.join(',')} environments`, 'info');
|
|
475
|
+
};
|
|
476
|
+
const onReject = ({ error, apiData, additionalInfo: { entryUid } }) => {
|
|
477
|
+
(0, utils_1.log)(this.importConfig, `${entryUid} entry of content type ${cTUid} in locale ${locale} failed to publish`, 'error');
|
|
478
|
+
(0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
|
|
479
|
+
};
|
|
480
|
+
for (const index in indexer) {
|
|
481
|
+
const chunk = await fs.readChunkFiles.next().catch((error) => {
|
|
482
|
+
(0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
|
|
483
|
+
});
|
|
484
|
+
if (chunk) {
|
|
485
|
+
let apiContent = (0, lodash_1.values)(chunk);
|
|
486
|
+
await this.makeConcurrentCall({
|
|
487
|
+
apiContent,
|
|
488
|
+
processName,
|
|
489
|
+
indexerCount,
|
|
490
|
+
currentIndexer: +index,
|
|
491
|
+
apiParams: {
|
|
492
|
+
reject: onReject,
|
|
493
|
+
resolve: onSuccess,
|
|
494
|
+
entity: 'publish-entries',
|
|
495
|
+
includeParamOnCompletion: true,
|
|
496
|
+
serializeData: this.serializePublishEntries.bind(this),
|
|
497
|
+
additionalInfo: { contentType, locale, cTUid },
|
|
498
|
+
},
|
|
499
|
+
concurrencyLimit: this.importConcurrency,
|
|
500
|
+
}).then(() => {
|
|
501
|
+
(0, utils_1.log)(this.importConfig, `Published entries for content type ${cTUid} in locale ${locale}`, 'success');
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* @method serializeEntries
|
|
508
|
+
* @param {ApiOptions} apiOptions ApiOptions
|
|
509
|
+
* @returns {ApiOptions} ApiOptions
|
|
510
|
+
*/
|
|
511
|
+
serializePublishEntries(apiOptions) {
|
|
512
|
+
let { apiData: entry, additionalInfo } = apiOptions;
|
|
513
|
+
additionalInfo.entryUid = this.entriesUidMapper[entry.uid];
|
|
514
|
+
const requestObject = {
|
|
515
|
+
environments: [],
|
|
516
|
+
locales: [],
|
|
517
|
+
};
|
|
518
|
+
if (entry.publish_details && entry.publish_details.length > 0) {
|
|
519
|
+
(0, lodash_1.forEach)(entry.publish_details, (pubObject) => {
|
|
520
|
+
if (this.envs.hasOwnProperty(pubObject.environment) &&
|
|
521
|
+
(0, lodash_1.indexOf)(requestObject.environments, this.envs[pubObject.environment].name) === -1) {
|
|
522
|
+
requestObject.environments.push(this.envs[pubObject.environment].name);
|
|
523
|
+
}
|
|
524
|
+
if (pubObject.locale && (0, lodash_1.indexOf)(requestObject.locales, pubObject.locale) === -1) {
|
|
525
|
+
requestObject.locales.push(pubObject.locale);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
additionalInfo.skip = true;
|
|
531
|
+
}
|
|
532
|
+
apiOptions.apiData = requestObject;
|
|
533
|
+
return apiOptions;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
exports.default = EntriesImport;
|
|
@@ -12,7 +12,7 @@ export default class ImportMarketplaceApps extends BaseClass {
|
|
|
12
12
|
private appUidMapping;
|
|
13
13
|
private installationUidMapping;
|
|
14
14
|
private installedApps;
|
|
15
|
-
private
|
|
15
|
+
private appOriginalName;
|
|
16
16
|
developerHubBaseUrl: string;
|
|
17
17
|
sdkClient: ContentstackClient;
|
|
18
18
|
nodeCrypto: NodeCrypto;
|
|
@@ -40,7 +40,10 @@ export default class ImportMarketplaceApps extends BaseClass {
|
|
|
40
40
|
appCreationCallback(app: any, response: any, appSuffix: number): Promise<any>;
|
|
41
41
|
/**
|
|
42
42
|
* @method installApps
|
|
43
|
-
*
|
|
43
|
+
*
|
|
44
|
+
* @param {Record<string, any>} app
|
|
45
|
+
* @param {Record<string, any>[]} installedApps
|
|
46
|
+
* @returns {Promise<void>}
|
|
44
47
|
*/
|
|
45
48
|
installApps(app: any): Promise<void>;
|
|
46
49
|
/**
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
-
const
|
|
5
|
-
const find_1 = tslib_1.__importDefault(require("lodash/find"));
|
|
4
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
6
5
|
const map_1 = tslib_1.__importDefault(require("lodash/map"));
|
|
6
|
+
const find_1 = tslib_1.__importDefault(require("lodash/find"));
|
|
7
7
|
const omit_1 = tslib_1.__importDefault(require("lodash/omit"));
|
|
8
|
+
const pick_1 = tslib_1.__importDefault(require("lodash/pick"));
|
|
8
9
|
const first_1 = tslib_1.__importDefault(require("lodash/first"));
|
|
9
10
|
const split_1 = tslib_1.__importDefault(require("lodash/split"));
|
|
10
|
-
const toLower_1 = tslib_1.__importDefault(require("lodash/toLower"));
|
|
11
|
-
const filter_1 = tslib_1.__importDefault(require("lodash/filter"));
|
|
12
|
-
const pick_1 = tslib_1.__importDefault(require("lodash/pick"));
|
|
13
11
|
const node_path_1 = require("node:path");
|
|
12
|
+
const filter_1 = tslib_1.__importDefault(require("lodash/filter"));
|
|
13
|
+
const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
|
|
14
|
+
const toLower_1 = tslib_1.__importDefault(require("lodash/toLower"));
|
|
14
15
|
const cli_utilities_1 = require("@contentstack/cli-utilities");
|
|
15
|
-
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
16
16
|
const config_1 = tslib_1.__importDefault(require("../../config"));
|
|
17
|
-
const utils_1 = require("../../utils");
|
|
18
17
|
const base_class_1 = tslib_1.__importDefault(require("./base-class"));
|
|
19
18
|
const interactive_1 = require("../../utils/interactive");
|
|
19
|
+
const utils_1 = require("../../utils");
|
|
20
20
|
class ImportMarketplaceApps extends base_class_1.default {
|
|
21
21
|
constructor({ importConfig, stackAPIClient }) {
|
|
22
22
|
super({ importConfig, stackAPIClient });
|
|
@@ -27,7 +27,7 @@ class ImportMarketplaceApps extends base_class_1.default {
|
|
|
27
27
|
this.httpClient = new cli_utilities_1.HttpClient();
|
|
28
28
|
this.appNameMapping = {};
|
|
29
29
|
this.appUidMapping = {};
|
|
30
|
-
this.
|
|
30
|
+
this.appOriginalName = undefined;
|
|
31
31
|
this.installedApps = [];
|
|
32
32
|
this.installationUidMapping = {};
|
|
33
33
|
}
|
|
@@ -105,7 +105,8 @@ class ImportMarketplaceApps extends base_class_1.default {
|
|
|
105
105
|
const listOfNewMeta = [];
|
|
106
106
|
const listOfOldMeta = [];
|
|
107
107
|
const extensionUidMap = {};
|
|
108
|
-
this.installedApps =
|
|
108
|
+
this.installedApps =
|
|
109
|
+
(await (0, utils_1.getAllStackSpecificApps)(this.developerHubBaseUrl, this.httpClient, this.importConfig)) || [];
|
|
109
110
|
for (const app of this.marketplaceApps) {
|
|
110
111
|
listOfOldMeta.push(...(0, map_1.default)((_a = app === null || app === void 0 ? void 0 : app.ui_location) === null || _a === void 0 ? void 0 : _a.locations, 'meta').flat());
|
|
111
112
|
}
|
|
@@ -160,7 +161,7 @@ class ImportMarketplaceApps extends base_class_1.default {
|
|
|
160
161
|
for (let app of privateApps) {
|
|
161
162
|
// NOTE keys can be passed to install new app in the developer hub
|
|
162
163
|
app.manifest = (0, pick_1.default)(app.manifest, ['uid', 'name', 'description', 'icon', 'target_type', 'webhook', 'oauth']);
|
|
163
|
-
this.
|
|
164
|
+
this.appOriginalName = app.manifest.name;
|
|
164
165
|
const obj = {
|
|
165
166
|
oauth: app.oauth,
|
|
166
167
|
webhook: app.webhook,
|
|
@@ -168,10 +169,11 @@ class ImportMarketplaceApps extends base_class_1.default {
|
|
|
168
169
|
};
|
|
169
170
|
await this.createPrivateApps(Object.assign(Object.assign({}, obj), app.manifest));
|
|
170
171
|
}
|
|
171
|
-
this.
|
|
172
|
+
this.appOriginalName = undefined;
|
|
172
173
|
}
|
|
173
174
|
async createPrivateApps(app, uidCleaned = false, appSuffix = 1) {
|
|
174
|
-
|
|
175
|
+
var _a;
|
|
176
|
+
let locations = (_a = app === null || app === void 0 ? void 0 : app.ui_location) === null || _a === void 0 ? void 0 : _a.locations;
|
|
175
177
|
if (!uidCleaned && !(0, isEmpty_1.default)(locations)) {
|
|
176
178
|
app.ui_location.locations = await this.updateManifestUILocations(locations, 'uid');
|
|
177
179
|
}
|
|
@@ -199,8 +201,8 @@ class ImportMarketplaceApps extends base_class_1.default {
|
|
|
199
201
|
location.meta = (0, map_1.default)(location.meta, (meta) => {
|
|
200
202
|
if (meta.name) {
|
|
201
203
|
const name = `${(0, first_1.default)((0, split_1.default)(meta.name, '◈'))}◈${appSuffix}`;
|
|
202
|
-
if (!this.appNameMapping[this.
|
|
203
|
-
this.appNameMapping[this.
|
|
204
|
+
if (!this.appNameMapping[this.appOriginalName]) {
|
|
205
|
+
this.appNameMapping[this.appOriginalName] = name;
|
|
204
206
|
}
|
|
205
207
|
meta.name = name;
|
|
206
208
|
}
|
|
@@ -234,12 +236,15 @@ class ImportMarketplaceApps extends base_class_1.default {
|
|
|
234
236
|
// NOTE new app installation
|
|
235
237
|
(0, utils_1.log)(this.importConfig, `${response.name} app created successfully.!`, 'success');
|
|
236
238
|
this.appUidMapping[app.uid] = response.uid;
|
|
237
|
-
this.appNameMapping[this.
|
|
239
|
+
this.appNameMapping[this.appOriginalName] = response.name;
|
|
238
240
|
}
|
|
239
241
|
}
|
|
240
242
|
/**
|
|
241
243
|
* @method installApps
|
|
242
|
-
*
|
|
244
|
+
*
|
|
245
|
+
* @param {Record<string, any>} app
|
|
246
|
+
* @param {Record<string, any>[]} installedApps
|
|
247
|
+
* @returns {Promise<void>}
|
|
243
248
|
*/
|
|
244
249
|
async installApps(app) {
|
|
245
250
|
var _a, _b, _c, _d;
|