@contentstack/cli-cm-branches 1.0.10 → 1.0.12

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.
@@ -0,0 +1,571 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.entryCreateUpdateScript = void 0;
4
+ function entryCreateUpdateScript(contentType) {
5
+ return `
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ module.exports = async ({ migration, stackSDKInstance, managementAPIClient, config, branch, apiKey }) => {
9
+ const keysToRemove = [
10
+ 'content_type_uid',
11
+ 'created_at',
12
+ 'updated_at',
13
+ 'created_by',
14
+ 'updated_by',
15
+ 'ACL',
16
+ 'stackHeaders',
17
+ 'urlPath',
18
+ '_version',
19
+ '_in_progress',
20
+ 'update',
21
+ 'delete',
22
+ 'fetch',
23
+ 'publish',
24
+ 'unpublish',
25
+ 'publishRequest',
26
+ 'setWorkflowStage',
27
+ 'import',
28
+ ];
29
+
30
+ let compareBranch = config['compare-branch'];
31
+ let filePath = config['file-path'] || process.cwd();
32
+ let assetDirPath = path.resolve(filePath, 'assets');
33
+ let assetDetails = [];
34
+ let newAssetDetails = [];
35
+ let assetUIDMapper = {};
36
+ let assetUrlMapper = {};
37
+ let assetRefPath = {};
38
+ let isAssetDownload = false;
39
+
40
+ function converter(data) {
41
+ let arr = [];
42
+ for (const elm of data.entries()) {
43
+ // @ts-ignore
44
+ arr.push([elm[1].title, elm[1]]);
45
+ }
46
+ return arr;
47
+ }
48
+
49
+ function deleteUnwantedKeysFromObject(obj, keysToRemove) {
50
+ if(obj){
51
+ keysToRemove.map((key) => delete obj[key]);
52
+ return obj;
53
+ }
54
+ }
55
+
56
+ function uniquelyConcatenateArrays(compareArr, baseArr) {
57
+ let uniqueArray = compareArr.concat(baseArr.filter((item) => compareArr.indexOf(item) < 0));
58
+ return uniqueArray;
59
+ }
60
+
61
+ function getValueByPath(obj, path) {
62
+ return path.split('[').reduce((o, key) => o && o[key.replace(/\]$/, '')], obj);
63
+ }
64
+
65
+ function updateValueByPath(obj, path, newValue, type, fileIndex) {
66
+ path.split('[').reduce((o, key, index, arr) => {
67
+ if (index === arr.length - 1) {
68
+ if (type === 'file') {
69
+ o[key.replace(/]$/, '')][fileIndex] = newValue;
70
+ } else {
71
+ o[key.replace(/]$/, '')][0].uid = newValue;
72
+ }
73
+ } else {
74
+ return o[key.replace(/\]$/, '')];
75
+ }
76
+ }, obj);
77
+ }
78
+
79
+ const findReference = function (schema, path, flag) {
80
+ let references = [];
81
+
82
+ for (const i in schema) {
83
+ const currentPath = path ? path + '[' + schema[i].uid : schema[i].uid;
84
+ if (schema[i].data_type === 'group' || schema[i].data_type === 'global_field') {
85
+ references = references.concat(findReference(schema[i].schema, currentPath, flag));
86
+ } else if (schema[i].data_type === 'blocks') {
87
+ for (const block in schema[i].blocks) {
88
+ references = references.concat(
89
+ findReference(
90
+ schema[i].blocks[block].schema,
91
+ currentPath + '[' + block + '][' + schema[i].blocks[block].uid + ']',
92
+ flag,
93
+ ),
94
+ );
95
+ }
96
+ } else if (schema[i].data_type === 'reference') {
97
+ flag.references = true;
98
+ references.push(currentPath);
99
+ }
100
+ }
101
+
102
+ return references;
103
+ };
104
+
105
+ const findAssets = function (schema, entry, refPath, path) {
106
+ for (const i in schema) {
107
+ const currentPath = path ? path + '[' + schema[i].uid : schema[i].uid;
108
+ if (schema[i].data_type === 'group' || schema[i].data_type === 'global_field') {
109
+ findAssets(schema[i].schema, entry, refPath, currentPath + '[0]');
110
+ } else if (schema[i].data_type === 'blocks') {
111
+ for (const block in schema[i].blocks) {
112
+ {
113
+ if (schema[i].blocks[block].schema) {
114
+ findAssets(
115
+ schema[i].blocks[block].schema,
116
+ entry,
117
+ refPath,
118
+ currentPath + '[' + block + '][' + schema[i].blocks[block].uid + ']',
119
+ );
120
+ }
121
+ }
122
+ }
123
+ } else if (schema[i].data_type === 'json' && schema[i].field_metadata.rich_text_type) {
124
+ findAssetIdsFromJsonRte(entry, schema, refPath, path);
125
+ } else if (
126
+ schema[i].data_type === 'text' &&
127
+ schema[i].field_metadata &&
128
+ (schema[i].field_metadata.markdown || schema[i].field_metadata.rich_text_type)
129
+ ) {
130
+ findFileUrls(schema[i], entry);
131
+ } else if (schema[i].data_type === 'file') {
132
+ refPath.push(currentPath)
133
+ const imgDetails = getValueByPath(entry, currentPath);
134
+ if (schema[i].multiple) {
135
+ if (imgDetails && imgDetails.length) {
136
+ imgDetails.forEach((img) => {
137
+ const obj = {
138
+ uid: img.uid,
139
+ parent_uid: img.parent_uid,
140
+ description: img.description,
141
+ title: img.title,
142
+ filename: img.filename,
143
+ url: img.url,
144
+ };
145
+ assetDetails.push(obj);
146
+ });
147
+ }
148
+ } else {
149
+ if (imgDetails) {
150
+ const obj = {
151
+ uid: imgDetails.uid,
152
+ parent_uid: imgDetails.parent_uid,
153
+ description: imgDetails.description,
154
+ title: imgDetails.title,
155
+ filename: imgDetails.filename,
156
+ url: imgDetails.url,
157
+ };
158
+ assetDetails.push(obj);
159
+ }
160
+ }
161
+ }
162
+ }
163
+ };
164
+
165
+ function findFileUrls(schema, _entry) {
166
+ let markdownRegEx;
167
+ let markdownMatch;
168
+ let text;
169
+ // Regex to detect v3 asset uri patterns
170
+ if (schema && schema.field_metadata && schema.field_metadata.markdown) {
171
+ text = marked(JSON.stringify(_entry));
172
+ } else {
173
+ text = JSON.stringify(_entry);
174
+ }
175
+ markdownRegEx = new RegExp(
176
+ '(https://(assets|(eu-|azure-na-|azure-eu-)?images).contentstack.(io|com)/v3/assets/(.*?)/(.*?)/(.*?)/(.*?)(?="))',
177
+ 'g',
178
+ );
179
+ while ((markdownMatch = markdownRegEx.exec(text)) !== null) {
180
+ if (markdownMatch && typeof markdownMatch[0] === 'string') {
181
+ const assetDetails = markdownMatch[0].split('/');
182
+ //fetch assetUID from url
183
+ const assetUID = assetDetails && assetDetails[6];
184
+ const obj = {
185
+ uid: assetUID,
186
+ url: markdownMatch[0],
187
+ };
188
+ assetDetails.push(obj);
189
+ }
190
+ }
191
+ }
192
+
193
+ function findAssetIdsFromJsonRte(entryObj, ctSchema) {
194
+ if(ctSchema !== undefined){
195
+ for (const element of ctSchema) {
196
+ switch (element.data_type) {
197
+ case 'blocks': {
198
+ if (entryObj[element.uid]) {
199
+ if (element.multiple) {
200
+ entryObj[element.uid].forEach((e) => {
201
+ let key = Object.keys(e).pop();
202
+ let subBlock = element.blocks.filter((block) => block.uid === key).pop();
203
+ findAssetIdsFromJsonRte(e[key], subBlock.schema);
204
+ });
205
+ }
206
+ }
207
+ break;
208
+ }
209
+ case 'global_field':
210
+ case 'group': {
211
+ if (entryObj[element.uid]) {
212
+ if (element.multiple) {
213
+ entryObj[element.uid].forEach((e) => {
214
+ findAssetIdsFromJsonRte(e, element.schema);
215
+ });
216
+ } else {
217
+ findAssetIdsFromJsonRte(entryObj[element.uid], element.schema);
218
+ }
219
+ }
220
+ break;
221
+ }
222
+ case 'json': {
223
+ if (entryObj[element.uid] && element.field_metadata.rich_text_type) {
224
+ if (element.multiple) {
225
+ entryObj[element.uid].forEach((jsonRteData) => {
226
+ gatherJsonRteAssetIds(jsonRteData);
227
+ });
228
+ } else {
229
+ gatherJsonRteAssetIds(entryObj[element.uid]);
230
+ }
231
+ }
232
+ break;
233
+ }
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ function gatherJsonRteAssetIds(jsonRteData) {
240
+ jsonRteData.children.forEach((element) => {
241
+ if (element.type) {
242
+ switch (element.type) {
243
+ case 'a':
244
+ case 'p': {
245
+ if (element.children && element.children.length > 0) {
246
+ gatherJsonRteAssetIds(element);
247
+ }
248
+ break;
249
+ }
250
+ case 'reference': {
251
+ if (Object.keys(element.attrs).length > 0 && element.attrs.type === 'asset') {
252
+ assetDetails.push({ uid: element.attrs['asset-uid'] });
253
+ if (element.attrs['asset-link']) {
254
+ const assetDetails = element.attrs['asset-link'].split('/');
255
+ //fetch assetUID from url
256
+ const assetUID = assetDetails && assetDetails[6];
257
+ const obj = {
258
+ uid: assetUID,
259
+ url: element.attrs['asset-link'],
260
+ };
261
+ assetDetails.push(obj);
262
+ } else if (element.attrs['href']) {
263
+ const assetDetails = element.attrs['href'].split('/');
264
+ //fetch assetUID from url
265
+ const assetUID = assetDetails && assetDetails[6];
266
+ const obj = {
267
+ uid: assetUID,
268
+ url: element.attrs['href'],
269
+ };
270
+ assetDetails.push(obj);
271
+ }
272
+ }
273
+ if (element.children && element.children.length > 0) {
274
+ gatherJsonRteAssetIds(element);
275
+ }
276
+ break;
277
+ }
278
+ }
279
+ }
280
+ });
281
+ }
282
+
283
+ const updateAssetDetailsInEntries = function (entry) {
284
+ assetRefPath[entry.uid].forEach((refPath) => {
285
+ let imgDetails = entry[refPath];
286
+ if (imgDetails !== undefined) {
287
+ if (imgDetails && !Array.isArray(imgDetails)) {
288
+ entry[refPath] = assetUIDMapper[imgDetails.uid];
289
+ } else if (imgDetails && Array.isArray(imgDetails)) {
290
+ for (let i = 0; i < imgDetails.length; i++) {
291
+ const img = imgDetails[i];
292
+ entry[refPath][i] = assetUIDMapper[img.uid];
293
+ }
294
+ }
295
+ } else {
296
+ imgDetails = getValueByPath(entry, refPath);
297
+ if (imgDetails && !Array.isArray(imgDetails)) {
298
+ const imgUID = imgDetails?.uid;
299
+ updateValueByPath(entry, refPath, assetUIDMapper[imgUID], 'file', 0);
300
+ } else if (imgDetails && Array.isArray(imgDetails)) {
301
+ for (let i = 0; i < imgDetails.length; i++) {
302
+ const img = imgDetails[i];
303
+ const imgUID = img?.uid;
304
+ updateValueByPath(entry, refPath, assetUIDMapper[imgUID], 'file', i);
305
+ }
306
+ }
307
+ }
308
+ });
309
+ entry = JSON.stringify(entry);
310
+ const assetUrls = assetDetails.map((asset) => asset.url);
311
+ const assetUIDs = assetDetails.map((asset) => asset.uid);
312
+ assetUrls.forEach(function (assetUrl) {
313
+ let mappedAssetUrl = assetUrlMapper[assetUrl];
314
+ if (typeof mappedAssetUrl !== 'undefined') {
315
+ entry = entry.replace(assetUrl, mappedAssetUrl);
316
+ }
317
+ });
318
+
319
+ assetUIDs.forEach(function (assetUid) {
320
+ let uid = assetUIDMapper[assetUid];
321
+ if (typeof uid !== 'undefined') {
322
+ entry = entry.replace(new RegExp(assetUid, 'img'), uid);
323
+ }
324
+ });
325
+ return JSON.parse(entry);
326
+ };
327
+
328
+ const checkAndDownloadAsset = async function (cAsset) {
329
+ if (cAsset) {
330
+ const assetUID = cAsset.uid;
331
+ const bAssetDetail = await managementAPIClient
332
+ .stack({ api_key: stackSDKInstance.api_key, branch_uid: branch })
333
+ .asset(assetUID)
334
+ .fetch()
335
+ .then((assets) => assets)
336
+ .catch((error) => {});
337
+ if (bAssetDetail) {
338
+ assetUIDMapper[cAsset.uid] = bAssetDetail.uid;
339
+ assetUrlMapper[cAsset.url] = bAssetDetail.url;
340
+ return false;
341
+ }
342
+ else {
343
+ isAssetDownload = true;
344
+ const cAssetDetail = await managementAPIClient
345
+ .stack({ api_key: stackSDKInstance.api_key, branch_uid: compareBranch })
346
+ .asset(assetUID)
347
+ .fetch()
348
+ .then((assets) => assets)
349
+ .catch((error) => {});
350
+ const updatedObj = {
351
+ parent_uid: cAssetDetail.parent_uid,
352
+ description: cAssetDetail.description,
353
+ title: cAssetDetail.title,
354
+ filename: cAssetDetail.filename,
355
+ url: cAssetDetail.url,
356
+ };
357
+ Object.assign(cAsset, updatedObj);
358
+ const url = cAssetDetail?.url;
359
+ if (url) {
360
+ const assetFolderPath = path.resolve(assetDirPath, assetUID);
361
+ if (!fs.existsSync(assetFolderPath)) fs.mkdirSync(assetFolderPath);
362
+ const assetFilePath = path.resolve(assetFolderPath, cAsset.filename);
363
+ const assetWriterStream = fs.createWriteStream(assetFilePath);
364
+ const data = await managementAPIClient
365
+ .stack({ api_key: stackSDKInstance.api_key, branch_uid: compareBranch })
366
+ .asset(assetUID)
367
+ .download({ url, responseType: 'stream' })
368
+ .then(({ data }) => data)
369
+ .catch((error) => {
370
+ throw error;
371
+ });
372
+ assetWriterStream.on('error', (error) => {
373
+ throw error;
374
+ });
375
+ data.pipe(assetWriterStream);
376
+ }
377
+ }
378
+ }
379
+ return cAsset;
380
+ };
381
+
382
+ const uploadAssets = async function () {
383
+ const assetFolderMap = JSON.parse(fs.readFileSync(path.resolve(filePath, 'folder-mapper.json'), 'utf8'));
384
+ const stackAPIClient = managementAPIClient.stack({ api_key: stackSDKInstance.api_key, branch_uid: branch });
385
+ for (let i = 0; i < assetDetails?.length; i++) {
386
+ const asset = assetDetails[i];
387
+ let requestOption = {};
388
+
389
+ requestOption.parent_uid = assetFolderMap[asset.parent_uid] || asset.parent_uid;
390
+
391
+ if (asset.hasOwnProperty('description') && typeof asset.description === 'string') {
392
+ requestOption.description = asset.description;
393
+ }
394
+
395
+ if (asset.hasOwnProperty('tags') && Array.isArray(asset.tags)) {
396
+ requestOption.tags = asset.tags;
397
+ }
398
+
399
+ if (asset.hasOwnProperty('title') && typeof asset.title === 'string') {
400
+ requestOption.title = asset.title;
401
+ }
402
+ requestOption.upload = path.resolve(assetDirPath, asset.uid, asset.filename);
403
+ const res = await stackAPIClient
404
+ .asset()
405
+ .create(requestOption)
406
+ .then((asset) => asset)
407
+ .catch((error) => {
408
+ throw error;
409
+ });
410
+ assetUIDMapper[asset.uid] = res && res.uid;
411
+ assetUrlMapper[asset.url] = res && res.url;
412
+ }
413
+ };
414
+
415
+
416
+ const updateEntryTask = () => {
417
+ return {
418
+ title: 'Update Entries',
419
+ successMessage: 'Entries Updated Successfully',
420
+ failedMessage: 'Failed to update entries',
421
+ task: async () => {
422
+ let compareBranchEntries = await managementAPIClient
423
+ .stack({ api_key: stackSDKInstance.api_key, branch_uid: compareBranch })
424
+ .contentType('${contentType}')
425
+ .entry()
426
+ .query()
427
+ .find();
428
+
429
+ let baseBranchEntries = await stackSDKInstance.contentType('${contentType}').entry().query().find();
430
+
431
+ let contentType = await managementAPIClient
432
+ .stack({ api_key: stackSDKInstance.api_key, branch_uid: compareBranch })
433
+ .contentType('${contentType}')
434
+ .fetch();
435
+
436
+ for (let i = 0; i < compareBranchEntries?.items?.length; i++) {
437
+ assetRefPath[compareBranchEntries.items[i].uid] = []
438
+ findAssets(contentType.schema, compareBranchEntries.items[i], assetRefPath[compareBranchEntries.items[i].uid]);
439
+ }
440
+
441
+ for (let i = 0; i < baseBranchEntries?.items?.length; i++) {
442
+ assetRefPath[baseBranchEntries.items[i].uid] = []
443
+ findAssets(contentType.schema, baseBranchEntries.items[i], assetRefPath[baseBranchEntries.items[i].uid]);
444
+ }
445
+ assetDetails = [...new Map(assetDetails.map((item) => [item['uid'], item])).values()];
446
+ newAssetDetails = assetDetails;
447
+
448
+ if (newAssetDetails && newAssetDetails.length) {
449
+ if (!fs.existsSync(assetDirPath)) {
450
+ fs.mkdirSync(assetDirPath);
451
+ }
452
+ for (let i = 0; i < newAssetDetails.length; i++) {
453
+ const asset = newAssetDetails[i];
454
+ const updatedCAsset = await checkAndDownloadAsset(asset);
455
+ if(updatedCAsset){
456
+ newAssetDetails[i] = updatedCAsset;
457
+ }
458
+ }
459
+ if (isAssetDownload) await uploadAssets();
460
+ }
461
+
462
+ let flag = {
463
+ references: false
464
+ };
465
+
466
+ const references = await findReference(contentType.schema, '', flag);
467
+
468
+ async function updateEntry(entry, entryDetails) {
469
+ Object.assign(entry, { ...entryDetails });
470
+ await entry.update();
471
+ }
472
+
473
+ async function updateReferences(entryDetails, baseEntry, references) {
474
+ for (let i in references) {
475
+ let compareEntryRef = getValueByPath(entryDetails, references[i]);
476
+ let baseEntryRef = getValueByPath(baseEntry, references[i]);
477
+
478
+ if (compareEntryRef && compareEntryRef.length > 0 && baseEntryRef && baseEntryRef.length >= 0) {
479
+ let compareRefEntry = await managementAPIClient
480
+ .stack({ api_key: stackSDKInstance.api_key, branch_uid: compareBranch })
481
+ .contentType(compareEntryRef[0]._content_type_uid)
482
+ .entry(compareEntryRef[0].uid)
483
+ .fetch();
484
+ let baseRefEntry = await stackSDKInstance
485
+ .contentType(compareEntryRef[0]._content_type_uid)
486
+ .entry()
487
+ .query({ query: { title: compareRefEntry.title } })
488
+ .find();
489
+
490
+ updateValueByPath(entryDetails, references[i], baseRefEntry.items[0].uid);
491
+ }
492
+ }
493
+ }
494
+
495
+ try {
496
+ if (contentType.options.singleton) {
497
+ compareBranchEntries.items.map(async (el) => {
498
+ let entryDetails = deleteUnwantedKeysFromObject(el, keysToRemove);
499
+ entryDetails = updateAssetDetailsInEntries(entryDetails);
500
+
501
+ if (baseBranchEntries && baseBranchEntries.items.length) {
502
+ let baseEntryUid = baseBranchEntries.items[0].uid;
503
+ let entry = await stackSDKInstance.contentType('${contentType}').entry(baseEntryUid);
504
+
505
+ if (flag.references) {
506
+ await updateReferences(entryDetails, baseBranchEntries.items[0], references);
507
+ }
508
+
509
+ await updateEntry(entry, entryDetails);
510
+ } else {
511
+ let createdEntry = await stackSDKInstance.contentType('${contentType}').entry().create({ entry: entryDetails });
512
+
513
+ if (flag.references) {
514
+ await updateReferences(entryDetails, createdEntry, references);
515
+ }
516
+
517
+ await updateEntry(createdEntry, entryDetails);
518
+ }
519
+ });
520
+ } else {
521
+ let compareMap = new Map(converter(compareBranchEntries.items));
522
+ let baseMap = new Map(converter(baseBranchEntries.items));
523
+
524
+ let arr = uniquelyConcatenateArrays(Array.from(compareMap.keys()), Array.from(baseMap.keys()));
525
+
526
+ arr.map(async (el) => {
527
+ let entryDetails = deleteUnwantedKeysFromObject(compareMap.get(el), keysToRemove);
528
+ entryDetails = updateAssetDetailsInEntries(entryDetails);
529
+ if (compareMap.get(el) && !baseMap.get(el)) {
530
+ let createdEntry = await stackSDKInstance.contentType('${contentType}').entry().create({ entry: entryDetails });
531
+
532
+ if (flag.references) {
533
+ await updateReferences(entryDetails, createdEntry, references);
534
+ }
535
+
536
+ await updateEntry(createdEntry, entryDetails);
537
+ } else if (compareMap.get(el) && baseMap.get(el)) {
538
+ let baseEntry = baseMap.get(el);
539
+ let entry = await stackSDKInstance.contentType('${contentType}').entry(baseEntry.uid);
540
+
541
+ if (flag.references) {
542
+ await updateReferences(entryDetails, baseEntry, references);
543
+ }
544
+
545
+ await updateEntry(entry, entryDetails);
546
+ }
547
+ });
548
+ }
549
+ } catch (error) {
550
+ throw error;
551
+ }
552
+ },
553
+ };
554
+ };
555
+
556
+ if (compareBranch && branch.length !== 0 && apiKey.length !== 0) {
557
+ migration.addTask(updateEntryTask());
558
+ } else {
559
+ if (apiKey.length === 0) {
560
+ console.error('Please provide api key using --stack-api-key flag');
561
+ }
562
+ if (!compareBranch) {
563
+ console.error('Please provide compare branch through --config compare-branch:<value> flag');
564
+ }
565
+ if (branch.length === 0) {
566
+ console.error('Please provide branch name through --branch flag');
567
+ }
568
+ }
569
+ };`;
570
+ }
571
+ exports.entryCreateUpdateScript = entryCreateUpdateScript;