{"version":3,"file":"add_label_to_query.cjs","sources":["../../src/add_label_to_query.ts"],"sourcesContent":["// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/add_label_to_query.ts\nimport { parser, VectorSelector } from '@prometheus-io/lezer-promql';\n\nimport { buildVisualQueryFromString } from './querybuilder/parsing';\nimport { renderQuery } from './querybuilder/shared/rendering/query';\nimport { type QueryBuilderLabelFilter } from './querybuilder/shared/types';\nimport { type PromVisualQuery } from './querybuilder/types';\n\n/**\n * Adds label filter to existing query. Useful for query modification for example for ad hoc filters.\n *\n * It uses PromQL parser to find instances of metric and labels, alters them and then splices them back into the query.\n * Ideally we could use the parse -> change -> render is a simple 3 steps but right now building the visual query\n * object does not support all possible queries.\n *\n * So instead this just operates on substrings of the query with labels and operates just on those. This makes this\n * more robust and can alter even invalid queries, and preserves in general the query structure and whitespace.\n * @param query\n * @param key\n * @param value\n * @param operator\n */\nexport function addLabelToQuery(query: string, key: string, value: string | number, operator = '='): string {\n  if (!key) {\n    throw new Error('Need label to add to query.');\n  }\n\n  const vectorSelectorPositions = getVectorSelectorPositions(query);\n  if (!vectorSelectorPositions.length) {\n    return query;\n  }\n\n  const filter = toLabelFilter(key, value, operator);\n  return addFilter(query, vectorSelectorPositions, filter);\n}\n\ntype VectorSelectorPosition = { from: number; to: number; query: PromVisualQuery };\n\n/**\n * Parse the string and get all VectorSelector positions in the query together with parsed representation of the vector\n * selector.\n * @param query\n */\nfunction getVectorSelectorPositions(query: string): VectorSelectorPosition[] {\n  const tree = parser.parse(query);\n  const positions: VectorSelectorPosition[] = [];\n  tree.iterate({\n    enter: ({ to, from, type }): false | void => {\n      if (type.id === VectorSelector) {\n        const visQuery = buildVisualQueryFromString(query.substring(from, to));\n        positions.push({ query: visQuery.query, from, to });\n        return false;\n      }\n    },\n  });\n  return positions;\n}\n\nfunction toLabelFilter(key: string, value: string | number, operator: string): QueryBuilderLabelFilter {\n  // We need to make sure that we convert the value back to string because it may be a number\n  const transformedValue = value === Infinity ? '+Inf' : value.toString();\n  return { label: key, op: operator, value: transformedValue };\n}\n\nfunction addFilter(\n  query: string,\n  vectorSelectorPositions: VectorSelectorPosition[],\n  filter: QueryBuilderLabelFilter\n): string {\n  let newQuery = '';\n  let prev = 0;\n\n  for (let i = 0; i < vectorSelectorPositions.length; i++) {\n    // This is basically just doing splice on a string for each matched vector selector.\n\n    const match = vectorSelectorPositions[i];\n    const isLast = i === vectorSelectorPositions.length - 1;\n\n    const start = query.substring(prev, match.from);\n    const end = isLast ? query.substring(match.to) : '';\n\n    const labelToMatch = labelExists(match.query.labels, filter);\n    if (labelToMatch) {\n      // if label exists, check the operator, if it is different, update it.\n      // We don't want to add duplicate labels.\n      if (labelToMatch.op !== filter.op) {\n        match.query.labels = match.query.labels.map((label) =>\n          label.label === filter.label && label.value === filter.value ? filter : label\n        );\n      }\n    } else {\n      // label does not exist, add as is.\n      match.query.labels.push(filter);\n    }\n    const newLabels = renderQuery(match.query);\n    newQuery += start + newLabels + end;\n    prev = match.to;\n  }\n  return newQuery;\n}\n\n/**\n * Check if label exists in the list of labels but ignore the operator.\n * @param labels\n * @param filter\n */\nfunction labelExists(labels: QueryBuilderLabelFilter[], filter: QueryBuilderLabelFilter) {\n  return labels.find((label) => label.label === filter.label && label.value === filter.value);\n}\n"],"names":["parser","VectorSelector","buildVisualQueryFromString","query","renderQuery"],"mappings":";;;;;;;;;AAsBO,SAAS,eAAA,CAAgB,KAAA,EAAe,GAAA,EAAa,KAAA,EAAwB,WAAW,GAAA,EAAa;AAC1G,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,MAAM,6BAA6B,CAAA;AAAA,EAC/C;AAEA,EAAA,MAAM,uBAAA,GAA0B,2BAA2B,KAAK,CAAA;AAChE,EAAA,IAAI,CAAC,wBAAwB,MAAA,EAAQ;AACnC,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,MAAA,GAAS,aAAA,CAAc,GAAA,EAAK,KAAA,EAAO,QAAQ,CAAA;AACjD,EAAA,OAAO,SAAA,CAAU,KAAA,EAAO,uBAAA,EAAyB,MAAM,CAAA;AACzD;AASA,SAAS,2BAA2B,KAAA,EAAyC;AAC3E,EAAA,MAAM,IAAA,GAAOA,kBAAA,CAAO,KAAA,CAAM,KAAK,CAAA;AAC/B,EAAA,MAAM,YAAsC,EAAC;AAC7C,EAAA,IAAA,CAAK,OAAA,CAAQ;AAAA,IACX,OAAO,CAAC,EAAE,EAAA,EAAI,IAAA,EAAM,MAAK,KAAoB;AAC3C,MAAA,IAAI,IAAA,CAAK,OAAOC,0BAAA,EAAgB;AAC9B,QAAA,MAAM,WAAWC,kCAAA,CAA2B,KAAA,CAAM,SAAA,CAAU,IAAA,EAAM,EAAE,CAAC,CAAA;AACrE,QAAA,SAAA,CAAU,KAAK,EAAE,KAAA,EAAO,SAAS,KAAA,EAAO,IAAA,EAAM,IAAI,CAAA;AAClD,QAAA,OAAO,KAAA;AAAA,MACT;AAAA,IACF;AAAA,GACD,CAAA;AACD,EAAA,OAAO,SAAA;AACT;AAEA,SAAS,aAAA,CAAc,GAAA,EAAa,KAAA,EAAwB,QAAA,EAA2C;AAErG,EAAA,MAAM,gBAAA,GAAmB,KAAA,KAAU,QAAA,GAAW,MAAA,GAAS,MAAM,QAAA,EAAS;AACtE,EAAA,OAAO,EAAE,KAAA,EAAO,GAAA,EAAK,EAAA,EAAI,QAAA,EAAU,OAAO,gBAAA,EAAiB;AAC7D;AAEA,SAAS,SAAA,CACPC,OAAA,EACA,uBAAA,EACA,MAAA,EACQ;AACR,EAAA,IAAI,QAAA,GAAW,EAAA;AACf,EAAA,IAAI,IAAA,GAAO,CAAA;AAEX,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,uBAAA,CAAwB,QAAQ,CAAA,EAAA,EAAK;AAGvD,IAAA,MAAM,KAAA,GAAQ,wBAAwB,CAAC,CAAA;AACvC,IAAA,MAAM,MAAA,GAAS,CAAA,KAAM,uBAAA,CAAwB,MAAA,GAAS,CAAA;AAEtD,IAAA,MAAM,KAAA,GAAQA,OAAA,CAAM,SAAA,CAAU,IAAA,EAAM,MAAM,IAAI,CAAA;AAC9C,IAAA,MAAM,MAAM,MAAA,GAASA,OAAA,CAAM,SAAA,CAAU,KAAA,CAAM,EAAE,CAAA,GAAI,EAAA;AAEjD,IAAA,MAAM,YAAA,GAAe,WAAA,CAAY,KAAA,CAAM,KAAA,CAAM,QAAQ,MAAM,CAAA;AAC3D,IAAA,IAAI,YAAA,EAAc;AAGhB,MAAA,IAAI,YAAA,CAAa,EAAA,KAAO,MAAA,CAAO,EAAA,EAAI;AACjC,QAAA,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,KAAA,CAAM,KAAA,CAAM,MAAA,CAAO,GAAA;AAAA,UAAI,CAAC,KAAA,KAC3C,KAAA,CAAM,KAAA,KAAU,MAAA,CAAO,SAAS,KAAA,CAAM,KAAA,KAAU,MAAA,CAAO,KAAA,GAAQ,MAAA,GAAS;AAAA,SAC1E;AAAA,MACF;AAAA,IACF,CAAA,MAAO;AAEL,MAAA,KAAA,CAAM,KAAA,CAAM,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA;AAAA,IAChC;AACA,IAAA,MAAM,SAAA,GAAYC,iBAAA,CAAY,KAAA,CAAM,KAAK,CAAA;AACzC,IAAA,QAAA,IAAY,QAAQ,SAAA,GAAY,GAAA;AAChC,IAAA,IAAA,GAAO,KAAA,CAAM,EAAA;AAAA,EACf;AACA,EAAA,OAAO,QAAA;AACT;AAOA,SAAS,WAAA,CAAY,QAAmC,MAAA,EAAiC;AACvF,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,CAAC,KAAA,KAAU,KAAA,CAAM,KAAA,KAAU,MAAA,CAAO,KAAA,IAAS,KAAA,CAAM,KAAA,KAAU,MAAA,CAAO,KAAK,CAAA;AAC5F;;;;"}