From f4b9d57eff1da156715af3e750146f645041d9c5 Mon Sep 17 00:00:00 2001 From: AishDani Date: Tue, 9 Dec 2025 15:36:57 +0530 Subject: [PATCH 1/4] fix: Update buildSchemaTree to handle old parent UID and improve field UID checks --- api/src/utils/content-type-creator.utils.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/api/src/utils/content-type-creator.utils.ts b/api/src/utils/content-type-creator.utils.ts index 9bc51d83a..f33b4b388 100644 --- a/api/src/utils/content-type-creator.utils.ts +++ b/api/src/utils/content-type-creator.utils.ts @@ -237,7 +237,7 @@ function getLastSegmentNew(str: string, separator: string): string { return segments[segments.length - 1].trim(); } -export function buildSchemaTree(fields: any[], parentUid = '', parentType = ''): any[] { +export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', oldPrentUid = ''): any[] { if (!Array.isArray(fields)) { console.warn('buildSchemaTree called with invalid fields:', fields); @@ -261,10 +261,10 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = ''): } // Check if direct child of parent - if (!fieldUid.startsWith(parentUid + '.')) return false; + //if (!fieldUid.startsWith(parentUid + '.')) return false; // Verify it's exactly one level deeper - const remainder = fieldUid.substring(parentUid.length + 1); + const remainder = fieldUid.startsWith(parentUid) ? fieldUid.substring(parentUid.length + 1) : fieldUid.substring(oldPrentUid.length + 1); return remainder && !remainder.includes('.'); }); @@ -282,11 +282,12 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = ''): // Determine if field should have nested schema const fieldUid = field.contentstackFieldUid; const fieldType = field.contentstackFieldType; + const oldFieldtUid = field?.backupFieldUid || ''; // Check if this field has children const hasChildren = fields.some(f => f.contentstackFieldUid && - f.contentstackFieldUid.startsWith(fieldUid + '.') + (f.contentstackFieldUid.startsWith(fieldUid + '.') || f.contentstackFieldUid.startsWith(oldFieldtUid + '.')) ); if (hasChildren) { @@ -307,13 +308,14 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = ''): ...child, uid: childUid, display_name: childDisplay, - schema: buildSchemaTree(fields, child.contentstackFieldUid, 'modular_blocks_child') + schema: buildSchemaTree(fields, child.contentstackFieldUid, 'modular_blocks_child', field?.backupFieldUid || '') }; }); } else if (fieldType === 'group' || (fieldType === 'modular_blocks_child' && hasChildren)) { + //console.info(`Building schema for group/modular_blocks_child: ${fieldUid}`); // Recursively build schema for groups and modular block children with nested content - result.schema = buildSchemaTree(fields, fieldUid, fieldType); + result.schema = buildSchemaTree(fields, fieldUid, fieldType, field?.backupFieldUid); } } @@ -1026,7 +1028,7 @@ export const contenTypeMaker = async ({ contentType, destinationStackId, project // Use the deep converter that properly handles groups & modular blocks for (const item of ctData) { if (item?.isDeleted === true) continue; - + //console.info("item --> ", item) const fieldSchema = buildFieldSchema(item, marketPlacePath, ''); if (fieldSchema) { ct?.schema.push(fieldSchema); @@ -1037,6 +1039,7 @@ export const contenTypeMaker = async ({ contentType, destinationStackId, project ct.schema = removeDuplicateFields(ct.schema || []); if (currentCt?.uid) { + console.info('Merging with existing content type:', ctData); ct = await mergeTwoCts(ct, currentCt); } if (ct?.uid && Array.isArray(ct?.schema) && ct?.schema.length) { From 4ac47644647e05a95832e29facc9cfe3c9c3e1b4 Mon Sep 17 00:00:00 2001 From: AishDani Date: Wed, 10 Dec 2025 13:01:12 +0530 Subject: [PATCH 2/4] fix: Enhance buildFieldSchema and buildSchemaTree functions to handle duplicate fields and improve UID checks --- api/src/utils/content-type-creator.utils.ts | 68 ++++++++++++++------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/api/src/utils/content-type-creator.utils.ts b/api/src/utils/content-type-creator.utils.ts index f33b4b388..af1f782dc 100644 --- a/api/src/utils/content-type-creator.utils.ts +++ b/api/src/utils/content-type-creator.utils.ts @@ -200,7 +200,7 @@ function buildFieldSchema(item: any, marketPlacePath: string, parentUid = ''): a data_type: "group", display_name: item?.display_name || rawUid, // Keep original for display field_metadata: {}, - schema: groupSchema, + schema: removeDuplicateFields(groupSchema), uid: itemUid, // Snake case uid multiple: item?.advanced?.multiple || false, mandatory: item?.advanced?.mandatory || false, @@ -245,9 +245,9 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', } // Build a lookup map for O(1) access const fieldMap = new Map(); - fields.forEach(f => { - if (f.contentstackFieldUid) { - fieldMap.set(f.contentstackFieldUid, f); + fields?.forEach(f => { + if (f?.contentstackFieldUid) { + fieldMap?.set(f?.contentstackFieldUid, f); } }); @@ -260,12 +260,22 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', return fieldUid && !fieldUid.includes('.'); } - // Check if direct child of parent - //if (!fieldUid.startsWith(parentUid + '.')) return false; + // Check if field is a direct child of parentUid + if (fieldUid.startsWith(parentUid + '.')) { + const remainder = fieldUid.substring(parentUid.length + 1); + // Verify it's exactly one level deeper (no more dots in remainder) + return remainder && !remainder.includes('.'); + } + + // Fallback: check if field is a direct child of oldPrentUid (if provided and different) + if (oldPrentUid && oldPrentUid !== parentUid && fieldUid.startsWith(oldPrentUid + '.')) { + const remainder = fieldUid.substring(oldPrentUid.length + 1); + // Verify it's exactly one level deeper (no more dots in remainder) + return remainder && !remainder.includes('.'); + } - // Verify it's exactly one level deeper - const remainder = fieldUid.startsWith(parentUid) ? fieldUid.substring(parentUid.length + 1) : fieldUid.substring(oldPrentUid.length + 1); - return remainder && !remainder.includes('.'); + // Not a direct child + return false; }); return directChildren.map(field => { @@ -282,13 +292,27 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', // Determine if field should have nested schema const fieldUid = field.contentstackFieldUid; const fieldType = field.contentstackFieldType; - const oldFieldtUid = field?.backupFieldUid || ''; - - // Check if this field has children - const hasChildren = fields.some(f => - f.contentstackFieldUid && - (f.contentstackFieldUid.startsWith(fieldUid + '.') || f.contentstackFieldUid.startsWith(oldFieldtUid + '.')) - ); + const oldFieldtUid = field.backupFieldUid; + + // Check if this field has direct children (exactly one level deeper) + const hasChildren = fields.some(f => { + const fUid = f.contentstackFieldUid || ''; + if (!fUid) return false; + + // Check if field starts with current fieldUid and is exactly one level deeper + if (fieldUid && fUid.startsWith(fieldUid + '.')) { + const remainder = fUid.substring(fieldUid.length + 1); + return remainder && !remainder.includes('.'); + } + + // Check if field starts with oldFieldtUid and is exactly one level deeper + if (oldFieldtUid && fUid.startsWith(oldFieldtUid + '.')) { + const remainder = fUid.substring(oldFieldtUid.length + 1); + return remainder && !remainder.includes('.'); + } + + return false; + }); if (hasChildren) { if (fieldType === 'modular_blocks') { @@ -308,14 +332,13 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', ...child, uid: childUid, display_name: childDisplay, - schema: buildSchemaTree(fields, child.contentstackFieldUid, 'modular_blocks_child', field?.backupFieldUid || '') + schema: buildSchemaTree(fields, child.contentstackFieldUid, 'modular_blocks_child', child?.backupFieldUid) }; }); } else if (fieldType === 'group' || (fieldType === 'modular_blocks_child' && hasChildren)) { - //console.info(`Building schema for group/modular_blocks_child: ${fieldUid}`); // Recursively build schema for groups and modular block children with nested content - result.schema = buildSchemaTree(fields, fieldUid, fieldType, field?.backupFieldUid); + result.schema = buildSchemaTree(fields, fieldUid, fieldType, oldFieldtUid); } } @@ -998,7 +1021,7 @@ const mergeTwoCts = async (ct: any, mergeCts: any) => { group?.push(fieldGp); } } - field.schema = [...field?.schema ?? [], ...group]; + field.schema = removeDuplicateFields([...field?.schema ?? [], ...group]); } } ctData.schema = await mergeArrays(ctData?.schema, mergeCts?.schema) ?? []; @@ -1024,11 +1047,11 @@ export const contenTypeMaker = async ({ contentType, destinationStackId, project // Safe: ensures we never pass undefined to the builder const ctData: any[] = buildSchemaTree(contentType?.fieldMapping || []); - + // Use the deep converter that properly handles groups & modular blocks for (const item of ctData) { if (item?.isDeleted === true) continue; - //console.info("item --> ", item) + const fieldSchema = buildFieldSchema(item, marketPlacePath, ''); if (fieldSchema) { ct?.schema.push(fieldSchema); @@ -1039,7 +1062,6 @@ export const contenTypeMaker = async ({ contentType, destinationStackId, project ct.schema = removeDuplicateFields(ct.schema || []); if (currentCt?.uid) { - console.info('Merging with existing content type:', ctData); ct = await mergeTwoCts(ct, currentCt); } if (ct?.uid && Array.isArray(ct?.schema) && ct?.schema.length) { From 0271527e8f16375085e42a391d40f5c011b839d2 Mon Sep 17 00:00:00 2001 From: AishDani Date: Wed, 10 Dec 2025 14:54:15 +0530 Subject: [PATCH 3/4] fix: Refactor buildSchemaTree function to improve null safety and handle optional chaining for field properties --- api/src/utils/content-type-creator.utils.ts | 40 ++++++++++----------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/api/src/utils/content-type-creator.utils.ts b/api/src/utils/content-type-creator.utils.ts index af1f782dc..8e9d514a1 100644 --- a/api/src/utils/content-type-creator.utils.ts +++ b/api/src/utils/content-type-creator.utils.ts @@ -237,7 +237,7 @@ function getLastSegmentNew(str: string, separator: string): string { return segments[segments.length - 1].trim(); } -export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', oldPrentUid = ''): any[] { +export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', oldParentUid = ''): any[] { if (!Array.isArray(fields)) { console.warn('buildSchemaTree called with invalid fields:', fields); @@ -253,25 +253,25 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', // Filter direct children of current parent const directChildren = fields.filter(field => { - const fieldUid = field.contentstackFieldUid || ''; + const fieldUid = field?.contentstackFieldUid || ''; if (!parentUid) { // Root level - only fields without dots - return fieldUid && !fieldUid.includes('.'); + return fieldUid && !fieldUid?.includes('.'); } // Check if field is a direct child of parentUid - if (fieldUid.startsWith(parentUid + '.')) { - const remainder = fieldUid.substring(parentUid.length + 1); + if (fieldUid?.startsWith(parentUid + '.')) { + const remainder = fieldUid?.substring(parentUid.length + 1); // Verify it's exactly one level deeper (no more dots in remainder) - return remainder && !remainder.includes('.'); + return remainder && !remainder?.includes('.'); } // Fallback: check if field is a direct child of oldPrentUid (if provided and different) - if (oldPrentUid && oldPrentUid !== parentUid && fieldUid.startsWith(oldPrentUid + '.')) { - const remainder = fieldUid.substring(oldPrentUid.length + 1); + 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) - return remainder && !remainder.includes('.'); + return remainder && !remainder?.includes('.'); } // Not a direct child @@ -280,7 +280,7 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', return directChildren.map(field => { const uid = getLastSegmentNew(field.contentstackFieldUid, '.'); - const displayName = field.display_name || getLastSegmentNew(field.contentstackField || '', '>').trim(); + const displayName = field?.display_name || getLastSegmentNew(field?.contentstackField || '', '>').trim(); // Base field structure const result: any = { @@ -290,25 +290,25 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', }; // Determine if field should have nested schema - const fieldUid = field.contentstackFieldUid; - const fieldType = field.contentstackFieldType; - const oldFieldtUid = field.backupFieldUid; + const fieldUid = field?.contentstackFieldUid; + const fieldType = field?.contentstackFieldType; + const oldFieldUid = field?.backupFieldUid; // Check if this field has direct children (exactly one level deeper) const hasChildren = fields.some(f => { - const fUid = f.contentstackFieldUid || ''; + const fUid = f?.contentstackFieldUid || ''; if (!fUid) return false; // Check if field starts with current fieldUid and is exactly one level deeper if (fieldUid && fUid.startsWith(fieldUid + '.')) { - const remainder = fUid.substring(fieldUid.length + 1); - return remainder && !remainder.includes('.'); + const remainder = fUid?.substring(fieldUid.length + 1); + return remainder && !remainder?.includes('.'); } // Check if field starts with oldFieldtUid and is exactly one level deeper - if (oldFieldtUid && fUid.startsWith(oldFieldtUid + '.')) { - const remainder = fUid.substring(oldFieldtUid.length + 1); - return remainder && !remainder.includes('.'); + if (oldFieldUid && fUid.startsWith(oldFieldUid + '.')) { + const remainder = fUid.substring(oldFieldUid.length + 1); + return remainder && !remainder?.includes('.'); } return false; @@ -338,7 +338,7 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', } else if (fieldType === 'group' || (fieldType === 'modular_blocks_child' && hasChildren)) { // Recursively build schema for groups and modular block children with nested content - result.schema = buildSchemaTree(fields, fieldUid, fieldType, oldFieldtUid); + result.schema = buildSchemaTree(fields, fieldUid, fieldType, oldFieldUid); } } From 47f7a3eb2e8281f317a083c78507c5c2fb4846b8 Mon Sep 17 00:00:00 2001 From: AishDani Date: Wed, 10 Dec 2025 15:26:56 +0530 Subject: [PATCH 4/4] fix: Improve null safety in buildSchemaTree by adding optional chaining for field UIDs --- api/src/utils/content-type-creator.utils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/src/utils/content-type-creator.utils.ts b/api/src/utils/content-type-creator.utils.ts index 8e9d514a1..a4b2ab3f0 100644 --- a/api/src/utils/content-type-creator.utils.ts +++ b/api/src/utils/content-type-creator.utils.ts @@ -279,7 +279,7 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', }); return directChildren.map(field => { - const uid = getLastSegmentNew(field.contentstackFieldUid, '.'); + const uid = getLastSegmentNew(field?.contentstackFieldUid, '.'); const displayName = field?.display_name || getLastSegmentNew(field?.contentstackField || '', '>').trim(); // Base field structure @@ -300,14 +300,14 @@ export function buildSchemaTree(fields: any[], parentUid = '', parentType = '', if (!fUid) return false; // Check if field starts with current fieldUid and is exactly one level deeper - if (fieldUid && fUid.startsWith(fieldUid + '.')) { + if (fieldUid && fUid?.startsWith(fieldUid + '.')) { const remainder = fUid?.substring(fieldUid.length + 1); return remainder && !remainder?.includes('.'); } // Check if field starts with oldFieldtUid and is exactly one level deeper - if (oldFieldUid && fUid.startsWith(oldFieldUid + '.')) { - const remainder = fUid.substring(oldFieldUid.length + 1); + if (oldFieldUid && fUid?.startsWith(oldFieldUid + '.')) { + const remainder = fUid?.substring(oldFieldUid.length + 1); return remainder && !remainder?.includes('.'); }