{"version":3,"file":"utils.mjs","sources":["../../../../src/grafana/notificationPolicies/utils.ts"],"sourcesContent":["import { groupBy, isArray, pick, reduce, uniqueId } from 'lodash';\n\nimport { RoutingTree, RoutingTreeRoute } from '@grafana/api-clients/rtkq/notifications.alerting/v0alpha1';\n\nimport { Label } from '../matchers/types';\nimport { LabelMatchDetails, matchLabels } from '../matchers/utils';\n\nimport { Route, RouteWithID } from './types';\n\nexport const INHERITABLE_KEYS = ['receiver', 'group_by', 'group_wait', 'group_interval', 'repeat_interval'] as const;\nexport type 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-expect-error 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    (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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"}