@contentstack/cli-cm-branches 1.0.9 → 1.0.11

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