Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 83 additions & 15 deletions api/src/services/aem.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,14 +356,14 @@ export async function fetchFilesByQuery(

function addUidToEntryMapping(
entryMapping: Record<string, string[]>,
contentType: ContentType | undefined,
resolvedCtUid: string,
uid: string
) {
if (!contentType?.contentstackUid) return;
if (!entryMapping[contentType.contentstackUid]) {
entryMapping[contentType.contentstackUid] = [];
if (!resolvedCtUid) return;
if (!entryMapping[resolvedCtUid]) {
entryMapping[resolvedCtUid] = [];
}
entryMapping[contentType.contentstackUid].push(uid);
entryMapping[resolvedCtUid].push(uid);
}

function uidCorrector(str: string): string {
Expand Down Expand Up @@ -587,17 +587,71 @@ function processFieldsRecursive(
for (const field of fields) {
switch (field?.contentstackFieldType) {
case 'modular_blocks': {
const modularData = items?.[field?.uid] ? items?.[field?.uid] : items?.[':items'];
const uid = getLastKey(field?.contentstackFieldUid);
const modularBlocksArray: any[] = [];
const aemSourcePath = field?.backupFieldUid || field?.otherCmsField?.replace?.(/ > /g, '.') || '';
const aemSourceField = getLastKey(aemSourcePath);
const sourceData = aemSourceField ? (items?.[aemSourceField] || items) : items;

if (Array.isArray(field?.schema)) {
const itemsData = modularData?.[':items'] ?? modularData;
const value = processFieldsRecursive(field.schema, itemsData, title, pathToUidMap, assetDetailsMap);
const uid = getLastKey(field?.contentstackFieldUid);
obj[uid] = value;
// Process each child block schema
for (const childBlockSchema of field.schema) {
if (childBlockSchema?.contentstackFieldType === 'modular_blocks_child') {
const blockTypeUid = getLastKey(childBlockSchema?.contentstackFieldUid);
const aemChildPath = childBlockSchema?.backupFieldUid ||
childBlockSchema?.otherCmsField?.replace?.(/ > /g, '.') || '';
const aemBlockUid = getLastKey(aemChildPath);

// Find matching items in the source data
const itemsList: any[] = (() => {
if (Array.isArray(sourceData)) return sourceData;
const order = Array.isArray(sourceData?.[':itemsOrder']) ? sourceData[':itemsOrder'] : null;
const map = sourceData?.[':items'] || sourceData;
if (order && map) return order.map((k: string) => map?.[k]).filter(Boolean);
return Object.values(map || {});
})();

// Process each item that matches current block type
for (const item of itemsList) {
if (!item || typeof item !== 'object') continue;

const typeValue = (item as any)[':type'] || '';
const getTypeComp = getLastKey(typeValue, '/');
const isMatch = (aemBlockUid && getTypeComp === aemBlockUid) ||
getTypeComp === blockTypeUid ||
(aemBlockUid && item?.['sling:resourceType']?.includes(aemBlockUid)) ||
item?.['sling:resourceType']?.includes(blockTypeUid);

if (isMatch) {
// Process the fields within this child block
if (Array.isArray(childBlockSchema?.schema)) {
const blockData = processFieldsRecursive(
childBlockSchema.schema,
item,
title,
pathToUidMap,
assetDetailsMap
);

if (blockData && Object.keys(blockData).length) {
const blockEntry: any = {};
blockEntry[blockTypeUid] = blockData;
modularBlocksArray.push(blockEntry);
}
}
}
}
}
}
}

obj[uid] = modularBlocksArray;
break;
}

case 'modular_blocks_child': {
// This case is handled in the modular_blocks case above
// This is for backwards compatibility
const list: any[] = (() => {
if (Array.isArray(items)) return items;
const order = Array.isArray(items?.[':itemsOrder']) ? items[':itemsOrder'] : null;
Expand All @@ -615,7 +669,14 @@ function processFieldsRecursive(
const getTypeComp = getLastKey(typeValue, '/');
if (getTypeComp !== blockTypeUid) continue;

const compValue = processFieldsRecursive(field.schema, value, title, pathToUidMap, assetDetailsMap);
const compValue = processFieldsRecursive(
field.schema,
value,
title,
pathToUidMap,
assetDetailsMap
);

if (compValue && Object.keys(compValue).length) {
const objData: any = {};
objData[uid] = compValue;
Expand Down Expand Up @@ -1206,7 +1267,8 @@ const createEntry = async ({
contentTypes,
destinationStackId,
projectId,
project
project,
keyMapper
}: CreateEntryOptions) => {
const srcFunc = 'createEntry';
const baseDir = path.join(baseDirName, destinationStackId);
Expand Down Expand Up @@ -1262,9 +1324,15 @@ const createEntry = async ({
data.publish_details = [];

if (contentType?.contentstackUid && data && mappedLocale) {
const resolvedCtUid: string =
(keyMapper as Record<string, string>)?.[contentType.contentstackUid] !== '' &&
(keyMapper as Record<string, string>)?.[contentType.contentstackUid] !== undefined
? (keyMapper as Record<string, string>)[contentType.contentstackUid]
: contentType.contentstackUid;

const message = getLogMessage(
srcFunc,
`Entry title "${data?.title}"(${contentType?.contentstackUid}) in the ${mappedLocale} locale has been successfully transformed.`,
`Entry title "${data?.title}"(${resolvedCtUid}) in the ${mappedLocale} locale has been successfully transformed.`,
{}
);
await customLogger(
Expand All @@ -1273,8 +1341,8 @@ const createEntry = async ({
'info',
message
);
addEntryToEntriesData(entriesData, contentType.contentstackUid, data, mappedLocale);
addUidToEntryMapping(entryMapping, contentType, uid);
addEntryToEntriesData(entriesData, resolvedCtUid, data, mappedLocale);
addUidToEntryMapping(entryMapping, resolvedCtUid, uid);
}
}
}
Expand Down
89 changes: 77 additions & 12 deletions api/src/utils/content-type-creator.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,11 +238,11 @@ function getLastSegmentNew(str: string, separator: string): string {
}

export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', oldParentUid = ''): any[] {

if (!Array.isArray(fields)) {
console.warn('buildSchemaTree called with invalid fields:', fields);
return [];
}

// Build a lookup map for O(1) access
const fieldMap = new Map<string, any>();
fields?.forEach(f => {
Expand All @@ -267,7 +267,7 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = '',
return remainder && !remainder?.includes('.');
}

// Fallback: check if field is a direct child of oldPrentUid (if provided and different)
// Fallback: check if field is a direct child of oldParentUid (if provided and different from parentUid)
if (oldParentUid && oldParentUid !== parentUid && fieldUid?.startsWith(oldParentUid + '.')) {
const remainder = fieldUid?.substring(oldParentUid.length + 1);
// Verify it's exactly one level deeper (no more dots in remainder)
Expand Down Expand Up @@ -305,7 +305,7 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = '',
return remainder && !remainder?.includes('.');
}

// Check if field starts with oldFieldtUid and is exactly one level deeper
// Check if field starts with oldFieldUid and is exactly one level deeper
if (oldFieldUid && fUid?.startsWith(oldFieldUid + '.')) {
const remainder = fUid?.substring(oldFieldUid.length + 1);
return remainder && !remainder?.includes('.');
Expand All @@ -332,6 +332,7 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = '',
...child,
uid: childUid,
display_name: childDisplay,
// Recursively build schema for fields inside this child block
schema: buildSchemaTree(fields, child.contentstackFieldUid, 'modular_blocks_child', child?.backupFieldUid)
};
});
Expand Down Expand Up @@ -1028,22 +1029,86 @@ const mergeTwoCts = async (ct: any, mergeCts: any) => {
"singleton": false,
}
}

for await (const field of ctData?.schema ?? []) {
// Handle regular groups
if (field?.data_type === 'group') {
const currentGroup = mergeCts?.schema?.find((grp: any) => grp?.uid === field?.uid &&
grp?.data_type === 'group');
const group = [];
for await (const fieldGp of currentGroup?.schema ?? []) {
const fieldNst = field?.schema?.find((fld: any) => fld?.uid === fieldGp?.uid &&
fld?.data_type === fieldGp?.data_type);
if (fieldNst === undefined) {
group?.push(fieldGp);
const currentGroup = mergeCts?.schema?.find((grp: any) =>
grp?.uid === field?.uid && grp?.data_type === 'group'
);

if (currentGroup) {
const group = [];
for await (const fieldGp of currentGroup?.schema ?? []) {
const fieldNst = field?.schema?.find((fld: any) =>
fld?.uid === fieldGp?.uid && fld?.data_type === fieldGp?.data_type
);
if (fieldNst === undefined) {
group?.push(fieldGp);
}
}
field.schema = removeDuplicateFields([...field?.schema ?? [], ...group]);
}
}

// Handle modular blocks
if (field?.data_type === 'blocks') {
const currentModularBlock = mergeCts?.schema?.find((mb: any) =>
mb?.uid === field?.uid && mb?.data_type === 'blocks'
);

if (currentModularBlock && currentModularBlock?.blocks) {
// Iterate through each child block in the source
for (const sourceBlock of field?.blocks ?? []) {
// Find matching child block in target by UID
const targetBlock = currentModularBlock?.blocks?.find((tb: any) =>
tb?.uid === sourceBlock?.uid
);

if (targetBlock && targetBlock?.schema) {
// Merge the schemas of matching child blocks
const additionalFields = [];

for (const targetField of targetBlock?.schema ?? []) {
// Check if this field already exists in source block
const existsInSource = sourceBlock?.schema?.find((sf: any) =>
sf?.uid === targetField?.uid && sf?.data_type === targetField?.data_type
);

if (!existsInSource) {
additionalFields.push(targetField);
}
}

// Merge source and target fields, removing duplicates
sourceBlock.schema = removeDuplicateFields([
...sourceBlock?.schema ?? [],
...additionalFields
]);
}
}

// Add any child blocks from target that don't exist in source
const additionalBlocks = [];
for (const targetBlock of currentModularBlock?.blocks ?? []) {
const existsInSource = field?.blocks?.find((sb: any) =>
sb?.uid === targetBlock?.uid
);

if (!existsInSource) {
additionalBlocks.push(targetBlock);
}
}

field.blocks = removeDuplicateFields([
...field?.blocks ?? [],
...additionalBlocks
]);
}
field.schema = removeDuplicateFields([...field?.schema ?? [], ...group]);
}
}
ctData.schema = await mergeArrays(ctData?.schema, mergeCts?.schema) ?? [];

return ctData;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export interface FieldMetadata {
allow_json_rte?: boolean;
}
export interface ContentTypesSchema {
blocks?: ContentTypesSchema[];
display_type: string;
data_type?:
| 'text'
Expand All @@ -116,7 +117,9 @@ export interface ContentTypesSchema {
| 'boolean'
| 'link'
| 'Marketplace app'
| 'Extension';
| 'Extension'
| 'blocks'
| 'modular_blocks_child';
display_name: string;
enum?: any;
error_messages?: ErrorMessages;
Expand Down
Loading
Loading