diff --git a/package-lock.json b/package-lock.json index 7d87485b0a..17194481d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2251,9 +2251,9 @@ } }, "node_modules/@oclif/plugin-plugins/node_modules/typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "optional": true, "peer": true, "bin": { @@ -22778,7 +22778,7 @@ "@contentstack/cli-audit": "~1.3.0", "@contentstack/cli-auth": "~1.3.17", "@contentstack/cli-cm-bootstrap": "~1.7.1", - "@contentstack/cli-cm-branches": "~1.0.18", + "@contentstack/cli-cm-branches": "~1.0.19", "@contentstack/cli-cm-bulk-publish": "~1.3.15", "@contentstack/cli-cm-clone": "~1.8.0", "@contentstack/cli-cm-export": "~1.10.2", @@ -23660,7 +23660,7 @@ }, "packages/contentstack-branches": { "name": "@contentstack/cli-cm-branches", - "version": "1.0.18", + "version": "1.0.19", "license": "MIT", "dependencies": { "@contentstack/cli-command": "~1.2.16", @@ -27236,7 +27236,7 @@ "@contentstack/cli-audit": "~1.3.0", "@contentstack/cli-auth": "~1.3.17", "@contentstack/cli-cm-bootstrap": "~1.7.1", - "@contentstack/cli-cm-branches": "~1.0.18", + "@contentstack/cli-cm-branches": "~1.0.19", "@contentstack/cli-cm-bulk-publish": "~1.3.15", "@contentstack/cli-cm-clone": "~1.8.0", "@contentstack/cli-cm-export": "~1.10.2", @@ -31291,9 +31291,9 @@ "requires": {} }, "typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "optional": true, "peer": true }, diff --git a/packages/contentstack-branches/README.md b/packages/contentstack-branches/README.md index 0a929cc3a2..2cdc9f6ddf 100755 --- a/packages/contentstack-branches/README.md +++ b/packages/contentstack-branches/README.md @@ -37,7 +37,7 @@ $ npm install -g @contentstack/cli-cm-branches $ csdx COMMAND running command... $ csdx (--version) -@contentstack/cli-cm-branches/1.0.18 darwin-arm64 node-v20.8.0 +@contentstack/cli-cm-branches/1.0.19 darwin-arm64 node-v20.8.0 $ csdx --help [COMMAND] USAGE $ csdx COMMAND diff --git a/packages/contentstack-branches/package.json b/packages/contentstack-branches/package.json index 6a5a23c323..c085e73c4b 100644 --- a/packages/contentstack-branches/package.json +++ b/packages/contentstack-branches/package.json @@ -1,7 +1,7 @@ { "name": "@contentstack/cli-cm-branches", "description": "Contentstack CLI plugin to do branches operations", - "version": "1.0.18", + "version": "1.0.19", "author": "Contentstack", "bugs": "https://github.com/contentstack/cli/issues", "dependencies": { diff --git a/packages/contentstack-branches/src/utils/entry-create-script.ts b/packages/contentstack-branches/src/utils/entry-create-script.ts index 435bb4a372..e40f18cb16 100644 --- a/packages/contentstack-branches/src/utils/entry-create-script.ts +++ b/packages/contentstack-branches/src/utils/entry-create-script.ts @@ -475,15 +475,17 @@ export function entryCreateScript(contentType) { try { compareFilteredProperties.length !== 0 && compareFilteredProperties.forEach(async (entryDetails) => { - entryDetails = updateAssetDetailsInEntries(entryDetails); - let createdEntry = await stackSDKInstance.contentType('${contentType}').entry().create({ entry: entryDetails }).catch(error => { - throw error; - }); - if(createdEntry){ - if (flag.references) { - await updateReferences(entryDetails, createdEntry, references); + if(entryDetails !== undefined){ + entryDetails = updateAssetDetailsInEntries(entryDetails); + let createdEntry = await stackSDKInstance.contentType('${contentType}').entry().create({ entry: entryDetails }).catch(error => { + throw error; + }); + if(createdEntry){ + if (flag.references) { + await updateReferences(entryDetails, createdEntry, references); + } + await updateEntry(createdEntry, entryDetails); } - await updateEntry(createdEntry, entryDetails); } }); } catch (error) { diff --git a/packages/contentstack-branches/src/utils/entry-create-update-script.ts b/packages/contentstack-branches/src/utils/entry-create-update-script.ts index c3661024e1..194a218aad 100644 --- a/packages/contentstack-branches/src/utils/entry-create-update-script.ts +++ b/packages/contentstack-branches/src/utils/entry-create-update-script.ts @@ -494,56 +494,70 @@ export function entryCreateUpdateScript(contentType) { } try { - if (contentType.options.singleton) { - compareBranchEntries.items.map(async (el) => { + if (contentType?.options?.singleton) { + compareBranchEntries?.items?.map(async (el) => { let entryDetails = deleteUnwantedKeysFromObject(el, keysToRemove); - entryDetails = updateAssetDetailsInEntries(entryDetails); - - if (baseBranchEntries && baseBranchEntries.items.length) { - let baseEntryUid = baseBranchEntries.items[0].uid; - let entry = await stackSDKInstance.contentType('${contentType}').entry(baseEntryUid); + if(entryDetails !== undefined){ + entryDetails = updateAssetDetailsInEntries(entryDetails); + + if (baseBranchEntries && baseBranchEntries.items.length) { + let baseEntryUid = baseBranchEntries.items[0].uid; + let entry = await stackSDKInstance.contentType('${contentType}').entry(baseEntryUid); + + if (flag.references) { + await updateReferences(entryDetails, baseBranchEntries.items[0], references); + } + + await updateEntry(entry, entryDetails); + } else { + let createdEntry = await stackSDKInstance.contentType('${contentType}').entry().create({ entry: entryDetails }); - if (flag.references) { - await updateReferences(entryDetails, baseBranchEntries.items[0], references); - } - - await updateEntry(entry, entryDetails); - } else { - let createdEntry = await stackSDKInstance.contentType('${contentType}').entry().create({ entry: entryDetails }); - - if (flag.references) { - await updateReferences(entryDetails, createdEntry, references); + if (flag.references) { + await updateReferences(entryDetails, createdEntry, references); + } + + await updateEntry(createdEntry, entryDetails); } - - await updateEntry(createdEntry, entryDetails); } }); } else { let compareMap = new Map(converter(compareBranchEntries.items)); let baseMap = new Map(converter(baseBranchEntries.items)); - + + //NOTE: Filter distinct entries from the base and compare branches according to their titles. + //TODO: Need to discuss this approach and replace it with uid condition let arr = uniquelyConcatenateArrays(Array.from(compareMap.keys()), Array.from(baseMap.keys())); - + arr.map(async (el) => { let entryDetails = deleteUnwantedKeysFromObject(compareMap.get(el), keysToRemove); - entryDetails = updateAssetDetailsInEntries(entryDetails); - if (compareMap.get(el) && !baseMap.get(el)) { - let createdEntry = await stackSDKInstance.contentType('${contentType}').entry().create({ entry: entryDetails }); - - if (flag.references) { - await updateReferences(entryDetails, createdEntry, references); - } + //NOTE: In the compare branch, entry must exist. Condition of deleted entry not handled + if(entryDetails !== undefined){ + entryDetails = updateAssetDetailsInEntries(entryDetails); + if (compareMap.get(el) && !baseMap.get(el)) { + let createdEntry = await stackSDKInstance + .contentType('${contentType}') + .entry() + .create({ entry: entryDetails }) + .catch(err => { + (err?.errorMessage || err?.message) ? err?.errorMessage || err?.message : 'Something went wrong!' + }) + + if(createdEntry){ + if (flag.references) { + await updateReferences(entryDetails, createdEntry, references); + } + await updateEntry(createdEntry, entryDetails); + } + } else if (compareMap.get(el) && baseMap.get(el)) { + let baseEntry = baseMap.get(el); + let entry = await stackSDKInstance.contentType('${contentType}').entry(baseEntry.uid); + + if (flag.references) { + await updateReferences(entryDetails, baseEntry, references); + } - await updateEntry(createdEntry, entryDetails); - } else if (compareMap.get(el) && baseMap.get(el)) { - let baseEntry = baseMap.get(el); - let entry = await stackSDKInstance.contentType('${contentType}').entry(baseEntry.uid); - - if (flag.references) { - await updateReferences(entryDetails, baseEntry, references); + await updateEntry(entry, entryDetails); } - - await updateEntry(entry, entryDetails); } }); } diff --git a/packages/contentstack-branches/src/utils/entry-update-script.ts b/packages/contentstack-branches/src/utils/entry-update-script.ts index d6021f5fe4..19b6549bda 100644 --- a/packages/contentstack-branches/src/utils/entry-update-script.ts +++ b/packages/contentstack-branches/src/utils/entry-update-script.ts @@ -491,56 +491,70 @@ export function entryUpdateScript(contentType) { } try { - if (contentType.options.singleton) { - compareBranchEntries.items.map(async (el) => { + if (contentType?.options?.singleton) { + compareBranchEntries?.items?.map(async (el) => { let entryDetails = deleteUnwantedKeysFromObject(el, keysToRemove); - entryDetails = updateAssetDetailsInEntries(entryDetails); - - if (baseBranchEntries && baseBranchEntries.items.length) { - let baseEntryUid = baseBranchEntries.items[0].uid; - let entry = await stackSDKInstance.contentType('${contentType}').entry(baseEntryUid); + if(entryDetails !== undefined){ + entryDetails = updateAssetDetailsInEntries(entryDetails); + if (baseBranchEntries && baseBranchEntries.items.length) { + let baseEntryUid = baseBranchEntries.items[0].uid; + let entry = await stackSDKInstance.contentType('${contentType}').entry(baseEntryUid); + + if (flag.references) { + await updateReferences(entryDetails, baseBranchEntries.items[0], references); + } + + await updateEntry(entry, entryDetails); + } else { + let createdEntry = await stackSDKInstance.contentType('${contentType}').entry().create({ entry: entryDetails }); - if (flag.references) { - await updateReferences(entryDetails, baseBranchEntries.items[0], references); + if (flag.references) { + await updateReferences(entryDetails, createdEntry, references); + } + + await updateEntry(createdEntry, entryDetails); } - - await updateEntry(entry, entryDetails); - } else { - let createdEntry = await stackSDKInstance.contentType('${contentType}').entry().create({ entry: entryDetails }); - - if (flag.references) { - await updateReferences(entryDetails, createdEntry, references); - } - - await updateEntry(createdEntry, entryDetails); } }); } else { let compareMap = new Map(converter(compareBranchEntries.items)); let baseMap = new Map(converter(baseBranchEntries.items)); - + + //NOTE: Filter distinct entries from the base and compare branches according to their titles. + //TODO: Need to discuss this approach and replace it with uid approach let arr = uniquelyConcatenateArrays(Array.from(compareMap.keys()), Array.from(baseMap.keys())); arr.map(async (el) => { let entryDetails = deleteUnwantedKeysFromObject(compareMap.get(el), keysToRemove); - entryDetails = updateAssetDetailsInEntries(entryDetails); - if (compareMap.get(el) && !baseMap.get(el)) { - let createdEntry = await stackSDKInstance.contentType('${contentType}').entry().create({ entry: entryDetails }); - - if (flag.references) { - await updateReferences(entryDetails, createdEntry, references); - } - - await updateEntry(createdEntry, entryDetails); - } else if (compareMap.get(el) && baseMap.get(el)) { - let baseEntry = baseMap.get(el); - let entry = await stackSDKInstance.contentType('${contentType}').entry(baseEntry.uid); + //NOTE: In the compare branch, entry must exist. Condition of deleted entry not handled + if(entryDetails !== undefined){ + entryDetails = updateAssetDetailsInEntries(entryDetails); + if (compareMap.get(el) && !baseMap.get(el)) { + let createdEntry = await stackSDKInstance + .contentType('${contentType}') + .entry() + .create({ entry: entryDetails }) + .catch(err => { + (err?.errorMessage || err?.message) ? err?.errorMessage || err?.message : 'Something went wrong!' + }) - if (flag.references) { - await updateReferences(entryDetails, baseEntry, references); + if(createdEntry){ + if (flag.references) { + await updateReferences(entryDetails, createdEntry, references); + } + + await updateEntry(createdEntry, entryDetails); + } + } else if (compareMap.get(el) && baseMap.get(el)) { + let baseEntry = baseMap.get(el); + let entry = await stackSDKInstance.contentType('${contentType}').entry(baseEntry.uid); + + if (flag.references) { + await updateReferences(entryDetails, baseEntry, references); + } + + await updateEntry(entry, entryDetails); } - - await updateEntry(entry, entryDetails); } }); } diff --git a/packages/contentstack-import/src/commands/cm/stacks/import.ts b/packages/contentstack-import/src/commands/cm/stacks/import.ts index 1170f8cc4c..fbf94f43b6 100644 --- a/packages/contentstack-import/src/commands/cm/stacks/import.ts +++ b/packages/contentstack-import/src/commands/cm/stacks/import.ts @@ -135,7 +135,6 @@ export default class ImportCommand extends Command { 'success', ); } catch (error) { - trace(error, 'error', true); log({ data: backupDir } as ImportConfig, `Failed to import stack content - ${formatError(error)}`, 'error'); log( { data: backupDir } as ImportConfig, diff --git a/packages/contentstack-import/src/import/modules/entries.ts b/packages/contentstack-import/src/import/modules/entries.ts index 6bb3d0a5b7..4f2e1a5b0e 100644 --- a/packages/contentstack-import/src/import/modules/entries.ts +++ b/packages/contentstack-import/src/import/modules/entries.ts @@ -7,7 +7,7 @@ import * as path from 'path'; import { isEmpty, values, cloneDeep, find, indexOf, forEach } from 'lodash'; -import { ContentType, FsUtility } from '@contentstack/cli-utilities'; +import { FsUtility } from '@contentstack/cli-utilities'; import { fsUtil, log, @@ -125,7 +125,7 @@ export default class EntriesImport extends BaseClass { await fileHelper.writeLargeFile(path.join(this.entriesMapperPath, 'uid-mapping.json'), this.entriesUidMapper); // TBD: manages mapper in one file, should find an alternative fsUtil.writeFile(path.join(this.entriesMapperPath, 'failed-entries.json'), this.failedEntries); - if (this.autoCreatedEntries.length > 0) { + if (this.autoCreatedEntries?.length > 0) { log(this.importConfig, 'Removing entries from master language which got created by default', 'info'); await this.removeAutoCreatedEntries().catch((error) => { log( @@ -528,8 +528,7 @@ export default class EntriesImport extends BaseClass { async replaceEntriesHandler({ apiParams, - element: entry, - isLastRequest, + element: entry }: { apiParams: ApiOptions; element: Record; @@ -672,6 +671,7 @@ export default class EntriesImport extends BaseClass { if (this.jsonRteCTs.indexOf(cTUid) > -1) { // the entries stored in eSuccessFilePath, have the same uids as the entries from source data entry = restoreJsonRteEntryRefs(entry, sourceEntry, contentType.schema, { + uidMapper: this.entriesUidMapper, mappedAssetUids: this.assetUidMapper, mappedAssetUrls: this.assetUrlMapper, }); @@ -785,13 +785,13 @@ export default class EntriesImport extends BaseClass { const contentType: any = find(cTs, { uid: cTUid }); if (contentType.field_rules) { const fieldDatatypeMap: { [key: string]: string } = {}; - for (let i = 0; i < contentType.schema.length; i++) { + for (let i = 0; i < contentType.schema?.length; i++) { const field = contentType.schema[i].uid; fieldDatatypeMap[field] = contentType.schema[i].data_type; } - let fieldRuleLength = contentType.field_rules.length; + let fieldRuleLength = contentType.field_rules?.length; for (let k = 0; k < fieldRuleLength; k++) { - let fieldRuleConditionLength = contentType.field_rules[k].conditions.length; + let fieldRuleConditionLength = contentType.field_rules[k].conditions?.length; for (let i = 0; i < fieldRuleConditionLength; i++) { if (fieldDatatypeMap[contentType.field_rules[k].conditions[i].operand_field] === 'reference') { let fieldRulesValue = contentType.field_rules[k].conditions[i].value; @@ -905,7 +905,7 @@ export default class EntriesImport extends BaseClass { locales: [], entryUid: this.entriesUidMapper[entry.uid], }; - if (entry.publish_details && entry.publish_details.length > 0) { + if (entry.publish_details && entry.publish_details?.length > 0) { forEach(entry.publish_details, (pubObject) => { if ( this.envs.hasOwnProperty(pubObject.environment) && diff --git a/packages/contentstack-import/src/types/entries.ts b/packages/contentstack-import/src/types/entries.ts new file mode 100644 index 0000000000..fc2ee1c616 --- /dev/null +++ b/packages/contentstack-import/src/types/entries.ts @@ -0,0 +1,9 @@ +type EntryJsonRTEFieldDataType = { + uid?: string; + type?: string; + text?: string; + attrs?: { 'entry-uid'?: string; type?: string } & Record; + children?: EntryJsonRTEFieldDataType[]; +}; + +export { EntryJsonRTEFieldDataType }; diff --git a/packages/contentstack-import/src/types/index.ts b/packages/contentstack-import/src/types/index.ts index 20a140a74c..5a570afac3 100644 --- a/packages/contentstack-import/src/types/index.ts +++ b/packages/contentstack-import/src/types/index.ts @@ -104,4 +104,6 @@ export interface TermsConfig{ export { default as DefaultConfig } from './default-config'; export { default as ImportConfig } from './import-config'; + +export * from './entries' export * from './marketplace-app' \ No newline at end of file diff --git a/packages/contentstack-import/src/utils/entries-helper.ts b/packages/contentstack-import/src/utils/entries-helper.ts index a94ae3d8da..9880e76c63 100644 --- a/packages/contentstack-import/src/utils/entries-helper.ts +++ b/packages/contentstack-import/src/utils/entries-helper.ts @@ -1,12 +1,15 @@ /** * Entries lookup */ +// FIXME refactor the complete file/code after discussed with the team import * as path from 'path'; import * as _ from 'lodash'; import config from '../config'; import * as fileHelper from './file-helper'; +import { EntryJsonRTEFieldDataType } from '../types/entries'; + // update references in entry object export const lookupEntries = function ( data: { @@ -25,7 +28,7 @@ export const lookupEntries = function ( let preserveStackVersion = config.preserveStackVersion; function gatherJsonRteEntryIds(jsonRteData: any) { - jsonRteData.children.forEach((element: any) => { + jsonRteData.children?.forEach((element: any) => { if (element.type) { switch (element.type) { default: { @@ -35,7 +38,7 @@ export const lookupEntries = function ( break; } case 'reference': { - if (Object.keys(element.attrs).length > 0 && element.attrs.type === 'entry') { + if (Object.keys(element.attrs)?.length > 0 && element.attrs?.type === 'entry') { if (uids.indexOf(element.attrs['entry-uid']) === -1) { uids.push(element.attrs['entry-uid']); } @@ -52,13 +55,13 @@ export const lookupEntries = function ( const update = function (_parent: any, form_id: string, updateEntry: any) { let _entry = updateEntry; - let len = _parent.length; + let len = _parent?.length; for (let j = 0; j < len; j++) { if (_entry && _parent[j]) { if (j === len - 1 && _entry[_parent[j]]) { if (form_id !== '_assets') { - if (_entry[_parent[j]].length) { + if (_entry[_parent[j]]?.length) { _entry[_parent[j]].forEach((item: any, idx: any) => { if (typeof item.uid === 'string' && item._content_type_uid) { uids.push(item.uid); @@ -75,18 +78,18 @@ export const lookupEntries = function ( } } else if (Array.isArray(_entry[_parent[j]])) { for (const element of _entry[_parent[j]]) { - if (element.uid.length) { + if (element.uid?.length) { uids.push(element.uid); } } - } else if (_entry[_parent[j]].uid.length) { + } else if (_entry[_parent[j]].uid?.length) { uids.push(_entry[_parent[j]].uid); } } else { _entry = _entry[_parent[j]]; let _keys = _.clone(_parent).splice(j + 1, len); if (Array.isArray(_entry)) { - for (let i = 0, _i = _entry.length; i < _i; i++) { + for (let i = 0, _i = _entry?.length; i < _i; i++) { update(_keys, form_id, _entry[i]); } } else if (!(_entry instanceof Object)) { @@ -96,6 +99,7 @@ export const lookupEntries = function ( } } }; + const find = function (schema: any = [], _entry: any) { for (let i = 0, _i = schema?.length; i < _i; i++) { switch (schema[i].data_type) { @@ -120,7 +124,7 @@ export const lookupEntries = function ( parent.pop(); break; case 'blocks': - for (let j = 0, _j = schema[i].blocks.length; j < _j; j++) { + for (let j = 0, _j = schema[i].blocks?.length; j < _j; j++) { parent.push(schema[i].uid); parent.push(schema[i].blocks[j].uid); find(schema[i].blocks[j].schema, _entry); @@ -143,7 +147,7 @@ export const lookupEntries = function ( case 'blocks': { if (entry[element.uid]) { if (element.multiple) { - entry[element.uid].forEach((e: any) => { + entry[element.uid]?.forEach((e: any) => { let key = Object.keys(e).pop(); let subBlock = element.blocks.filter((e: any) => e.uid === key).pop(); findEntryIdsFromJsonRte(e[key], subBlock.schema); @@ -156,7 +160,7 @@ export const lookupEntries = function ( case 'group': { if (entry[element.uid]) { if (element.multiple) { - entry[element.uid].forEach((e: any) => { + entry[element.uid]?.forEach((e: any) => { findEntryIdsFromJsonRte(e, element.schema); }); } else { @@ -168,7 +172,7 @@ export const lookupEntries = function ( case 'json': { if (entry[element.uid] && element.field_metadata.rich_text_type) { if (element.multiple) { - entry[element.uid].forEach((jsonRteData: any) => { + entry[element.uid]?.forEach((jsonRteData: any) => { gatherJsonRteEntryIds(jsonRteData); }); } else { @@ -195,6 +199,9 @@ export const lookupEntries = function ( let entry = JSON.stringify(data.entry); uids.forEach(function (uid: any) { if (mappedUids.hasOwnProperty(uid)) { + // NOTE g: matches the pattern multiple times + // NOTE i: makes the regex case insensitive + // NOTE m: enables multi-line mode. Where ^ and $ match the start and end of the entire string. Without this, multi-line strings match the beginning and end of each line. entry = entry.replace(new RegExp(uid, 'img'), mappedUids[uid]); mapped.push(uid); } else { @@ -262,7 +269,7 @@ export const removeUidsFromJsonRteFields = ( if (element.multiple) { entry[element.uid] = entry[element.uid].map((e: any) => { let key = Object.keys(e).pop(); - let subBlock = element.blocks.filter((block: any) => block.uid === key).pop(); + let subBlock = element.blocks?.filter((block: any) => block.uid === key).pop(); e[key] = removeUidsFromJsonRteFields(e[key], subBlock.schema); return e; }); @@ -320,7 +327,7 @@ export const removeUidsFromJsonRteFields = ( }; function removeUidsFromChildren(children: Record[] | any) { - if (children.length && children.length > 0) { + if (children?.length && children.length > 0) { return children.map((child: any) => { if (child.type && child.type.length > 0) { delete child.uid; // remove uid @@ -356,7 +363,7 @@ export const removeEntryRefsFromJSONRTE = (entry: Record, ctSchema: if (element.multiple) { entry[element.uid] = entry[element.uid].map((e: any) => { let key = Object.keys(e).pop(); - let subBlock = element.blocks.filter((block: any) => block.uid === key).pop(); + let subBlock = element.blocks?.filter((block: any) => block.uid === key).pop(); e[key] = removeEntryRefsFromJSONRTE(e[key], subBlock.schema); return e; }); @@ -382,10 +389,12 @@ export const removeEntryRefsFromJSONRTE = (entry: Record, ctSchema: const structuredPTag = '{"type":"p","attrs":{},"children":[{"text":""}]}'; if (entry[element.uid] && element.field_metadata.rich_text_type) { if (element.multiple) { + entry[element.uid] = entry[element.uid].map(removeReferenceInJsonRTE); + entry[element.uid] = entry[element.uid].map((jsonRteData: any) => { // repeated code from else block, will abstract later - let entryReferences = jsonRteData.children.filter((e: any) => doEntryReferencesExist(e)); - if (entryReferences.length > 0) { + let entryReferences = jsonRteData.children?.filter((e: any) => doEntryReferencesExist(e)); + if (entryReferences?.length > 0) { jsonRteData.children = jsonRteData.children.filter((e: any) => !doEntryReferencesExist(e)); if (jsonRteData.children.length === 0) { // empty children array are no longer acceptable by the API, a default structure must be there @@ -397,10 +406,12 @@ export const removeEntryRefsFromJSONRTE = (entry: Record, ctSchema: } }); } else { - let entryReferences = entry[element.uid].children.filter((e: any) => doEntryReferencesExist(e)); - if (entryReferences.length > 0) { + // NOTE Clean up all the reference + entry[element.uid] = removeReferenceInJsonRTE(entry[element.uid]); + let entryReferences = entry[element.uid].children?.filter((e: any) => doEntryReferencesExist(e)); + if (entryReferences?.length > 0) { entry[element.uid].children = entry[element.uid].children.filter((e: any) => !doEntryReferencesExist(e)); - if (entry[element.uid].children.length === 0) { + if (entry[element.uid].children?.length === 0) { entry[element.uid].children.push(JSON.parse(structuredPTag)); } } @@ -417,9 +428,13 @@ function doEntryReferencesExist(element: Record[] | any): boolean { // checks if the children of p element contain any references // only checking one level deep, not recursive - if (element.length) { + if (element?.length) { for (const item of element) { - if ((item.type === 'p' || item.type === 'a' || item.type === 'span') && item.children && item.children.length > 0) { + if ( + (item.type === 'p' || item.type === 'a' || item.type === 'span') && + item.children && + item.children.length > 0 + ) { return doEntryReferencesExist(item.children); } else if (isEntryRef(item)) { return true; @@ -430,7 +445,11 @@ function doEntryReferencesExist(element: Record[] | any): boolean { return true; } - if ((element.type === 'p' || element.type === 'a' || element.type === 'span') && element.children && element.children.length > 0) { + if ( + (element.type === 'p' || element.type === 'a' || element.type === 'span') && + element.children && + element.children.length > 0 + ) { return doEntryReferencesExist(element.children); } } @@ -438,17 +457,15 @@ function doEntryReferencesExist(element: Record[] | any): boolean { } function isEntryRef(element: any) { - return element.type === 'reference' && element.attrs.type === 'entry'; + return element.type === 'reference' && element.attrs?.type === 'entry'; } export const restoreJsonRteEntryRefs = ( entry: Record, sourceStackEntry: any, ctSchema: any = [], - { mappedAssetUids, mappedAssetUrls }: any, + { uidMapper, mappedAssetUids, mappedAssetUrls }: any, ) => { - // let mappedAssetUids = fileHelper.readFileSync(this.mappedAssetUidPath) || {}; - // let mappedAssetUrls = fileHelper.readFileSync(this.mappedAssetUrlPath) || {}; for (const element of ctSchema) { switch (element.data_type) { case 'blocks': { @@ -456,9 +473,10 @@ export const restoreJsonRteEntryRefs = ( if (element.multiple && Array.isArray(entry[element.uid])) { entry[element.uid] = entry[element.uid].map((e: any, eIndex: number) => { let key = Object.keys(e).pop(); - let subBlock = element.blocks.filter((block: any) => block.uid === key).pop(); + let subBlock = element.blocks?.filter((block: any) => block.uid === key).pop(); let sourceStackElement = sourceStackEntry[element.uid][eIndex][key]; e[key] = restoreJsonRteEntryRefs(e[key], sourceStackElement, subBlock.schema, { + uidMapper, mappedAssetUids, mappedAssetUrls, }); @@ -474,12 +492,17 @@ export const restoreJsonRteEntryRefs = ( if (element.multiple && Array.isArray(entry[element.uid])) { entry[element.uid] = entry[element.uid].map((e: any, eIndex: number) => { let sourceStackElement = sourceStackEntry[element.uid][eIndex]; - e = restoreJsonRteEntryRefs(e, sourceStackElement, element.schema, { mappedAssetUids, mappedAssetUrls }); + e = restoreJsonRteEntryRefs(e, sourceStackElement, element.schema, { + uidMapper, + mappedAssetUids, + mappedAssetUrls, + }); return e; }); } else { let sourceStackElement = sourceStackEntry[element.uid]; entry[element.uid] = restoreJsonRteEntryRefs(entry[element.uid], sourceStackElement, element.schema, { + uidMapper, mappedAssetUids, mappedAssetUrls, }); @@ -490,57 +513,23 @@ export const restoreJsonRteEntryRefs = ( case 'json': { if (entry[element.uid] && element.field_metadata.rich_text_type) { if (element.multiple && Array.isArray(entry[element.uid])) { - entry[element.uid] = entry[element.uid].map((field: any, index: number) => { - // i am facing a Maximum call stack exceeded issue, - // probably because of this loop operation - - let entryRefs = sourceStackEntry[element.uid][index].children - .map((e: any, i: number) => { - return { index: i, value: e }; - }) - .filter((e: any) => doEntryReferencesExist(e.value)) - .map((e: any) => { - // commenting the line below resolved the maximum call stack exceeded issue - // e.value = this.setDirtyTrue(e.value) - setDirtyTrue(e.value); - return e; - }) - .map((e: any) => { - // commenting the line below resolved the maximum call stack exceeded issue - // e.value = this.resolveAssetRefsInEntryRefsForJsonRte(e, mappedAssetUids, mappedAssetUrls) - resolveAssetRefsInEntryRefsForJsonRte(e.value, mappedAssetUids, mappedAssetUrls); - return e; - }); - - if (entryRefs.length > 0) { - entryRefs.forEach((entryRef: any) => { - field.children.splice(entryRef.index, 0, entryRef.value); - }); - } - return field; - }); - } else { - let entryRefs = sourceStackEntry[element.uid].children - .map((e: any, index: number) => { - return { index: index, value: e }; - }) - .filter((e: any) => doEntryReferencesExist(e.value)) - .map((e: any) => { - setDirtyTrue(e.value); - return e; - }) - .map((e: any) => { - resolveAssetRefsInEntryRefsForJsonRte(e.value, mappedAssetUids, mappedAssetUrls); - return e; + entry[element.uid] = sourceStackEntry[element.uid].map((jsonRTE: any) => { + jsonRTE = restoreReferenceInJsonRTE(jsonRTE, uidMapper); + jsonRTE.children = jsonRTE.children.map((child: any) => { + child = setDirtyTrue(child); + child = resolveAssetRefsInEntryRefsForJsonRte(child, mappedAssetUids, mappedAssetUrls); + return child; }); - if (entryRefs.length > 0) { - entryRefs.forEach((entryRef: any) => { - if (!_.isEmpty(entry[element.uid]) && entry[element.uid].children) { - entry[element.uid].children.splice(entryRef.index, 0, entryRef.value); - } - }); - } + return jsonRTE; + }); + } else { + entry[element.uid] = restoreReferenceInJsonRTE(sourceStackEntry[element.uid], uidMapper); + entry[element.uid].children = entry[element.uid].children.map((child: any) => { + child = setDirtyTrue(child); + child = resolveAssetRefsInEntryRefsForJsonRte(child, mappedAssetUids, mappedAssetUrls); + return child; + }); } } break; @@ -562,12 +551,13 @@ function setDirtyTrue(jsonRteChild: any) { jsonRteChild.children = jsonRteChild.children.map((subElement: any) => setDirtyTrue(subElement)); } } + return jsonRteChild; } function resolveAssetRefsInEntryRefsForJsonRte(jsonRteChild: any, mappedAssetUids: any, mappedAssetUrls: any) { if (jsonRteChild.type) { - if (jsonRteChild.attrs.type === 'asset') { + if (jsonRteChild.attrs?.type === 'asset') { let assetUrl; if (mappedAssetUids[jsonRteChild.attrs['asset-uid']]) { jsonRteChild.attrs['asset-uid'] = mappedAssetUids[jsonRteChild.attrs['asset-uid']]; @@ -597,3 +587,73 @@ function resolveAssetRefsInEntryRefsForJsonRte(jsonRteChild: any, mappedAssetUid return jsonRteChild; } + +/** + * The function removes references from a JSON RTE (Rich Text Editor) object. + * @param {EntryJsonRTEFieldDataType} jsonRTE - The parameter `jsonRTE` is of type + * `EntryJsonRTEFieldDataType`. It represents a JSON object that contains rich text content. The + * function `removeReferenceInJsonRTE` takes this JSON object as input and removes any references + * present in the content. It recursively traverses the JSON + * @returns the modified `jsonRTE` object after removing any references in the JSON RTE. + */ +function removeReferenceInJsonRTE(jsonRTE: EntryJsonRTEFieldDataType): EntryJsonRTEFieldDataType { + // NOTE Other possible reference logic will be added related to JSON RTE (Ex missing assets, extensions etc.,) + if (jsonRTE?.children && Array.isArray(jsonRTE.children)) { + jsonRTE.children = jsonRTE?.children?.map((child) => { + const { children, attrs, type } = child; + + if (type === 'reference' && attrs?.['entry-uid']) { + child = { + type: 'p', + attrs: {}, + children: [{ text: '' }], + }; + } + + if (!_.isEmpty(children)) { + return removeReferenceInJsonRTE(child); + } + + return child; + }); + } + + return jsonRTE; +} + +/** + * The function `restoreReferenceInJsonRTE` takes a JSON object `jsonRTE` and a mapping object + * `uidMapper`, and recursively replaces the `entry-uid` attribute values in any `reference` type + * elements with their corresponding values from the `uidMapper` object. + * @param {EntryJsonRTEFieldDataType} jsonRTE - The `jsonRTE` parameter is an object that represents a + * JSON structure. It contains a `children` property which is an array of objects. Each object + * represents a child element in the JSON structure and can have properties like `children`, `attrs`, + * and `type`. + * @param uidMapper - The `uidMapper` parameter is an object that maps entry UIDs to their + * corresponding restored UIDs. It is used to replace the `entry-uid` attribute in the JSON RTE with + * the restored UID. + * @returns the updated `jsonRTE` object with the restored references. + */ +function restoreReferenceInJsonRTE( + jsonRTE: EntryJsonRTEFieldDataType, + uidMapper: Record, +): EntryJsonRTEFieldDataType { + if (jsonRTE?.children && Array.isArray(jsonRTE.children)) { + jsonRTE.children = jsonRTE?.children?.map((child, index) => { + const { children, attrs, type } = child; + + if (type === 'reference' && attrs?.['entry-uid']) { + jsonRTE.children[index] = child; + jsonRTE.children[index].attrs['entry-uid'] = uidMapper[child.attrs['entry-uid']]; + } + + if (!_.isEmpty(children)) { + return restoreReferenceInJsonRTE(child, uidMapper); + } + + return child; + }); + } + + return jsonRTE; +} \ No newline at end of file diff --git a/packages/contentstack-launch/package.json b/packages/contentstack-launch/package.json index 634d01b52b..9b4a192d1e 100755 --- a/packages/contentstack-launch/package.json +++ b/packages/contentstack-launch/package.json @@ -105,4 +105,4 @@ "launch:deployments": "LNCH-DPLMNT" } } -} \ No newline at end of file +} diff --git a/packages/contentstack/package.json b/packages/contentstack/package.json index ae5c640ca6..d8dc8779c4 100755 --- a/packages/contentstack/package.json +++ b/packages/contentstack/package.json @@ -25,7 +25,7 @@ "@contentstack/cli-audit": "~1.3.0", "@contentstack/cli-auth": "~1.3.17", "@contentstack/cli-cm-bootstrap": "~1.7.1", - "@contentstack/cli-cm-branches": "~1.0.18", + "@contentstack/cli-cm-branches": "~1.0.19", "@contentstack/cli-cm-bulk-publish": "~1.3.15", "@contentstack/cli-cm-clone": "~1.8.0", "@contentstack/cli-cm-export": "~1.10.2", @@ -160,4 +160,4 @@ } }, "repository": "https://github.com/contentstack/cli" -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d336107cc..d5f4516dd3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: '@contentstack/cli-audit': ~1.3.0 '@contentstack/cli-auth': ~1.3.17 '@contentstack/cli-cm-bootstrap': ~1.7.1 - '@contentstack/cli-cm-branches': ~1.0.18 + '@contentstack/cli-cm-branches': ~1.0.19 '@contentstack/cli-cm-bulk-publish': ~1.3.15 '@contentstack/cli-cm-clone': ~1.8.0 '@contentstack/cli-cm-export': ~1.10.2