@grafana/alerting
Version:
Grafana Alerting Library – Build vertical integrations on top of the industry-leading alerting solution
1 lines • 11.8 kB
Source Map (JSON)
{"version":3,"file":"utils.mjs","sources":["../../../../src/grafana/notificationPolicies/utils.ts"],"sourcesContent":["import { groupBy, isArray, pick, reduce, uniqueId } from 'lodash';\n\nimport { type RoutingTree, type RoutingTreeRoute } from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';\n\nimport { type Label } from '../matchers/types';\nimport { type LabelMatchDetails, matchLabels } from '../matchers/utils';\n\nimport { type Route, type RouteWithID } from './types';\n\nexport const INHERITABLE_KEYS = ['receiver', 'group_by', 'group_wait', 'group_interval', 'repeat_interval'] as const;\ntype InheritableKeys = typeof INHERITABLE_KEYS;\nexport type InheritableProperties = Pick<Route, InheritableKeys[number]>;\n\n// Represents matching information for a single route in the traversal path\nexport type RouteMatchInfo<T extends Route> = {\n route: T;\n matchDetails: LabelMatchDetails[];\n matched: boolean;\n};\n\nexport interface RouteMatchResult<T extends Route> {\n route: T;\n labels: Label[];\n // Track matching information for each route in the traversal path\n matchingJourney: Array<RouteMatchInfo<T>>;\n}\n\n/**\n * This function performs a depth-first left-to-right search through the route tree and returns the matching routing nodes.\n *\n * If the current node is not a match, return nothing\n * Normalization should have happened earlier in the code\n */\nexport function findMatchingRoutes<T extends Route>(\n route: T,\n labels: Label[],\n matchingJourney: Array<RouteMatchInfo<T>> = []\n): Array<RouteMatchResult<T>> {\n let childMatches: Array<RouteMatchResult<T>> = [];\n\n // Check if the current node matches\n const matchResult = matchLabels(route.matchers ?? [], labels);\n\n // Create matching info for this route\n const currentMatchInfo: RouteMatchInfo<T> = {\n route,\n matchDetails: matchResult.details,\n matched: matchResult.matches,\n };\n\n // Add current route's matching info to the journey\n const currentMatchingJourney = [...matchingJourney, currentMatchInfo];\n\n // If the current node is not a match, return nothing\n if (!matchResult.matches) {\n return [];\n }\n\n // If the current node matches, recurse through child nodes\n if (route.routes) {\n for (const child of route.routes) {\n const matchingChildren = findMatchingRoutes(child, labels, currentMatchingJourney);\n // TODO how do I solve this typescript thingy? It looks correct to me /shrug\n // @ts-ignore\n childMatches = childMatches.concat(matchingChildren);\n // we have matching children and we don't want to continue, so break here\n if (matchingChildren.length && !child.continue) {\n break;\n }\n }\n }\n\n // If no child nodes were matches, the current node itself is a match.\n if (childMatches.length === 0) {\n childMatches.push({\n route,\n labels,\n matchingJourney: currentMatchingJourney,\n });\n }\n\n return childMatches;\n}\n\n/**\n * This function will compute the full tree with inherited properties – this is mostly used for search and filtering\n */\nexport function computeInheritedTree<T extends Route>(parent: T): T {\n return {\n ...parent,\n routes: parent.routes?.map((child) => {\n const inheritedProperties = getInheritedProperties(parent, child);\n\n return computeInheritedTree({\n ...child,\n ...inheritedProperties,\n });\n }),\n };\n}\n\n// inherited properties are config properties that exist on the parent route (or its inherited properties) but not on the child route\nexport function getInheritedProperties<T extends Route>(\n parentRoute: T,\n childRoute: T,\n propertiesParentInherited?: InheritableProperties\n): InheritableProperties {\n const propsFromParent: InheritableProperties = pick(parentRoute, INHERITABLE_KEYS);\n const inheritableProperties: InheritableProperties = {\n ...propsFromParent,\n ...propertiesParentInherited,\n } as const;\n\n // @ts-ignore TS5 & TS6 we're using \"keyof\" for the property so the type checker can help us out but this makes the\n // reduce function signature unhappy\n const inherited = reduce(\n inheritableProperties,\n // @ts-ignore TSGO / TS7 see above comment about the reduce function signature.\n (inheritedProperties: InheritableProperties, parentValue, property: keyof InheritableProperties) => {\n const parentHasValue = parentValue != null;\n\n const inheritableValues = [undefined, '', null];\n const childIsInheriting = inheritableValues.some((value) => childRoute[property] === value);\n const inheritFromValue = childIsInheriting && parentHasValue;\n\n const inheritEmptyGroupByFromParent =\n property === 'group_by' &&\n parentHasValue &&\n isArray(childRoute[property]) &&\n childRoute[property]?.length === 0;\n\n const inheritFromParent = inheritFromValue || inheritEmptyGroupByFromParent;\n\n if (inheritFromParent) {\n // @ts-ignore\n inheritedProperties[property] = parentValue;\n }\n\n return inheritedProperties;\n },\n {}\n );\n\n return inherited;\n}\n\nexport function addUniqueIdentifier(route: Route): RouteWithID {\n return {\n id: uniqueId('route-'),\n ...route,\n routes: route.routes?.map(addUniqueIdentifier) ?? [],\n };\n}\n\n// all policies that were matched of a single tree\nexport type TreeMatch = {\n /* we'll include the entire expanded policy tree for diagnostics */\n expandedTree: RouteWithID;\n /* the routes that matched the labels where the key is a route and the value is an array of instances that match that route */\n matchedPolicies: Map<RouteWithID, Array<RouteMatchResult<RouteWithID>>>;\n};\n\n/**\n * This function will return what notification policies would match a set of labels.\n *\n * ⚠️ This function is rather CPU intensive depending on both the size of the labels list and the size of the notification policy tree.\n * When using this function, consider wrapping it in a web-worker to offload this from the main JavaScript thread.\n *\n * @param instances - A set of labels for which you want to determine the matching policies\n * @param routingTree - A notification policy tree (or subtree)\n */\nexport function matchInstancesToRoute(rootRoute: Route, instances: Label[][]): TreeMatch {\n // initially empty map of matches policies\n const matchedPolicies = new Map();\n\n // compute the entire expanded tree for matching routes and diagnostics\n // this will include inherited properties from parent nodes\n const expandedTree = addUniqueIdentifier(computeInheritedTree(rootRoute));\n\n // let's first find all matching routes for the provided instances\n const matchesArray = instances.flatMap((labels) => findMatchingRoutes(expandedTree, labels));\n\n // now group the matches by route ID\n // this will give us a map of route IDs to their matching instances\n // we use the route ID as the key to ensure uniqueness\n const groupedByRoute = groupBy(matchesArray, (match) => match.route.id);\n Object.entries(groupedByRoute).forEach(([_key, match]) => {\n matchedPolicies.set(match[0].route, match);\n });\n\n return {\n expandedTree,\n matchedPolicies,\n };\n}\n\n/**\n * Converts a RoutingTree to a Route by merging defaults with routes.\n *\n * @param routingTree - The RoutingTree from the API\n * @returns A Route that can be used with the matching functions\n */\nexport function convertRoutingTreeToRoute(routingTree: RoutingTree): Route {\n const convertRoutingTreeRoutes = (routes: RoutingTreeRoute[]): Route[] => {\n return routes.map(\n (route): Route => ({\n ...route,\n routes: route.routes ? convertRoutingTreeRoutes(route.routes) : [],\n })\n );\n };\n\n // Create the root route by merging defaults with the route structure\n const rootRoute: Route = {\n ...routingTree.spec.defaults,\n continue: false,\n active_time_intervals: [],\n mute_time_intervals: [],\n matchers: [], // Root route has no matchers (catch-all)\n routes: convertRoutingTreeRoutes(routingTree.spec.routes),\n };\n\n return rootRoute;\n}\n"],"names":[],"mappings":"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"}