Skip to content

Commit 351a68e

Browse files
refactor: improve null safety in aem.service.ts and fieldMappings.merge.ts by adding optional chaining to prevent runtime errors
1 parent 1f0d2cb commit 351a68e

File tree

2 files changed

+89
-89
lines changed

2 files changed

+89
-89
lines changed

api/src/services/aem.service.ts

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ async function isAssetJsonCreated(assetJsonPath: string): Promise<boolean> {
9595
}
9696
}
9797

98-
// Helper function to remove Sanitize data and remove unwanted HTML tags and other chars
98+
// Helper function to sanitize data and remove unwanted HTML tags and other chars
9999
function stripHtmlTags(html: string): string {
100100
if (!html || typeof html !== 'string') return '';
101101

@@ -118,25 +118,25 @@ function getFieldValue(items: any, fieldName: string): any {
118118

119119
// Try camelCase conversion (snake_case → camelCase)
120120
// Handle both single letter and multiple letter segments
121-
const camelCaseFieldName = fieldName.replace(/_([a-z]+)/gi, (_, letters) => {
121+
const camelCaseFieldName = fieldName?.replace(/_([a-z]+)/gi, (_, letters) => {
122122
// Capitalize first letter, keep rest as-is for acronyms
123-
return letters.charAt(0).toUpperCase() + letters.slice(1);
123+
return letters?.charAt(0)?.toUpperCase() + letters?.slice(1);
124124
});
125125
if (items[camelCaseFieldName] !== undefined) {
126126
return items[camelCaseFieldName];
127127
}
128128

129-
// Try all uppercase version for acronyms (show_oos → showOOS)
130-
const acronymVersion = fieldName.replace(/_([a-z]+)/gi, (_, letters) => {
131-
return letters.toUpperCase();
129+
// Try all uppercase version for acronyms
130+
const acronymVersion = fieldName?.replace(/_([a-z]+)/gi, (_, letters) => {
131+
return letters?.toUpperCase();
132132
});
133133
if (items[acronymVersion] !== undefined) {
134134
return items[acronymVersion];
135135
}
136136

137137
// Try case-insensitive match as last resort
138138
const itemKeys = Object.keys(items);
139-
const matchedKey = itemKeys.find(key => key.toLowerCase() === fieldName.toLowerCase());
139+
const matchedKey = itemKeys?.find(key => key.toLowerCase() === fieldName?.toLowerCase());
140140
if (matchedKey && items[matchedKey] !== undefined) {
141141
return items[matchedKey];
142142
}
@@ -386,29 +386,29 @@ const createAssets = async ({
386386
const lastSegment = value?.split?.('/')?.pop?.();
387387
if (typeof lastSegment === 'string') {
388388
const assetsQueryPath = await fetchFilesByQuery(damPath, lastSegment);
389-
const firstJson = assetsQueryPath.find((fp: string) => fp?.endsWith?.('.json')) ?? null;
389+
const firstJson = assetsQueryPath?.find((fp: string) => fp?.endsWith?.('.json')) ?? null;
390390

391391
if (typeof firstJson === 'string' && firstJson?.endsWith('.json')) {
392392
const contentAst = await fs.promises.readFile(firstJson, 'utf-8');
393393
if (typeof contentAst === 'string') {
394394
const parseData = JSON.parse(contentAst);
395395
const filename = parseData?.asset?.name;
396396

397-
// Store mapping from this AEM path to filename
397+
// Store mapping from this AEM path to filename
398398
pathToFilenameMap.set(value, filename);
399399

400-
// Only create asset ONCE per unique filename
401-
if (!seenFilenames.has(filename)) {
400+
// Only create asset ONCE per unique filename
401+
if (!seenFilenames?.has(filename)) {
402402
const uid = uuidv4?.()?.replace?.(/-/g, '');
403403
const blobPath = firstJson?.replace?.('.metadata.json', '');
404404

405-
seenFilenames.set(filename, {
405+
seenFilenames?.set(filename, {
406406
uid,
407407
metadata: parseData,
408408
blobPath
409409
});
410410
} else {
411-
console.info(`Reusing asset: ${filename}${seenFilenames.get(filename)!.uid}`);
411+
console.info(`Reusing asset: ${filename}${seenFilenames?.get(filename)?.uid}`);
412412
}
413413
}
414414
}
@@ -422,7 +422,7 @@ const createAssets = async ({
422422
}
423423

424424
// Create physical asset files (one per unique filename)
425-
for (const [filename, assetInfo] of seenFilenames.entries()) {
425+
for (const [filename, assetInfo] of seenFilenames?.entries()) {
426426
const { uid, metadata, blobPath } = assetInfo;
427427
const nameWithoutExt = typeof filename === 'string'
428428
? filename.split('.').slice(0, -1).join('.')
@@ -462,7 +462,7 @@ const createAssets = async ({
462462

463463
// Build path-to-UID mapping (ALL paths map to the SAME deduplicated UID)
464464
for (const [aemPath, filename] of pathToFilenameMap.entries()) {
465-
const assetInfo = seenFilenames.get(filename);
465+
const assetInfo = seenFilenames?.get(filename);
466466
if (assetInfo) {
467467
pathToUidMap[aemPath] = assetInfo.uid;
468468

@@ -474,10 +474,10 @@ const createAssets = async ({
474474
}
475475

476476
// Create UID-based index.json
477-
for (const [filename, assetInfo] of seenFilenames.entries()) {
477+
for (const [filename, assetInfo] of seenFilenames?.entries()) {
478478
const { uid, metadata } = assetInfo;
479479
const nameWithoutExt = typeof filename === 'string'
480-
? filename.split('.').slice(0, -1).join('.')
480+
? filename?.split('.').slice(0, -1).join('.')
481481
: filename;
482482

483483
allAssetJSON[uid] = {
@@ -491,7 +491,7 @@ const createAssets = async ({
491491
parent_uid: null,
492492
title: nameWithoutExt,
493493
publish_details: [],
494-
assetPath: assetFirstPath.get(uid) || '',
494+
assetPath: assetFirstPath?.get(uid) ?? '',
495495
url: `https://images.contentstack.io/v3/assets/${destinationStackId}/${uid}/${filename}`,
496496
ACL: [],
497497
_version: 1
@@ -594,7 +594,7 @@ function processFieldsRecursive(
594594
obj[uid] = true;
595595
}
596596
else if (typeof value === 'string') {
597-
const lowerValue = value.toLowerCase().trim();
597+
const lowerValue = value?.toLowerCase()?.trim();
598598
if (lowerValue === 'true' || lowerValue === 'yes' || lowerValue === '1') {
599599
obj[uid] = true;
600600
} else if (lowerValue === 'false' || lowerValue === 'no' || lowerValue === '0' || lowerValue === '') {
@@ -722,7 +722,7 @@ function processFieldsRecursive(
722722
const aemFieldName = field?.otherCmsField ? getLastKey(field.otherCmsField, ' > ') : 'src';
723723
const imageSrc = getFieldValue(items, aemFieldName) || getFieldValue(items, 'src');
724724

725-
if (!imageSrc || !Object.keys(pathToUidMap)?.length) {
725+
if (!imageSrc || !Object?.keys(pathToUidMap)?.length) {
726726
obj[uid] = null;
727727
break;
728728
}
@@ -733,17 +733,17 @@ function processFieldsRecursive(
733733

734734
if (assetDetails) {
735735
obj[uid] = {
736-
uid: assetDetails.uid,
737-
filename: assetDetails.filename,
738-
content_type: assetDetails.content_type,
739-
file_size: assetDetails.file_size,
740-
title: assetDetails.title,
741-
url: assetDetails.url,
742-
tags: assetDetails.tags || [],
743-
publish_details: assetDetails.publish_details || [],
744-
parent_uid: assetDetails.parent_uid || null,
736+
uid: assetDetails?.uid,
737+
filename: assetDetails?.filename,
738+
content_type: assetDetails?.content_type,
739+
file_size: assetDetails?.file_size,
740+
title: assetDetails?.title,
741+
url: assetDetails?.url,
742+
tags: assetDetails?.tags || [],
743+
publish_details: assetDetails?.publish_details || [],
744+
parent_uid: assetDetails?.parent_uid || null,
745745
is_dir: false,
746-
ACL: assetDetails.ACL || []
746+
ACL: assetDetails?.ACL || []
747747
};
748748
} else {
749749
obj[uid] = {
@@ -868,20 +868,20 @@ const createEntry = async ({
868868
const flatData = deepFlattenObject(entry);
869869
for (const [key, value] of Object.entries(flatData)) {
870870
if (key.endsWith('._content_type_uid') && typeof value === 'string') {
871-
const uidField = key.replace('._content_type_uid', '');
871+
const uidField = key?.replace('._content_type_uid', '');
872872
const refs: string[] = entryMapping?.[value];
873873

874874
if (refs?.length) {
875-
_.set(entry, `${uidField}.uid`, refs[0]);
875+
_.set(entry, `${uidField}.uid`, refs?.[0]);
876876
} else {
877-
console.info(`No entry found for content type: ${value}`);
877+
console.info(`No entry found for content type: ${value}`);
878878
}
879879
}
880880
}
881881
}
882882
const entriesObject: Record<string, any> = {};
883883
for (const entry of entries) {
884-
entriesObject[entry.uid] = entry;
884+
entriesObject[entry?.uid] = entry;
885885
}
886886
const fileMeta = { '1': `${locale}.json` };
887887
const entryPath = path.join(

0 commit comments

Comments
 (0)