From 4c91dfa71b41887e3743e3c1fdf19ff7201219ec Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 10 Mar 2025 09:45:50 -0700 Subject: [PATCH] Enhancement: support automatic service discovery services with layout-only nesting (#4900) --- src/utils/config/api-response.js | 55 +++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/utils/config/api-response.js b/src/utils/config/api-response.js index 3309eeed..03f155fb 100644 --- a/src/utils/config/api-response.js +++ b/src/utils/config/api-response.js @@ -85,6 +85,18 @@ export async function widgetsResponse() { return configuredWidgets; } +function convertLayoutGroupToGroup(name, layoutGroup) { + const group = { name, services: [], groups: [] }; + if (layoutGroup) { + Object.entries(layoutGroup).forEach(([key, value]) => { + if (typeof value === "object") { + group.groups.push(convertLayoutGroupToGroup(key, value)); + } + }); + } + return group; +} + function mergeSubgroups(configuredGroups, mergedGroup) { configuredGroups.forEach((group) => { if (group.name === mergedGroup.name) { @@ -96,6 +108,33 @@ function mergeSubgroups(configuredGroups, mergedGroup) { }); } +function ensureParentGroupExists(sortedGroups, configuredGroups, group, definedLayouts) { + // make sure the top level parent group exists in the sortedGroups array + const parentGroupName = group.parent; + const parentGroup = findGroupByName(configuredGroups, parentGroupName); + if (parentGroup && parentGroup.parent) { + ensureParentGroupExists(sortedGroups, configuredGroups, parentGroup); + } else { + const parentGroupIndex = definedLayouts.findIndex((layout) => layout === parentGroupName); + if (parentGroupIndex > -1) { + sortedGroups[parentGroupIndex] = parentGroup; + } + } +} + +function pruneEmptyGroups(groups) { + // remove any groups that have no services + return groups.filter((group) => { + if (group.services.length === 0 && group.groups.length === 0) { + return false; + } + if (group.groups.length > 0) { + group.groups = pruneEmptyGroups(group.groups); + } + return true; + }); +} + export async function servicesResponse() { let discoveredDockerServices; let discoveredKubernetesServices; @@ -150,6 +189,17 @@ export async function servicesResponse() { const sortedGroups = []; const unsortedGroups = []; const definedLayouts = initialSettings.layout ? Object.keys(initialSettings.layout) : null; + if (definedLayouts) { + // this handles cases where groups are only defined in the settings.yaml layout and not in the services.yaml + const layoutConfiguredGroups = Object.entries(initialSettings.layout).map(([key, value]) => + convertLayoutGroupToGroup(key, value), + ); + layoutConfiguredGroups.forEach((group) => { + if (!configuredServices.find((serviceGroup) => serviceGroup.name === group.name)) { + configuredServices.push(group); + } + }); + } mergedGroupsNames.forEach((groupName) => { const discoveredDockerGroup = findGroupByName(discoveredDockerServices, groupName) || { @@ -174,6 +224,8 @@ export async function servicesResponse() { else if (configuredGroup.parent) { // this is a nested group, so find the parent group and merge the services mergeSubgroups(configuredServices, mergedGroup); + // make sure the top level parent group exists in the sortedGroups array + ensureParentGroupExists(sortedGroups, configuredServices, configuredGroup, definedLayouts); } else unsortedGroups.push(mergedGroup); } else if (configuredGroup.parent) { // this is a nested group, so find the parent group and merge the services @@ -183,5 +235,6 @@ export async function servicesResponse() { } }); - return [...sortedGroups.filter((g) => g), ...unsortedGroups]; + const allGroups = [...sortedGroups.filter((g) => g), ...unsortedGroups]; + return pruneEmptyGroups(allGroups); }