The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/scopes/internal/utils.ts

177 lines
5.0 KiB

import { Scope, ScopeDashboardBinding } from '@grafana/data';
import { NodesMap, SelectedScope, SuggestedDashboardsFoldersMap, TreeScope } from './types';
export function getBasicScope(name: string): Scope {
return {
metadata: { name },
spec: {
filters: [],
title: name,
type: '',
category: '',
description: '',
},
};
}
export function mergeScopes(scope1: Scope, scope2: Scope): Scope {
return {
...scope1,
metadata: {
...scope1.metadata,
...scope2.metadata,
},
spec: {
...scope1.spec,
...scope2.spec,
},
};
}
export function getTreeScopesFromSelectedScopes(scopes: SelectedScope[]): TreeScope[] {
return scopes.map(({ scope, path }) => ({
scopeName: scope.metadata.name,
path,
}));
}
export function getScopesFromSelectedScopes(scopes: SelectedScope[]): Scope[] {
return scopes.map(({ scope }) => scope);
}
export function getScopeNamesFromSelectedScopes(scopes: SelectedScope[]): string[] {
return scopes.map(({ scope }) => scope.metadata.name);
}
// helper func to get the selected/tree scopes together with their paths
// needed to maintain selected scopes in tree for example when navigating
// between categories or when loading scopes from URL to find the scope's path
export function getScopesAndTreeScopesWithPaths(
selectedScopes: SelectedScope[],
treeScopes: TreeScope[],
path: string[],
childNodes: NodesMap
): [SelectedScope[], TreeScope[]] {
const childNodesArr = Object.values(childNodes);
// Get all scopes without paths
// We use tree scopes as the list is always up to date as opposed to selected scopes which can be outdated
const scopeNamesWithoutPaths = treeScopes.filter(({ path }) => path.length === 0).map(({ scopeName }) => scopeName);
// We search for the path of each scope name without a path
const scopeNamesWithPaths = scopeNamesWithoutPaths.reduce<Record<string, string[]>>((acc, scopeName) => {
const possibleParent = childNodesArr.find((childNode) => childNode.isSelectable && childNode.linkId === scopeName);
if (possibleParent) {
acc[scopeName] = [...path, possibleParent.name];
}
return acc;
}, {});
// Update the paths of the selected scopes based on what we found
const newSelectedScopes = selectedScopes.map((selectedScope) => {
if (selectedScope.path.length > 0) {
return selectedScope;
}
return {
...selectedScope,
path: scopeNamesWithPaths[selectedScope.scope.metadata.name] ?? [],
};
});
// Update the paths of the tree scopes based on what we found
const newTreeScopes = treeScopes.map((treeScope) => {
if (treeScope.path.length > 0) {
return treeScope;
}
return {
...treeScope,
path: scopeNamesWithPaths[treeScope.scopeName] ?? [],
};
});
return [newSelectedScopes, newTreeScopes];
}
export function groupDashboards(dashboards: ScopeDashboardBinding[]): SuggestedDashboardsFoldersMap {
return dashboards.reduce<SuggestedDashboardsFoldersMap>(
(acc, dashboard) => {
const rootNode = acc[''];
const groups = dashboard.status.groups ?? [];
groups.forEach((group) => {
if (group && !rootNode.folders[group]) {
rootNode.folders[group] = {
title: group,
isExpanded: false,
folders: {},
dashboards: {},
};
}
});
const targets =
groups.length > 0
? groups.map((group) => (group === '' ? rootNode.dashboards : rootNode.folders[group].dashboards))
: [rootNode.dashboards];
targets.forEach((target) => {
if (!target[dashboard.spec.dashboard]) {
target[dashboard.spec.dashboard] = {
dashboard: dashboard.spec.dashboard,
dashboardTitle: dashboard.status.dashboardTitle,
items: [],
};
}
target[dashboard.spec.dashboard].items.push(dashboard);
});
return acc;
},
{
'': {
title: '',
isExpanded: true,
folders: {},
dashboards: {},
},
}
);
}
export function filterFolders(folders: SuggestedDashboardsFoldersMap, query: string): SuggestedDashboardsFoldersMap {
query = (query ?? '').toLowerCase();
return Object.entries(folders).reduce<SuggestedDashboardsFoldersMap>((acc, [folderId, folder]) => {
// If folder matches the query, we show everything inside
if (folder.title.toLowerCase().includes(query)) {
acc[folderId] = {
...folder,
isExpanded: true,
};
return acc;
}
const filteredFolders = filterFolders(folder.folders, query);
const filteredDashboards = Object.entries(folder.dashboards).filter(([_, dashboard]) =>
dashboard.dashboardTitle.toLowerCase().includes(query)
);
if (Object.keys(filteredFolders).length > 0 || filteredDashboards.length > 0) {
acc[folderId] = {
...folder,
isExpanded: true,
folders: filteredFolders,
dashboards: Object.fromEntries(filteredDashboards),
};
}
return acc;
}, {});
}