Skip to content

Commit 4a6c1d4

Browse files
committed
fix: add error handling for missing files and refactor variable names
- Add try-catch blocks in fetchJSON to handle file not found errors for Browser File System Access API and Node.js local paths, returning null and logging errors instead of crashing - Add null check in mergeAndSortFixtures to skip processing when fetchJSON returns null - Rename variables in mergeMenuDictionaries (e.g., submenus to entries) for better consistency and clarity This improves robustness by preventing runtime errors when files are missing and enhances code maintainability through clearer naming.
1 parent 957e5e2 commit 4a6c1d4

2 files changed

Lines changed: 112 additions & 76 deletions

File tree

solutionBuilder.js

Lines changed: 102 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -41,33 +41,44 @@ async function fetchJSON(handleOrUrl,DIRECTORY=null, rootPath = '') {
4141
}
4242
// Case 2: Browser File System Access API (directory handle)
4343
else if (typeof DIRECTORY !== 'undefined' && typeof DIRECTORY !== 'string' && DIRECTORY) {
44+
try {
45+
if (typeof handleOrUrl == 'string') {
46+
// Split the path into parts (e.g., "subfolder/file.txt" -> ["subfolder", "file.txt"])
47+
const pathParts = handleOrUrl.split('/').filter(part => part.length > 0);
48+
let currentDir = DIRECTORY;
49+
50+
// Navigate directories if there are multiple parts
51+
for (let i = 0; i < pathParts.length - 1; i++) {
52+
currentDir = await currentDir.getDirectoryHandle(pathParts[i], { create: false });
53+
}
4454

45-
if (typeof handleOrUrl == 'string') {
46-
// Split the path into parts (e.g., "subfolder/file.txt" -> ["subfolder", "file.txt"])
47-
const pathParts = handleOrUrl.split('/').filter(part => part.length > 0);
48-
let currentDir = DIRECTORY;
49-
50-
// Navigate directories if there are multiple parts
51-
for (let i = 0; i < pathParts.length - 1; i++) {
52-
currentDir = await currentDir.getDirectoryHandle(pathParts[i], { create: false });
55+
// Get the file handle from the final directory
56+
const fileName = pathParts[pathParts.length - 1];
57+
fileHandle = await currentDir.getFileHandle(fileName);
58+
} else {
59+
// Assume handleOrUrl is an object with a handle property (file or directory handle)
60+
fileHandle = handleOrUrl;
5361
}
54-
55-
// Get the file handle from the final directory
56-
const fileName = pathParts[pathParts.length - 1];
57-
fileHandle = await currentDir.getFileHandle(fileName);
58-
} else {
59-
// Assume handleOrUrl is an object with a handle property (file or directory handle)
60-
fileHandle = handleOrUrl;
62+
const file = await fileHandle.getFile();
63+
const text = await file.text();
64+
return JSON.parse(text);
65+
} catch (error) {
66+
console.error(`File not found: ${handleOrUrl}`);
67+
return null;
6168
}
62-
const file = await fileHandle.getFile();
63-
const text = await file.text();
64-
return JSON.parse(text);
65-
}
69+
}
6670
// Case 3: Local file path (Node.js)
6771
else if (typeof window === 'undefined' && typeof handleOrUrl === 'string') {
72+
try {
73+
fs.accessSync(handleOrUrl, fs.constants.F_OK);
74+
} catch (error) {
75+
console.error(`File not found: ${handleOrUrl}`);
76+
return null;
77+
}
6878
const text = fs.readFileSync(handleOrUrl, 'utf8');
6979
// Create a timeout Promise that resolves after a short delay
70-
return JSON.parse(text);
80+
const js_obj = JSON.parse(text);
81+
return js_obj
7182
}
7283
// Fallback: Throw an error if no valid case is matched
7384
else {
@@ -200,14 +211,15 @@ async function mergeAndSortFixtures(inputFiles, output) {
200211
console.log(`Processing file: ${filename}`);
201212

202213
const data = await fetchJSON(filePath);
203-
204-
// Group entries by model
205-
for (const entry of data) {
206-
const model = entry.model;
207-
if (!fixturesByModel[model]) {
208-
fixturesByModel[model] = [];
209-
}
210-
fixturesByModel[model].push(entry);
214+
if (data){
215+
// Group entries by model
216+
for (const entry of data) {
217+
const model = entry.model;
218+
if (!fixturesByModel[model]) {
219+
fixturesByModel[model] = [];
220+
}
221+
fixturesByModel[model].push(entry);
222+
}
211223
}
212224
}
213225

@@ -274,51 +286,51 @@ function mergeRoleDictionaries(mergedMenusDict, resultMenusDict) {
274286
}
275287

276288
function mergeMenuDictionaries(mergedMenusDict, resultMenusDict) {
277-
// Helper function to merge submenus
278-
function mergeSubmenus(existingSubmenus, newSubmenus, mainMenuId) {
279-
const submenus = existingSubmenus ? [...existingSubmenus] : [];
280-
if (!newSubmenus) return submenus;
289+
// Helper function to merge entries
290+
function mergeentries(existingentries, newentries, mainMenuId) {
291+
const entries = existingentries ? [...existingentries] : [];
292+
if (!newentries) return entries;
281293

282-
// Remove submenus from other main menus to ensure uniqueness
294+
// Remove entries from other main menus to ensure uniqueness
283295
function removeSubmenuFromOtherMenus(submenuId, targetMainMenuId) {
284296
for (const menuId in mergedMenusDict) {
285-
if (menuId !== targetMainMenuId && mergedMenusDict[menuId].submenus) {
286-
mergedMenusDict[menuId].submenus = mergedMenusDict[menuId].submenus.filter(
297+
if (menuId !== targetMainMenuId && mergedMenusDict[menuId].entries) {
298+
mergedMenusDict[menuId].entries = mergedMenusDict[menuId].entries.filter(
287299
submenu => submenu.id !== submenuId
288300
);
289301
}
290302
}
291303
}
292304

293-
// Merge or add new submenus
294-
newSubmenus.forEach(newSubmenu => {
305+
// Merge or add new entries
306+
newentries.forEach(newSubmenu => {
295307
removeSubmenuFromOtherMenus(newSubmenu.id, mainMenuId);
296-
const existingIndex = submenus.findIndex(submenu => submenu.id === newSubmenu.id);
308+
const existingIndex = entries.findIndex(submenu => submenu.id === newSubmenu.id);
297309
if (existingIndex !== -1) {
298310
// Update existing submenu
299-
submenus[existingIndex] = { ...newSubmenu };
311+
entries[existingIndex] = { ...newSubmenu };
300312
} else {
301313
// Add new submenu
302-
submenus.push({ ...newSubmenu });
314+
entries.push({ ...newSubmenu });
303315
}
304316
});
305317

306-
return submenus;
318+
return entries;
307319
}
308320

309321
// Iterate through resultMenusDict to merge into mergedMenusDict
310322
for (const menuId in resultMenusDict) {
311323
const resultMenu = resultMenusDict[menuId];
312324
const existingMenu = mergedMenusDict[menuId] || {};
313325

314-
// Merge main menu fields, preserving existing submenus if not provided in result
326+
// Merge main menu fields, preserving existing entries if not provided in result
315327
mergedMenusDict[menuId] = {
316328
position: resultMenu.position !== undefined ? resultMenu.position : existingMenu.position,
317329
id: menuId,
318330
name: resultMenu.name !== undefined ? resultMenu.name : existingMenu.name,
319331
icon: resultMenu.icon !== undefined ? resultMenu.icon : existingMenu.icon,
320332
description: resultMenu.description !== undefined ? resultMenu.description : existingMenu.description,
321-
submenus: mergeSubmenus(existingMenu.submenus, resultMenu.submenus, menuId)
333+
entries: mergeentries(existingMenu.entries, resultMenu.entries, menuId)
322334
};
323335
}
324336

@@ -329,7 +341,7 @@ function cleanMenuDictionaries(menusDict) {
329341

330342
// Iterate through resultMenusDict to merge into mergedMenusDict
331343
for (const menuId in menusDict) {
332-
if(menusDict[menuId].submenus.length === 0 || menusDict[menuId].name === undefined){
344+
if(menusDict[menuId].entries.length === 0 || menusDict[menuId].name === undefined){
333345
delete menusDict[menuId];
334346
}
335347
}
@@ -382,6 +394,8 @@ async function processSolutions(
382394
let merged = await mergeSolutions(solutionFile, directoryPath, permissionMap);
383395
let result = {};
384396

397+
// Commented out buggy for loop that tries to merge modules as solutions
398+
/*
385399
for (let key in merged.moduleRefDict || {}) {
386400
const depPath = getAbsolutePath(merged.moduleRefDict[key], solutionFilePath);
387401
result = await mergeSolutions(
@@ -411,9 +425,23 @@ async function processSolutions(
411425
}
412426
413427
}
428+
*/
414429

415430
merged.menusDict = cleanMenuDictionaries(merged.menusDict)
416431

432+
// Generate module-level permissions map
433+
let modulePermissionsMap = {};
434+
const includedModules = Object.keys(merged.moduleRefDict);
435+
for (const permKey in permissionMap) {
436+
const module = permKey.split('.')[0];
437+
if (includedModules.includes(module)) {
438+
if (!modulePermissionsMap[module]) {
439+
modulePermissionsMap[module] = {};
440+
}
441+
modulePermissionsMap[module][permKey] = permissionMap[permKey];
442+
}
443+
}
444+
417445
const assemblyBranch = merged.bePackagesDefDict['assembly']?.branch || 'develop';
418446

419447
let PIPModules = new Set()
@@ -436,7 +464,8 @@ async function processSolutions(
436464
}
437465

438466
output = {}
439-
// TODO manage the language in a better way
467+
output['module_permissions_map.json'] = modulePermissionsMap;
468+
// TODO manage the language in a better way
440469
if(NPMModules.size>0){
441470
output['fe-openimis.json'] ={
442471
"modules": [...NPMModules],
@@ -465,7 +494,7 @@ async function processSolutions(
465494
}
466495
// merging all fixture
467496

468-
output = mergeAndSortFixtures(merged.initData, output);
497+
output = await mergeAndSortFixtures(merged.initData, output);
469498

470499
// sorting fixture by model
471500

@@ -506,9 +535,9 @@ async function mergeSolutions(
506535
solution = solutionFile;
507536
solutionFilePath = typeof directoryPath === 'string'?directoryPath:''
508537
}
509-
if (solution.toString() == '[object Promise]') {
538+
if (solution.toString() == '[object Promise]' || !solution) {
510539
console.warn(`Failed to load solution file: ${solutionFile}`);
511-
return { modules, menuDict, roleDict };
540+
return { rolesDict, menusDict, moduleRefDict, bePackagesList, bePackagesDefDict, fePackagesList, fePackagesDefDict, locales, servicesList, servicesDefDict, initData };
512541
}
513542

514543
// Process solutions
@@ -634,11 +663,14 @@ function mergeRolesData(roles, permissionMap, roleDict) {
634663
const roleCode = role.code;
635664
const permissions = role.permissions || [];
636665
const mappedPermissions = permissions
637-
.filter(permission => permission in permissionMap)
638-
.map(permission => ({
639-
name: permission,
640-
code: permissionMap[permission]
641-
}));
666+
.filter(permission => {
667+
const code = permissionMap[permission];
668+
return code; // only keep permissions that have a truthy code
669+
})
670+
.map(permission => ({
671+
name: permission,
672+
code: permissionMap[permission]
673+
}));
642674

643675
if (mappedPermissions.length === 0) {
644676
continue;
@@ -670,8 +702,8 @@ function mergeMenusData(menus, menuDict) {
670702
// Helper function to find and remove submenu from all main menus
671703
function removeSubmenuFromOtherMenus(submenuId, targetMainMenuId) {
672704
for (const mainMenuId in menuDict) {
673-
if (mainMenuId !== targetMainMenuId && menuDict[mainMenuId].submenus) {
674-
menuDict[mainMenuId].submenus = menuDict[mainMenuId].submenus.filter(
705+
if (mainMenuId !== targetMainMenuId && menuDict[mainMenuId].entries) {
706+
menuDict[mainMenuId].entries = menuDict[mainMenuId].entries.filter(
675707
submenu => submenu.id !== submenuId
676708
);
677709
}
@@ -687,49 +719,53 @@ function mergeMenusData(menus, menuDict) {
687719
id: menu.id,
688720
name: menu.name,
689721
icon: menu.icon,
690-
description: menu.description
722+
route: menu.route,
723+
text: menu.text,
724+
description: menu.description,
725+
withDivider: menu.withDivider
691726
};
692727

693728
// Create minimal main menu if it doesn't exist
694729
if (!menuDict[mainMenuId]) {
695730
menuDict[mainMenuId] = {
696731
id: mainMenuId,
697-
submenus: []
732+
entries: []
698733
};
699734
}
700735

701736
// Remove this submenu from other main menus
702737
removeSubmenuFromOtherMenus(menu.id, mainMenuId);
703738

704739
// Add submenu to the target main menu
705-
if (!menuDict[mainMenuId].submenus) {
706-
menuDict[mainMenuId].submenus = [];
740+
if (!menuDict[mainMenuId].entries) {
741+
menuDict[mainMenuId].entries = [];
707742
}
708743
// Check if submenu already exists
709-
const existingSubmenuIndex = menuDict[mainMenuId].submenus.findIndex(
744+
const existingSubmenuIndex = menuDict[mainMenuId].entries.findIndex(
710745
sm => sm.id === menu.id
711746
);
712747
if (existingSubmenuIndex !== -1) {
713748
// Update existing submenu
714-
menuDict[mainMenuId].submenus[existingSubmenuIndex] = submenu;
749+
menuDict[mainMenuId].entries[existingSubmenuIndex] = submenu;
715750
} else {
716751
// Add new submenu
717-
menuDict[mainMenuId].submenus.push(submenu);
752+
menuDict[mainMenuId].entries.push(submenu);
718753
}
719754
}
720755
// Handle main menu payload
721756
else {
722757
const menuId = menu.id;
723-
// Preserve existing submenus if new payload doesn't include them
724-
const existingSubmenus = menuDict[menuId]?.submenus || [];
758+
// Preserve existing entries if new payload doesn't include them
759+
const existingentries = menuDict[menuId]?.entries || [];
725760

726761
menuDict[menuId] = {
727762
position: menu.position,
728763
id: menu.id,
729764
name: menu.name,
765+
text: menu.text,
730766
icon: menu.icon,
731767
description: menu.description,
732-
submenus: menu.submenus || existingSubmenus
768+
entries: menu.entries || existingentries
733769
};
734770
}
735771
}
@@ -930,4 +966,4 @@ async function createZip(data, filename) {
930966

931967

932968

933-
module.exports = { mergeSolutions, getAbsolutePath, createZip, processSolutions, createSolutionDirectory };
969+
module.exports = { mergeSolutions, getAbsolutePath, createZip, processSolutions, createSolutionDirectory };

workbench.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ async function copyDistDkrAssetsFromCompose(composeContent, branch = 'develop')
142142
const outputFiles = {};
143143

144144
// Ensure we are getting the paths from composeConfig
145-
if (composeContent.include && Array.isArray(composeContent.include)) {
145+
if (composeContent && composeContent.include && Array.isArray(composeContent.include)) {
146146
for (const item of composeContent.include) {
147147
if (item.path) {
148148
// Resolve path within dist-dkr repo
@@ -238,31 +238,31 @@ async function main() {
238238
const folderArg = args.find(arg => arg.startsWith('--folder='));
239239
const solutionName = folderArg ? folderArg.split('=')[1] : 'default-solution';
240240
const solutions = {
241-
// 'coreMIS': './solution/solutions/coreMIS.json',
241+
'coreMIS': './solution/solutions/coreMIS.json',
242242
// 'SHI': './solution/solutions/HF.json',
243243
// 'claimai': './solution/solutions/HF.json',
244-
'full' : './solution/solutions/full.json',
244+
//'full' : './solution/solutions/full.json',
245245
// 'SR': './solution/solutions/SR.json',
246246
// 'IBR': './solution/solutions/IBR.json'
247247
}
248248
const permission = fs.readFileSync('./solution/permissions_map.json', 'utf8');
249249
const permissionMap = JSON.parse(permission);
250-
let output = []
250+
let outputs = {}
251251
for (const [name, solution_path] of Object.entries(solutions)){
252252
console.log(`generating ${name}`)
253-
const { output: solutionOutput, modules, assemblyBranch } = await processSolutions(
253+
const { output, modules, assemblyBranch } = await processSolutions(
254254
solution_path,
255255
process.cwd(),
256256
permissionMap,
257257
);
258-
output[name] = solutionOutput;
258+
outputs[name] = output;
259259
// Get dist-dkr files from compose.yml and merge them into output
260-
const composeFiles = await copyDistDkrAssetsFromCompose(output[name]['compose.yml'], assemblyBranch);
261-
Object.assign(output[name], composeFiles);
260+
const composeFiles = await copyDistDkrAssetsFromCompose(outputs[name]['compose.yml'], assemblyBranch);
261+
Object.assign(outputs[name], composeFiles);
262262
const zipPath = path.join(__dirname, 'build', name +'.zip');
263-
await createZip(output[name], zipPath);
263+
await createZip(outputs[name], zipPath);
264264
baseDir = 'build/'+name
265-
await createSolutionDirectory(baseDir, output[name])
265+
await createSolutionDirectory(baseDir, outputs[name])
266266

267267
// Generate aggregated Confluence markup only if --publish or --docs is set
268268
if (shouldPublish || shouldDocs) {

0 commit comments

Comments
 (0)