= {};\n\n for (const series of data) {\n if (series?.meta?.custom?.seriesMetaList) {\n for (const metaItem of series.meta.custom.seriesMetaList as MetricTankSeriesMeta[]) {\n // key is to dedupe as many series will have identitical meta\n const key = `${JSON.stringify(metaItem)}`;\n\n if (seriesMetas[key]) {\n seriesMetas[key].count += metaItem.count;\n } else {\n seriesMetas[key] = metaItem;\n }\n }\n }\n }\n\n if (Object.keys(seriesMetas).length === 0) {\n return No response meta data
;\n }\n\n return (\n \n
Metrictank Lineage
\n {Object.keys(seriesMetas).map((key) => this.renderMeta(seriesMetas[key], key))}\n \n );\n }\n}\n\nconst getStyles = stylesFactory(() => {\n const { theme } = config;\n const borderColor = theme.isDark ? theme.palette.gray25 : theme.palette.gray85;\n const background = theme.isDark ? theme.palette.dark1 : theme.palette.white;\n const headerBg = theme.isDark ? theme.palette.gray15 : theme.palette.gray85;\n\n return {\n metaItem: css`\n background: ${background};\n border: 1px solid ${borderColor};\n margin-bottom: ${theme.spacing.md};\n `,\n metaItemHeader: css`\n background: ${headerBg};\n padding: ${theme.spacing.xs} ${theme.spacing.md};\n font-size: ${theme.typography.size.md};\n display: flex;\n justify-content: space-between;\n `,\n metaItemBody: css`\n padding: ${theme.spacing.md};\n `,\n stepHeading: css`\n font-size: ${theme.typography.size.md};\n `,\n stepDescription: css`\n font-size: ${theme.typography.size.sm};\n color: ${theme.colors.textWeak};\n margin-bottom: ${theme.spacing.sm};\n `,\n step: css`\n margin-bottom: ${theme.spacing.lg};\n\n &:last-child {\n margin-bottom: 0;\n }\n `,\n bucket: css`\n display: flex;\n margin-bottom: ${theme.spacing.sm};\n border-radius: ${theme.border.radius.sm};\n `,\n bucketInterval: css`\n flex-grow: 0;\n width: 60px;\n `,\n bucketRetention: css`\n background: linear-gradient(0deg, ${theme.palette.blue85}, ${theme.palette.blue95});\n text-align: center;\n color: ${theme.palette.white};\n margin-right: ${theme.spacing.md};\n border-radius: ${theme.border.radius.sm};\n `,\n bucketRetentionActive: css`\n background: linear-gradient(0deg, ${theme.palette.greenBase}, ${theme.palette.greenShade});\n `,\n };\n});\n","import { last } from 'lodash';\n\nexport const GRAPHITE_VERSIONS = ['0.9', '1.0', '1.1'];\n\nexport const DEFAULT_GRAPHITE_VERSION = last(GRAPHITE_VERSIONS)!;\n","import React from 'react';\n\nimport { Alert } from '@grafana/ui';\n\ntype Props = {\n onDismiss: () => void;\n};\n\nexport default function MappingsHelp(props: Props): JSX.Element {\n return (\n \n Mappings are currently supported only between Graphite and Loki queries.
\n \n When you switch your data source from Graphite to Loki, your queries are mapped according to the mappings\n defined in the example below. To define a mapping, write the full path of the metric and replace nodes you want\n to map to label with the label name in parentheses. The value of the label is extracted from your Graphite query\n when you switch data sources.\n
\n \n All tags are automatically mapped to labels regardless of the mapping configuration. Graphite matching patterns\n (using {}) are converted to Loki's regular expressions matching patterns. When you use functions\n in your queries, the metrics, and tags are extracted to match them with defined mappings.\n
\n \n Example: for a mapping = servers.(cluster).(server).*
:\n
\n \n \n \n Graphite query | \n Mapped to Loki query | \n
\n \n \n \n \n \n alias(servers.west.001.cpu,1,2)\n \n | \n \n \n {cluster="west", server="001"}\n \n | \n
\n \n \n \n alias(servers.*.{001,002}.*,1,2)\n \n | \n \n \n {server=~"(001|002)"}\n \n | \n
\n \n \n interpolate(seriesByTag('foo=bar', 'server=002'), inf)) \n | \n \n {foo="bar", server="002"} \n | \n
\n \n
\n \n );\n}\n","import React, { ChangeEvent, useState } from 'react';\n\nimport { Button, Icon, InlineField, InlineFieldRow, Input } from '@grafana/ui';\n\nimport MappingsHelp from './MappingsHelp';\n\ntype Props = {\n mappings: string[];\n onChange: (mappings: string[]) => void;\n onDismiss: () => void;\n onRestoreHelp: () => void;\n showHelp: boolean;\n};\n\nexport const MappingsConfiguration = (props: Props): JSX.Element => {\n const [mappings, setMappings] = useState(props.mappings || []);\n\n return (\n \n
Label mappings
\n {!props.showHelp && (\n
\n \n
\n )}\n {props.showHelp &&
}\n\n
\n {mappings.map((mapping, i) => (\n \n \n ) => {\n let newMappings = mappings.concat();\n newMappings[i] = changeEvent.target.value;\n setMappings(newMappings);\n }}\n onBlur={() => {\n props.onChange(mappings);\n }}\n placeholder=\"e.g. test.metric.(labelName).*\"\n value={mapping}\n />\n \n \n \n ))}\n \n
\n
\n );\n};\n","import { GraphiteLokiMapping } from '../types';\n\n/**\n * Converts a simple string used in LokiLogsMappings component (e.g. \"servers.(name).*\")\n * to data model saved in data source configuration.\n */\nexport function fromString(text: string): GraphiteLokiMapping {\n return {\n matchers: text.split('.').map((metricNode) => {\n if (metricNode.startsWith('(') && metricNode.endsWith(')')) {\n return {\n value: '*',\n labelName: metricNode.slice(1, -1),\n };\n } else {\n return { value: metricNode };\n }\n }),\n };\n}\n\n/**\n * Coverts configuration stored in data source configuration into a string displayed in LokiLogsMappings component.\n */\nexport function toString(mapping: GraphiteLokiMapping): string {\n return mapping.matchers\n .map((matcher) => {\n return matcher.labelName ? `(${matcher.labelName})` : `${matcher.value}`;\n })\n .join('.');\n}\n","import React, { PureComponent } from 'react';\n\nimport {\n DataSourcePluginOptionsEditorProps,\n updateDatasourcePluginJsonDataOption,\n onUpdateDatasourceJsonDataOptionSelect,\n onUpdateDatasourceJsonDataOptionChecked,\n} from '@grafana/data';\nimport { Alert, DataSourceHttpSettings, Field, FieldSet, Select, Switch } from '@grafana/ui';\nimport { config } from 'app/core/config';\nimport store from 'app/core/store';\n\nimport { GraphiteOptions, GraphiteType } from '../types';\nimport { DEFAULT_GRAPHITE_VERSION, GRAPHITE_VERSIONS } from '../versions';\n\nimport { MappingsConfiguration } from './MappingsConfiguration';\nimport { fromString, toString } from './parseLokiLabelMappings';\n\nexport const SHOW_MAPPINGS_HELP_KEY = 'grafana.datasources.graphite.config.showMappingsHelp';\n\nconst graphiteVersions = GRAPHITE_VERSIONS.map((version) => ({ label: `${version}.x`, value: version }));\n\nconst graphiteTypes = Object.entries(GraphiteType).map(([label, value]) => ({\n label,\n value,\n}));\n\nexport type Props = DataSourcePluginOptionsEditorProps;\n\ntype State = {\n showMappingsHelp: boolean;\n};\n\nexport class ConfigEditor extends PureComponent {\n constructor(props: Props) {\n super(props);\n this.state = {\n showMappingsHelp: store.getObject(SHOW_MAPPINGS_HELP_KEY, true),\n };\n }\n\n componentDidMount() {\n updateDatasourcePluginJsonDataOption(this.props, 'graphiteVersion', this.currentGraphiteVersion);\n }\n\n render() {\n const { options, onOptionsChange } = this.props;\n\n const currentVersion = graphiteVersions.find((item) => item.value === this.currentGraphiteVersion);\n\n return (\n <>\n {options.access === 'direct' && (\n \n This data source uses browser access mode. This mode is deprecated and will be removed in the future. Please\n use server access mode instead.\n \n )}\n \n \n {\n this.setState({ showMappingsHelp: false });\n store.setObject(SHOW_MAPPINGS_HELP_KEY, false);\n }}\n onRestoreHelp={() => {\n this.setState({ showMappingsHelp: true });\n store.setObject(SHOW_MAPPINGS_HELP_KEY, true);\n }}\n onChange={(mappings) => {\n onOptionsChange({\n ...options,\n jsonData: {\n ...options.jsonData,\n importConfiguration: {\n ...options.jsonData.importConfiguration,\n loki: {\n mappings: mappings.map(fromString),\n },\n },\n },\n });\n }}\n />\n >\n );\n }\n\n private get currentGraphiteVersion() {\n return this.props.options.jsonData.graphiteVersion || DEFAULT_GRAPHITE_VERSION;\n }\n}\n","import { isNumber } from 'lodash';\n\nconst versionPattern = /^(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?(?:-([0-9A-Za-z\\.]+))?/;\n\nexport class SemVersion {\n major: number;\n minor: number;\n patch: number;\n meta: string;\n\n constructor(version: string) {\n this.major = 0;\n this.minor = 0;\n this.patch = 0;\n this.meta = '';\n const match = versionPattern.exec(version);\n if (match) {\n this.major = Number(match[1]);\n this.minor = Number(match[2] || 0);\n this.patch = Number(match[3] || 0);\n this.meta = match[4];\n }\n }\n\n isGtOrEq(version: string): boolean {\n const compared = new SemVersion(version);\n\n for (let i = 0; i < this.comparable.length; ++i) {\n if (this.comparable[i] > compared.comparable[i]) {\n return true;\n }\n if (this.comparable[i] < compared.comparable[i]) {\n return false;\n }\n }\n return true;\n }\n\n isValid(): boolean {\n return isNumber(this.major);\n }\n\n get comparable() {\n return [this.major, this.minor, this.patch];\n }\n}\n\nexport function isVersionGtOrEq(a: string, b: string): boolean {\n const aSemver = new SemVersion(a);\n return aSemver.isGtOrEq(b);\n}\n","import React, { useState } from 'react';\n\nimport { QueryEditorProps } from '@grafana/data';\nimport { InlineFormLabel, Input, TagsInput } from '@grafana/ui';\n\nimport { GraphiteDatasource } from '../datasource';\nimport { GraphiteQuery, GraphiteOptions } from '../types';\n\nexport const AnnotationEditor = (props: QueryEditorProps) => {\n const { query, onChange } = props;\n const [target, setTarget] = useState(query.target ?? '');\n const [tags, setTags] = useState(query.tags ?? []);\n const updateValue = (key: K, val: V) => {\n if (key === 'tags') {\n onChange({\n ...query,\n [key]: val,\n fromAnnotations: true,\n queryType: key,\n });\n } else {\n onChange({\n ...query,\n [key]: val,\n fromAnnotations: true,\n textEditor: true,\n });\n }\n };\n\n const onTagsChange = (tagsInput: string[]) => {\n setTags(tagsInput);\n updateValue('tags', tagsInput);\n };\n\n return (\n \n
\n Graphite Query\n setTarget(e.currentTarget.value || '')}\n onBlur={() => updateValue('target', target)}\n placeholder=\"Example: statsd.application.counters.*.count\"\n />\n
\n\n
Or
\n\n
\n Graphite events tags\n \n
\n
\n );\n};\n","import { assign, each, filter, forEach, get, includes, isString, last, map, toString, isFinite } from 'lodash';\n\nimport { InterpolateFunction } from '@grafana/data';\nimport { isVersionGtOrEq } from 'app/core/utils/version';\n\nexport type ParamDef = {\n name: string;\n type: string;\n options?: Array;\n multiple?: boolean;\n optional?: boolean;\n version?: string;\n};\n\nexport interface FuncDef {\n name: string;\n params: ParamDef[];\n defaultParams: Array;\n category?: string;\n shortName?: any;\n fake?: boolean;\n version?: string;\n description?: string;\n /**\n * True if the function was not found on the list of available function descriptions.\n */\n unknown?: boolean;\n}\n\nexport type FuncDefs = {\n [functionName in string]: FuncDef;\n};\n\nconst index: FuncDefs = {};\n\nfunction addFuncDef(funcDef: Partial & { name: string; category: string }) {\n funcDef.params = funcDef.params || [];\n funcDef.defaultParams = funcDef.defaultParams || [];\n\n index[funcDef.name] = funcDef as FuncDef;\n if (funcDef.shortName) {\n index[funcDef.shortName] = funcDef as FuncDef;\n }\n}\n\nconst optionalSeriesRefArgs = [{ name: 'other', type: 'value_or_series', optional: true, multiple: true }];\n\naddFuncDef({\n name: 'scaleToSeconds',\n category: 'Transform',\n params: [{ name: 'seconds', type: 'int' }],\n defaultParams: [1],\n});\n\naddFuncDef({\n name: 'perSecond',\n category: 'Transform',\n params: [{ name: 'max value', type: 'int', optional: true }],\n defaultParams: [],\n});\n\naddFuncDef({\n name: 'holtWintersForecast',\n category: 'Calculate',\n});\n\naddFuncDef({\n name: 'holtWintersConfidenceBands',\n category: 'Calculate',\n params: [{ name: 'delta', type: 'int' }],\n defaultParams: [3],\n});\n\naddFuncDef({\n name: 'holtWintersAberration',\n category: 'Calculate',\n params: [{ name: 'delta', type: 'int' }],\n defaultParams: [3],\n});\n\naddFuncDef({\n name: 'nPercentile',\n category: 'Calculate',\n params: [{ name: 'Nth percentile', type: 'int' }],\n defaultParams: [95],\n});\n\naddFuncDef({\n name: 'diffSeries',\n params: optionalSeriesRefArgs,\n defaultParams: ['#A'],\n category: 'Combine',\n});\n\naddFuncDef({\n name: 'stddevSeries',\n params: optionalSeriesRefArgs,\n defaultParams: [''],\n category: 'Combine',\n});\n\naddFuncDef({\n name: 'divideSeries',\n params: optionalSeriesRefArgs,\n defaultParams: ['#A'],\n category: 'Combine',\n});\n\naddFuncDef({\n name: 'multiplySeries',\n params: optionalSeriesRefArgs,\n defaultParams: ['#A'],\n category: 'Combine',\n});\n\naddFuncDef({\n name: 'asPercent',\n params: optionalSeriesRefArgs,\n defaultParams: ['#A'],\n category: 'Combine',\n});\n\naddFuncDef({\n name: 'group',\n params: optionalSeriesRefArgs,\n defaultParams: ['#A', '#B'],\n category: 'Combine',\n});\n\naddFuncDef({\n name: 'sumSeries',\n shortName: 'sum',\n category: 'Combine',\n params: optionalSeriesRefArgs,\n defaultParams: [''],\n});\n\naddFuncDef({\n name: 'averageSeries',\n shortName: 'avg',\n category: 'Combine',\n params: optionalSeriesRefArgs,\n defaultParams: [''],\n});\n\naddFuncDef({\n name: 'rangeOfSeries',\n category: 'Combine',\n});\n\naddFuncDef({\n name: 'percentileOfSeries',\n category: 'Combine',\n params: [\n { name: 'n', type: 'int' },\n { name: 'interpolate', type: 'boolean', options: ['true', 'false'] },\n ],\n defaultParams: [95, 'false'],\n});\n\naddFuncDef({\n name: 'sumSeriesWithWildcards',\n category: 'Combine',\n params: [{ name: 'node', type: 'int', multiple: true }],\n defaultParams: [3],\n});\n\naddFuncDef({\n name: 'maxSeries',\n shortName: 'max',\n category: 'Combine',\n});\n\naddFuncDef({\n name: 'minSeries',\n shortName: 'min',\n category: 'Combine',\n});\n\naddFuncDef({\n name: 'averageSeriesWithWildcards',\n category: 'Combine',\n params: [{ name: 'node', type: 'int', multiple: true }],\n defaultParams: [3],\n});\n\naddFuncDef({\n name: 'alias',\n category: 'Alias',\n params: [{ name: 'alias', type: 'string' }],\n defaultParams: ['alias'],\n});\n\naddFuncDef({\n name: 'aliasSub',\n category: 'Alias',\n params: [\n { name: 'search', type: 'string' },\n { name: 'replace', type: 'string' },\n ],\n defaultParams: ['', '\\\\1'],\n});\n\naddFuncDef({\n name: 'consolidateBy',\n category: 'Special',\n params: [\n {\n name: 'function',\n type: 'string',\n options: ['sum', 'average', 'min', 'max'],\n },\n ],\n defaultParams: ['max'],\n});\n\naddFuncDef({\n name: 'cumulative',\n category: 'Special',\n params: [],\n defaultParams: [],\n});\n\naddFuncDef({\n name: 'groupByNode',\n category: 'Combine',\n params: [\n {\n name: 'node',\n type: 'int',\n options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12],\n },\n {\n name: 'function',\n type: 'string',\n options: ['sum', 'avg', 'maxSeries'],\n },\n ],\n defaultParams: [3, 'sum'],\n});\n\naddFuncDef({\n name: 'aliasByNode',\n category: 'Alias',\n params: [\n {\n name: 'node',\n type: 'int',\n options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12],\n multiple: true,\n },\n ],\n defaultParams: [3],\n});\n\naddFuncDef({\n name: 'substr',\n category: 'Special',\n params: [\n {\n name: 'start',\n type: 'int',\n options: [-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12],\n },\n {\n name: 'stop',\n type: 'int',\n options: [-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12],\n },\n ],\n defaultParams: [0, 0],\n});\n\naddFuncDef({\n name: 'sortByName',\n category: 'Sorting',\n params: [\n {\n name: 'natural',\n type: 'boolean',\n options: ['true', 'false'],\n optional: true,\n },\n ],\n defaultParams: ['false'],\n});\n\naddFuncDef({\n name: 'sortByMaxima',\n category: 'Sorting',\n});\n\naddFuncDef({\n name: 'sortByMinima',\n category: 'Sorting',\n});\n\naddFuncDef({\n name: 'sortByTotal',\n category: 'Sorting',\n});\n\naddFuncDef({\n name: 'aliasByMetric',\n category: 'Alias',\n});\n\naddFuncDef({\n name: 'randomWalk',\n fake: true,\n category: 'Special',\n params: [{ name: 'name', type: 'string' }],\n defaultParams: ['randomWalk'],\n});\n\naddFuncDef({\n name: 'countSeries',\n category: 'Combine',\n});\n\naddFuncDef({\n name: 'constantLine',\n category: 'Special',\n params: [{ name: 'value', type: 'int' }],\n defaultParams: [10],\n});\n\naddFuncDef({\n name: 'cactiStyle',\n category: 'Special',\n});\n\naddFuncDef({\n name: 'keepLastValue',\n category: 'Transform',\n params: [{ name: 'n', type: 'int' }],\n defaultParams: [100],\n});\n\naddFuncDef({\n name: 'changed',\n category: 'Special',\n params: [],\n defaultParams: [],\n});\n\naddFuncDef({\n name: 'scale',\n category: 'Transform',\n params: [{ name: 'factor', type: 'int' }],\n defaultParams: [1],\n});\n\naddFuncDef({\n name: 'offset',\n category: 'Transform',\n params: [{ name: 'amount', type: 'int' }],\n defaultParams: [10],\n});\n\naddFuncDef({\n name: 'transformNull',\n category: 'Transform',\n params: [{ name: 'amount', type: 'int' }],\n defaultParams: [0],\n});\n\naddFuncDef({\n name: 'integral',\n category: 'Transform',\n});\n\naddFuncDef({\n name: 'derivative',\n category: 'Transform',\n});\n\naddFuncDef({\n name: 'nonNegativeDerivative',\n category: 'Transform',\n params: [{ name: 'max value or 0', type: 'int', optional: true }],\n defaultParams: [''],\n});\n\naddFuncDef({\n name: 'timeShift',\n category: 'Transform',\n params: [\n {\n name: 'amount',\n type: 'select',\n options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'],\n },\n ],\n defaultParams: ['1d'],\n});\n\naddFuncDef({\n name: 'timeStack',\n category: 'Transform',\n params: [\n {\n name: 'timeShiftUnit',\n type: 'select',\n options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'],\n },\n { name: 'timeShiftStart', type: 'int' },\n { name: 'timeShiftEnd', type: 'int' },\n ],\n defaultParams: ['1d', 0, 7],\n});\n\naddFuncDef({\n name: 'summarize',\n category: 'Transform',\n params: [\n { name: 'interval', type: 'string' },\n {\n name: 'func',\n type: 'select',\n options: ['sum', 'avg', 'min', 'max', 'last'],\n },\n {\n name: 'alignToFrom',\n type: 'boolean',\n optional: true,\n options: ['false', 'true'],\n },\n ],\n defaultParams: ['1h', 'sum', 'false'],\n});\n\naddFuncDef({\n name: 'smartSummarize',\n category: 'Transform',\n params: [\n { name: 'interval', type: 'string' },\n {\n name: 'func',\n type: 'select',\n options: ['sum', 'avg', 'min', 'max', 'last'],\n },\n ],\n defaultParams: ['1h', 'sum'],\n});\n\naddFuncDef({\n name: 'absolute',\n category: 'Transform',\n});\n\naddFuncDef({\n name: 'hitcount',\n category: 'Transform',\n params: [{ name: 'interval', type: 'string' }],\n defaultParams: ['10s'],\n});\n\naddFuncDef({\n name: 'log',\n category: 'Transform',\n params: [{ name: 'base', type: 'int' }],\n defaultParams: ['10'],\n});\n\naddFuncDef({\n name: 'averageAbove',\n category: 'Filter Series',\n params: [{ name: 'n', type: 'int' }],\n defaultParams: [25],\n});\n\naddFuncDef({\n name: 'averageBelow',\n category: 'Filter Series',\n params: [{ name: 'n', type: 'int' }],\n defaultParams: [25],\n});\n\naddFuncDef({\n name: 'currentAbove',\n category: 'Filter Series',\n params: [{ name: 'n', type: 'int' }],\n defaultParams: [25],\n});\n\naddFuncDef({\n name: 'currentBelow',\n category: 'Filter Series',\n params: [{ name: 'n', type: 'int' }],\n defaultParams: [25],\n});\n\naddFuncDef({\n name: 'maximumAbove',\n category: 'Filter Series',\n params: [{ name: 'value', type: 'int' }],\n defaultParams: [0],\n});\n\naddFuncDef({\n name: 'maximumBelow',\n category: 'Filter Series',\n params: [{ name: 'value', type: 'int' }],\n defaultParams: [0],\n});\n\naddFuncDef({\n name: 'minimumAbove',\n category: 'Filter Series',\n params: [{ name: 'value', type: 'int' }],\n defaultParams: [0],\n});\n\naddFuncDef({\n name: 'minimumBelow',\n category: 'Filter Series',\n params: [{ name: 'value', type: 'int' }],\n defaultParams: [0],\n});\n\naddFuncDef({\n name: 'limit',\n category: 'Filter Series',\n params: [{ name: 'n', type: 'int' }],\n defaultParams: [5],\n});\n\naddFuncDef({\n name: 'mostDeviant',\n category: 'Filter Series',\n params: [{ name: 'n', type: 'int' }],\n defaultParams: [10],\n});\n\naddFuncDef({\n name: 'exclude',\n category: 'Filter Series',\n params: [{ name: 'exclude', type: 'string' }],\n defaultParams: ['exclude'],\n});\n\naddFuncDef({\n name: 'highestCurrent',\n category: 'Filter Series',\n params: [{ name: 'count', type: 'int' }],\n defaultParams: [5],\n});\n\naddFuncDef({\n name: 'highestMax',\n category: 'Filter Series',\n params: [{ name: 'count', type: 'int' }],\n defaultParams: [5],\n});\n\naddFuncDef({\n name: 'lowestCurrent',\n category: 'Filter Series',\n params: [{ name: 'count', type: 'int' }],\n defaultParams: [5],\n});\n\naddFuncDef({\n name: 'movingAverage',\n category: 'Calculate',\n params: [\n {\n name: 'windowSize',\n type: 'int_or_interval',\n options: ['5', '7', '10', '5min', '10min', '30min', '1hour'],\n },\n ],\n defaultParams: [10],\n});\n\naddFuncDef({\n name: 'movingMedian',\n category: 'Calculate',\n params: [\n {\n name: 'windowSize',\n type: 'int_or_interval',\n options: ['5', '7', '10', '5min', '10min', '30min', '1hour'],\n },\n ],\n defaultParams: ['5'],\n});\n\naddFuncDef({\n name: 'stdev',\n category: 'Calculate',\n params: [\n { name: 'n', type: 'int' },\n { name: 'tolerance', type: 'int' },\n ],\n defaultParams: [5, 0.1],\n});\n\naddFuncDef({\n name: 'highestAverage',\n category: 'Filter Series',\n params: [{ name: 'count', type: 'int' }],\n defaultParams: [5],\n});\n\naddFuncDef({\n name: 'lowestAverage',\n category: 'Filter Series',\n params: [{ name: 'count', type: 'int' }],\n defaultParams: [5],\n});\n\naddFuncDef({\n name: 'removeAbovePercentile',\n category: 'Filter Data',\n params: [{ name: 'n', type: 'int' }],\n defaultParams: [5],\n});\n\naddFuncDef({\n name: 'removeAboveValue',\n category: 'Filter Data',\n params: [{ name: 'n', type: 'int' }],\n defaultParams: [5],\n});\n\naddFuncDef({\n name: 'removeBelowPercentile',\n category: 'Filter Data',\n params: [{ name: 'n', type: 'int' }],\n defaultParams: [5],\n});\n\naddFuncDef({\n name: 'removeBelowValue',\n category: 'Filter Data',\n params: [{ name: 'n', type: 'int' }],\n defaultParams: [5],\n});\n\naddFuncDef({\n name: 'useSeriesAbove',\n category: 'Filter Series',\n params: [\n { name: 'value', type: 'int' },\n { name: 'search', type: 'string' },\n { name: 'replace', type: 'string' },\n ],\n defaultParams: [0, 'search', 'replace'],\n});\n\n////////////////////\n// Graphite 1.0.x //\n////////////////////\n\naddFuncDef({\n name: 'aggregateLine',\n category: 'Calculate',\n params: [\n {\n name: 'func',\n type: 'select',\n options: ['sum', 'avg', 'min', 'max', 'last'],\n },\n ],\n defaultParams: ['avg'],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'averageOutsidePercentile',\n category: 'Filter Series',\n params: [{ name: 'n', type: 'int' }],\n defaultParams: [95],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'delay',\n category: 'Transform',\n params: [{ name: 'steps', type: 'int' }],\n defaultParams: [1],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'exponentialMovingAverage',\n category: 'Calculate',\n params: [\n {\n name: 'windowSize',\n type: 'int_or_interval',\n options: ['5', '7', '10', '5min', '10min', '30min', '1hour'],\n },\n ],\n defaultParams: [10],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'fallbackSeries',\n category: 'Special',\n params: [{ name: 'fallback', type: 'string' }],\n defaultParams: ['constantLine(0)'],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'grep',\n category: 'Filter Series',\n params: [{ name: 'grep', type: 'string' }],\n defaultParams: ['grep'],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'groupByNodes',\n category: 'Combine',\n params: [\n {\n name: 'function',\n type: 'string',\n options: ['sum', 'avg', 'maxSeries'],\n },\n {\n name: 'node',\n type: 'int',\n options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12],\n multiple: true,\n },\n ],\n defaultParams: ['sum', 3],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'integralByInterval',\n category: 'Transform',\n params: [\n {\n name: 'intervalUnit',\n type: 'select',\n options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'],\n },\n ],\n defaultParams: ['1d'],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'interpolate',\n category: 'Transform',\n params: [{ name: 'limit', type: 'int', optional: true }],\n defaultParams: [],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'invert',\n category: 'Transform',\n version: '1.0',\n});\n\naddFuncDef({\n name: 'isNonNull',\n category: 'Combine',\n version: '1.0',\n});\n\naddFuncDef({\n name: 'linearRegression',\n category: 'Calculate',\n params: [\n {\n name: 'startSourceAt',\n type: 'select',\n options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'],\n optional: true,\n },\n {\n name: 'endSourceAt',\n type: 'select',\n options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'],\n optional: true,\n },\n ],\n defaultParams: [],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'mapSeries',\n shortName: 'map',\n params: [{ name: 'node', type: 'int' }],\n defaultParams: [3],\n category: 'Combine',\n version: '1.0',\n});\n\naddFuncDef({\n name: 'movingMin',\n category: 'Calculate',\n params: [\n {\n name: 'windowSize',\n type: 'int_or_interval',\n options: ['5', '7', '10', '5min', '10min', '30min', '1hour'],\n },\n ],\n defaultParams: [10],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'movingMax',\n category: 'Calculate',\n params: [\n {\n name: 'windowSize',\n type: 'int_or_interval',\n options: ['5', '7', '10', '5min', '10min', '30min', '1hour'],\n },\n ],\n defaultParams: [10],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'movingSum',\n category: 'Calculate',\n params: [\n {\n name: 'windowSize',\n type: 'int_or_interval',\n options: ['5', '7', '10', '5min', '10min', '30min', '1hour'],\n },\n ],\n defaultParams: [10],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'multiplySeriesWithWildcards',\n category: 'Combine',\n params: [\n {\n name: 'position',\n type: 'int',\n options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12],\n multiple: true,\n },\n ],\n defaultParams: [2],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'offsetToZero',\n category: 'Transform',\n version: '1.0',\n});\n\naddFuncDef({\n name: 'pow',\n category: 'Transform',\n params: [{ name: 'factor', type: 'int' }],\n defaultParams: [10],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'powSeries',\n category: 'Transform',\n params: optionalSeriesRefArgs,\n defaultParams: [''],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'reduceSeries',\n shortName: 'reduce',\n params: [\n {\n name: 'function',\n type: 'string',\n options: ['asPercent', 'diffSeries', 'divideSeries'],\n },\n {\n name: 'reduceNode',\n type: 'int',\n options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],\n },\n { name: 'reduceMatchers', type: 'string', multiple: true },\n ],\n defaultParams: ['asPercent', 2, 'used_bytes'],\n category: 'Combine',\n version: '1.0',\n});\n\naddFuncDef({\n name: 'removeBetweenPercentile',\n category: 'Filter Series',\n params: [{ name: 'n', type: 'int' }],\n defaultParams: [95],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'removeEmptySeries',\n category: 'Filter Series',\n version: '1.0',\n});\n\naddFuncDef({\n name: 'squareRoot',\n category: 'Transform',\n version: '1.0',\n});\n\naddFuncDef({\n name: 'timeSlice',\n category: 'Transform',\n params: [\n {\n name: 'startSliceAt',\n type: 'select',\n options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'],\n },\n {\n name: 'endSliceAt',\n type: 'select',\n options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'],\n optional: true,\n },\n ],\n defaultParams: ['-1h'],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'weightedAverage',\n category: 'Combine',\n params: [\n { name: 'other', type: 'value_or_series', optional: true },\n {\n name: 'node',\n type: 'int',\n options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12],\n },\n ],\n defaultParams: ['#A', 4],\n version: '1.0',\n});\n\naddFuncDef({\n name: 'seriesByTag',\n category: 'Special',\n params: [{ name: 'tagExpression', type: 'string', multiple: true }],\n version: '1.1',\n});\n\naddFuncDef({\n name: 'groupByTags',\n category: 'Combine',\n params: [\n {\n name: 'function',\n type: 'string',\n options: ['sum', 'avg', 'maxSeries'],\n },\n { name: 'tag', type: 'string', multiple: true },\n ],\n defaultParams: ['sum', 'tag'],\n version: '1.1',\n});\n\naddFuncDef({\n name: 'aliasByTags',\n category: 'Alias',\n params: [{ name: 'tag', type: 'string', multiple: true }],\n defaultParams: ['tag'],\n version: '1.1',\n});\n\nfunction isVersionRelatedFunction(obj: { version?: string }, graphiteVersion: string) {\n return !obj.version || isVersionGtOrEq(graphiteVersion, obj.version);\n}\n\nexport class FuncInstance {\n def: FuncDef;\n params: Array;\n text: any;\n /**\n * True if this function was just added and not edited yet. It's used to focus on first\n * function param to edit it straight away after adding a function.\n */\n declare added: boolean;\n /**\n * Hidden functions are not displayed in UI but available in text editor\n * This is used for seriesByTagUsed function which when used switches\n * the editor to tag-only mode. Defined tags are provided to seriesByTagUsed\n * as parameters.\n */\n hidden?: boolean;\n\n constructor(funcDef: FuncDef, options?: { withDefaultParams: any }) {\n this.def = funcDef;\n this.params = [];\n\n if (options && options.withDefaultParams && funcDef.defaultParams) {\n this.params = funcDef.defaultParams.slice(0);\n }\n\n this.updateText();\n }\n\n render(metricExp: string, replaceVariables: InterpolateFunction): string {\n const str = this.def.name + '(';\n\n const parameters = map(this.params, (value, index) => {\n let paramType;\n\n if (index < this.def.params.length) {\n paramType = this.def.params[index].type;\n } else if (get(last(this.def.params), 'multiple')) {\n paramType = get(last(this.def.params), 'type');\n }\n\n // param types that should never be quoted\n const neverQuotedParams = ['value_or_series', 'boolean', 'int', 'float', 'node', 'int_or_infinity'];\n\n // functions that should not have param types quoted\n // https://github.com/grafana/grafana/issues/54924\n const neverQuotedFunctions = ['asPercent'];\n // params or functions that should never be quoted\n if (includes(neverQuotedParams, paramType) || includes(neverQuotedFunctions, this.def.name)) {\n return value;\n }\n\n const valueInterpolated = isString(value) ? replaceVariables(value) : value;\n\n // param types that might be quoted\n // To quote variables correctly we need to interpolate it to check if it contains a numeric or string value\n if (includes(['int_or_interval', 'node_or_tag'], paramType) && isFinite(+valueInterpolated)) {\n return toString(value);\n }\n\n return \"'\" + value + \"'\";\n });\n\n // don't send any blank parameters to graphite\n while (parameters[parameters.length - 1] === '') {\n parameters.pop();\n }\n\n if (metricExp) {\n parameters.unshift(metricExp);\n }\n\n return str + parameters.join(', ') + ')';\n }\n\n _hasMultipleParamsInString(strValue: string, index: number) {\n if (strValue.indexOf(',') === -1) {\n return false;\n }\n\n if (this.def.params[index + 1] && this.def.params[index + 1].optional) {\n return true;\n }\n\n if (index + 1 >= this.def.params.length && get(last(this.def.params), 'multiple')) {\n return true;\n }\n\n return false;\n }\n\n updateParam(strValue: string, index: number) {\n // handle optional parameters\n // if string contains ',' and next param is optional, split and update both\n if (this._hasMultipleParamsInString(strValue, index)) {\n each(strValue.split(','), (partVal, idx) => {\n this.updateParam(partVal.trim(), index + idx);\n });\n return;\n }\n\n if (strValue === '' && (index >= this.def.params.length || this.def.params[index].optional)) {\n this.params.splice(index, 1);\n } else {\n this.params[index] = strValue;\n }\n\n this.updateText();\n }\n\n updateText() {\n if (this.params.length === 0) {\n this.text = this.def.name + '()';\n return;\n }\n\n let text = this.def.name + '(';\n text += this.params.join(', ');\n text += ')';\n this.text = text;\n }\n}\n\nfunction createFuncInstance(funcDef: FuncDef | string, options?: { withDefaultParams: any }, idx?: any): FuncInstance {\n if (isString(funcDef)) {\n funcDef = getFuncDef(funcDef, idx);\n }\n return new FuncInstance(funcDef, options);\n}\n\nfunction getFuncDef(name: string, idx?: any): FuncDef {\n if (!(idx || index)[name]) {\n return { name: name, params: [{ name: '', type: '', multiple: true }], defaultParams: [''], unknown: true };\n }\n return (idx || index)[name];\n}\n\nfunction getFuncDefs(graphiteVersion: string, idx?: any): FuncDefs {\n const funcs: FuncDefs = {};\n forEach(idx || index, (funcDef: FuncDef) => {\n if (isVersionRelatedFunction(funcDef, graphiteVersion)) {\n funcs[funcDef.name] = assign({}, funcDef, {\n params: filter(funcDef.params, (param) => {\n return isVersionRelatedFunction(param, graphiteVersion);\n }),\n });\n }\n });\n return funcs;\n}\n\n// parse response from graphite /functions endpoint into internal format\nfunction parseFuncDefs(rawDefs: any): FuncDefs {\n const funcDefs: FuncDefs = {};\n\n forEach(rawDefs || {}, (funcDef, funcName) => {\n // skip graphite graph functions\n if (funcDef.group === 'Graph') {\n return;\n }\n\n let description = funcDef.description;\n if (description) {\n // tidy up some pydoc syntax that rst2html can't handle\n description = description\n .replace(/:py:func:`(.+)( <[^>]*>)?`/g, '``$1``')\n .replace(/.. seealso:: /g, 'See also: ')\n .replace(/.. code-block *:: *none/g, '.. code-block::');\n }\n\n const func: FuncDef = {\n name: funcDef.name,\n description,\n category: funcDef.group,\n params: [],\n defaultParams: [],\n fake: false,\n };\n\n // get rid of the first \"seriesList\" param\n if (/^seriesLists?$/.test(get(funcDef, 'params[0].type', ''))) {\n // handle functions that accept multiple seriesLists\n // we leave the param in place but mark it optional, so users can add more series if they wish\n if (funcDef.params[0].multiple) {\n funcDef.params[0].required = false;\n // otherwise chop off the first param, it'll be handled separately\n } else {\n funcDef.params.shift();\n }\n // tag function as fake\n } else {\n func.fake = true;\n }\n\n forEach(funcDef.params, (rawParam) => {\n const param: any = {\n name: rawParam.name,\n type: 'string',\n optional: !rawParam.required,\n multiple: !!rawParam.multiple,\n options: undefined,\n };\n\n if (rawParam.default !== undefined) {\n if (rawParam.default === Infinity) {\n func.defaultParams.push('inf');\n } else {\n func.defaultParams.push(toString(rawParam.default));\n }\n } else if (rawParam.suggestions) {\n func.defaultParams.push(toString(rawParam.suggestions[0]));\n } else {\n func.defaultParams.push('');\n }\n\n if (rawParam.type === 'boolean') {\n param.type = 'boolean';\n param.options = ['true', 'false'];\n } else if (rawParam.type === 'integer') {\n param.type = 'int';\n } else if (rawParam.type === 'float') {\n param.type = 'float';\n } else if (rawParam.type === 'node') {\n param.type = 'node';\n param.options = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];\n } else if (rawParam.type === 'nodeOrTag') {\n param.type = 'node_or_tag';\n param.options = ['name', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];\n } else if (rawParam.type === 'intOrInterval') {\n param.type = 'int_or_interval';\n } else if (rawParam.type === 'seriesList') {\n param.type = 'value_or_series';\n } else if (rawParam.type === 'intOrInf') {\n param.type = 'int_or_infinity';\n }\n\n if (rawParam.options) {\n param.options = map(rawParam.options, toString);\n } else if (rawParam.suggestions) {\n param.options = map(rawParam.suggestions, toString);\n }\n\n func.params.push(param);\n });\n\n funcDefs[funcName] = func;\n });\n\n return funcDefs;\n}\n\nexport default {\n createFuncInstance: createFuncInstance,\n getFuncDef: getFuncDef,\n getFuncDefs: getFuncDefs,\n parseFuncDefs: parseFuncDefs,\n};\n","type LegacyAnnotation = {\n target?: string;\n tags?: string;\n};\n\n// this becomes the target in the migrated annotations\nconst migrateLegacyAnnotation = (json: LegacyAnnotation) => {\n // return the target annotation\n if (typeof json.target === 'string' && json.target) {\n return {\n fromAnnotations: true,\n target: json.target,\n textEditor: true,\n };\n }\n\n // return the tags annotation\n return {\n queryType: 'tags',\n tags: (json.tags || '').split(' '),\n fromAnnotations: true,\n };\n};\n\n// eslint-ignore-next-line\nexport const prepareAnnotation = (json: any) => {\n // annotation attributes are either 'tags' or 'target'(a graphite query string)\n // because the new annotations will also have a target attribute, {}\n // we need to handle the ambiguous 'target' when migrating legacy annotations\n // so, to migrate legacy annotations\n // we check that target is a string\n // or\n // there is a tags attribute with no target\n const resultingTarget = json.target && typeof json.target !== 'string' ? json.target : migrateLegacyAnnotation(json);\n\n json.target = resultingTarget;\n\n return json;\n};\n","import { each, indexOf, isArray, isString, map as _map } from 'lodash';\nimport { lastValueFrom, merge, Observable, of, OperatorFunction, pipe, throwError } from 'rxjs';\nimport { catchError, map } from 'rxjs/operators';\n\nimport {\n AbstractLabelMatcher,\n AbstractLabelOperator,\n AbstractQuery,\n DataFrame,\n DataQueryRequest,\n DataQueryResponse,\n DataSourceApi,\n DataSourceWithQueryExportSupport,\n dateMath,\n dateTime,\n MetricFindValue,\n QueryResultMetaStat,\n ScopedVars,\n TimeRange,\n TimeZone,\n toDataFrame,\n getSearchFilterScopedVar,\n} from '@grafana/data';\nimport { getBackendSrv } from '@grafana/runtime';\nimport { isVersionGtOrEq, SemVersion } from 'app/core/utils/version';\nimport { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv';\nimport { getRollupNotice, getRuntimeConsolidationNotice } from 'app/plugins/datasource/graphite/meta';\n\nimport { AnnotationEditor } from './components/AnnotationsEditor';\nimport { convertToGraphiteQueryObject } from './components/helpers';\nimport gfunc, { FuncDef, FuncDefs, FuncInstance } from './gfunc';\nimport GraphiteQueryModel from './graphite_query';\nimport { prepareAnnotation } from './migrations';\n// Types\nimport {\n GraphiteLokiMapping,\n GraphiteMetricLokiMatcher,\n GraphiteOptions,\n GraphiteQuery,\n GraphiteQueryImportConfiguration,\n GraphiteQueryRequest,\n GraphiteQueryType,\n GraphiteType,\n MetricTankRequestMeta,\n} from './types';\nimport { reduceError } from './utils';\nimport { DEFAULT_GRAPHITE_VERSION } from './versions';\n\nconst GRAPHITE_TAG_COMPARATORS = {\n '=': AbstractLabelOperator.Equal,\n '!=': AbstractLabelOperator.NotEqual,\n '=~': AbstractLabelOperator.EqualRegEx,\n '!=~': AbstractLabelOperator.NotEqualRegEx,\n};\n\n/**\n * Converts Graphite glob-like pattern to a regular expression\n */\nfunction convertGlobToRegEx(text: string): string {\n if (text.includes('*') || text.includes('{')) {\n return '^' + text.replace(/\\*/g, '.*').replace(/\\{/g, '(').replace(/}/g, ')').replace(/,/g, '|');\n } else {\n return text;\n }\n}\n\nexport class GraphiteDatasource\n extends DataSourceApi\n implements DataSourceWithQueryExportSupport\n{\n basicAuth: string;\n url: string;\n name: string;\n graphiteVersion: any;\n supportsTags: boolean;\n isMetricTank: boolean;\n rollupIndicatorEnabled: boolean;\n cacheTimeout: any;\n withCredentials: boolean;\n funcDefs: FuncDefs | null = null;\n funcDefsPromise: Promise | null = null;\n _seriesRefLetters: string;\n requestCounter = 100;\n private readonly metricMappings: GraphiteLokiMapping[];\n\n constructor(\n instanceSettings: any,\n private readonly templateSrv: TemplateSrv = getTemplateSrv()\n ) {\n super(instanceSettings);\n this.basicAuth = instanceSettings.basicAuth;\n this.url = instanceSettings.url;\n this.name = instanceSettings.name;\n // graphiteVersion is set when a datasource is created but it hadn't been set in the past so we're\n // still falling back to the default behavior here for backwards compatibility (see also #17429)\n this.graphiteVersion = instanceSettings.jsonData.graphiteVersion || DEFAULT_GRAPHITE_VERSION;\n this.metricMappings = instanceSettings.jsonData.importConfiguration?.loki?.mappings || [];\n this.isMetricTank = instanceSettings.jsonData.graphiteType === GraphiteType.Metrictank;\n this.supportsTags = supportsTags(this.graphiteVersion);\n this.cacheTimeout = instanceSettings.cacheTimeout;\n this.rollupIndicatorEnabled = instanceSettings.jsonData.rollupIndicatorEnabled;\n this.withCredentials = instanceSettings.withCredentials;\n this.funcDefs = null;\n this.funcDefsPromise = null;\n this._seriesRefLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\n this.annotations = {\n QueryEditor: AnnotationEditor,\n prepareAnnotation,\n };\n }\n\n getQueryOptionsInfo() {\n return {\n maxDataPoints: true,\n cacheTimeout: true,\n links: [\n {\n text: 'Help',\n url: 'http://docs.grafana.org/features/datasources/graphite/#using-graphite-in-grafana',\n },\n ],\n };\n }\n\n getImportQueryConfiguration(): GraphiteQueryImportConfiguration {\n return {\n loki: {\n mappings: this.metricMappings,\n },\n };\n }\n\n async exportToAbstractQueries(queries: GraphiteQuery[]): Promise {\n return queries.map((query) => this.exportToAbstractQuery(query));\n }\n\n exportToAbstractQuery(query: GraphiteQuery): AbstractQuery {\n const graphiteQuery: GraphiteQueryModel = new GraphiteQueryModel(\n this,\n {\n ...query,\n target: query.target || '',\n textEditor: false,\n },\n getTemplateSrv()\n );\n graphiteQuery.parseTarget();\n\n let labels: AbstractLabelMatcher[] = [];\n const config = this.getImportQueryConfiguration().loki;\n\n if (graphiteQuery.seriesByTagUsed) {\n graphiteQuery.tags.forEach((tag) => {\n labels.push({\n name: tag.key,\n operator: GRAPHITE_TAG_COMPARATORS[tag.operator],\n value: tag.value,\n });\n });\n } else {\n const targetNodes = graphiteQuery.segments.map((segment) => segment.value);\n let mappings = config.mappings.filter((mapping) => mapping.matchers.length <= targetNodes.length);\n\n for (let mapping of mappings) {\n const matchers = mapping.matchers.concat();\n\n matchers.every((matcher: GraphiteMetricLokiMatcher, index: number) => {\n if (matcher.labelName) {\n let value = (targetNodes[index] as string)!;\n\n if (value === '*') {\n return true;\n }\n\n const converted = convertGlobToRegEx(value);\n labels.push({\n name: matcher.labelName,\n operator: converted !== value ? AbstractLabelOperator.EqualRegEx : AbstractLabelOperator.Equal,\n value: converted,\n });\n return true;\n }\n return targetNodes[index] === matcher.value || matcher.value === '*';\n });\n }\n }\n\n return {\n refId: query.refId,\n labelMatchers: labels,\n };\n }\n\n query(options: DataQueryRequest): Observable {\n if (options.targets.some((target: GraphiteQuery) => target.fromAnnotations)) {\n const streams: Array> = [];\n\n for (const target of options.targets) {\n streams.push(\n new Observable((subscriber) => {\n this.annotationEvents(options.range, target)\n .then((events) => subscriber.next({ data: [toDataFrame(events)] }))\n .catch((ex) => subscriber.error(new Error(ex)))\n .finally(() => subscriber.complete());\n })\n );\n }\n\n return merge(...streams);\n }\n\n // handle the queries here\n const graphOptions = {\n from: this.translateTime(options.range.from, false, options.timezone),\n until: this.translateTime(options.range.to, true, options.timezone),\n targets: options.targets,\n format: (options as GraphiteQueryRequest).format,\n cacheTimeout: options.cacheTimeout || this.cacheTimeout,\n maxDataPoints: options.maxDataPoints,\n };\n\n const params = this.buildGraphiteParams(graphOptions, options.scopedVars);\n if (params.length === 0) {\n return of({ data: [] });\n }\n\n if (this.isMetricTank) {\n params.push('meta=true');\n }\n\n const httpOptions: any = {\n method: 'POST',\n url: '/render',\n data: params.join('&'),\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n };\n\n this.addTracingHeaders(httpOptions, options);\n\n if (options.panelId) {\n httpOptions.requestId = this.name + '.panelId.' + options.panelId;\n }\n\n return this.doGraphiteRequest(httpOptions).pipe(map(this.convertResponseToDataFrames));\n }\n\n addTracingHeaders(\n httpOptions: { headers: any },\n options: { dashboardId?: number; panelId?: number; panelPluginId?: string }\n ) {\n const proxyMode = !this.url.match(/^http/);\n if (proxyMode) {\n if (options.dashboardId) {\n httpOptions.headers['X-Dashboard-Id'] = options.dashboardId;\n }\n if (options.panelId) {\n httpOptions.headers['X-Panel-Id'] = options.panelId;\n }\n if (options.panelPluginId) {\n httpOptions.headers['X-Panel-Plugin-Id'] = options.panelPluginId;\n }\n }\n }\n\n convertResponseToDataFrames = (result: any): DataQueryResponse => {\n const data: DataFrame[] = [];\n if (!result || !result.data) {\n return { data };\n }\n\n // Series are either at the root or under a node called 'series'\n const series = result.data.series || result.data;\n\n if (!isArray(series)) {\n throw { message: 'Missing series in result', data: result };\n }\n\n for (let i = 0; i < series.length; i++) {\n const s = series[i];\n\n // Disables Grafana own series naming\n s.title = s.target;\n\n for (let y = 0; y < s.datapoints.length; y++) {\n s.datapoints[y][1] *= 1000;\n }\n\n const frame = toDataFrame(s);\n\n // Metrictank metadata\n if (s.meta) {\n frame.meta = {\n custom: {\n requestMetaList: result.data.meta, // info for the whole request\n seriesMetaList: s.meta, // Array of metadata\n },\n };\n\n if (this.rollupIndicatorEnabled) {\n const rollupNotice = getRollupNotice(s.meta);\n const runtimeNotice = getRuntimeConsolidationNotice(s.meta);\n\n if (rollupNotice) {\n frame.meta.notices = [rollupNotice];\n } else if (runtimeNotice) {\n frame.meta.notices = [runtimeNotice];\n }\n }\n\n // only add the request stats to the first frame\n if (i === 0 && result.data.meta.stats) {\n frame.meta.stats = this.getRequestStats(result.data.meta);\n }\n }\n\n data.push(frame);\n }\n\n return { data };\n };\n\n getRequestStats(meta: MetricTankRequestMeta): QueryResultMetaStat[] {\n const stats: QueryResultMetaStat[] = [];\n\n for (const key in meta.stats) {\n let unit: string | undefined = undefined;\n\n if (key.endsWith('.ms')) {\n unit = 'ms';\n }\n\n stats.push({ displayName: key, value: meta.stats[key], unit });\n }\n\n return stats;\n }\n\n parseTags(tagString: string) {\n let tags: string[] = [];\n tags = tagString.split(',');\n if (tags.length === 1) {\n tags = tagString.split(' ');\n if (tags[0] === '') {\n tags = [];\n }\n }\n return tags;\n }\n\n interpolateVariablesInQueries(queries: GraphiteQuery[], scopedVars: ScopedVars): GraphiteQuery[] {\n let expandedQueries = queries;\n if (queries && queries.length > 0) {\n expandedQueries = queries.map((query) => {\n const expandedQuery = {\n ...query,\n datasource: this.getRef(),\n target: this.templateSrv.replace(query.target ?? '', scopedVars),\n };\n return expandedQuery;\n });\n }\n return expandedQueries;\n }\n\n annotationEvents(range: any, target: any) {\n if (target.target) {\n // Graphite query as target as annotation\n const targetAnnotation = this.templateSrv.replace(target.target, {}, 'glob');\n const graphiteQuery = {\n range: range,\n targets: [{ target: targetAnnotation }],\n format: 'json',\n maxDataPoints: 100,\n } as unknown as DataQueryRequest;\n\n return lastValueFrom(\n this.query(graphiteQuery).pipe(\n map((result) => {\n const list = [];\n\n for (let i = 0; i < result.data.length; i++) {\n const target = result.data[i];\n\n for (let y = 0; y < target.length; y++) {\n const time = target.fields[0].values[y];\n const value = target.fields[1].values[y];\n\n if (!value) {\n continue;\n }\n\n list.push({\n annotation: target,\n time,\n title: target.name,\n });\n }\n }\n\n return list;\n })\n )\n );\n } else {\n // Graphite event/tag as annotation\n const tags = this.templateSrv.replace(target.tags?.join(' '));\n return this.events({ range: range, tags: tags }).then((results) => {\n const list = [];\n if (!isArray(results.data)) {\n console.error(`Unable to get annotations from ${results.url}.`);\n return [];\n }\n for (let i = 0; i < results.data.length; i++) {\n const e = results.data[i];\n\n let tags = e.tags;\n if (isString(e.tags)) {\n tags = this.parseTags(e.tags);\n }\n\n list.push({\n annotation: target,\n time: e.when * 1000,\n title: e.what,\n tags: tags,\n text: e.data,\n });\n }\n\n return list;\n });\n }\n }\n\n events(options: { range: TimeRange; tags: any; timezone?: any }) {\n try {\n let tags = '';\n if (options.tags) {\n tags = '&tags=' + options.tags;\n }\n return lastValueFrom(\n this.doGraphiteRequest({\n method: 'GET',\n url:\n '/events/get_data?from=' +\n this.translateTime(options.range.raw.from, false, options.timezone) +\n '&until=' +\n this.translateTime(options.range.raw.to, true, options.timezone) +\n tags,\n })\n );\n } catch (err) {\n return Promise.reject(err);\n }\n }\n\n targetContainsTemplate(target: GraphiteQuery) {\n return this.templateSrv.containsTemplate(target.target ?? '');\n }\n\n translateTime(date: any, roundUp: any, timezone: TimeZone) {\n if (isString(date)) {\n if (date === 'now') {\n return 'now';\n } else if (date.indexOf('now-') >= 0 && date.indexOf('/') === -1) {\n date = date.substring(3);\n date = date.replace('m', 'min');\n date = date.replace('M', 'mon');\n return date;\n }\n date = dateMath.parse(date, roundUp, timezone);\n }\n\n // graphite' s from filter is exclusive\n // here we step back one minute in order\n // to guarantee that we get all the data that\n // exists for the specified range\n if (roundUp) {\n if (date.get('s')) {\n date.add(1, 's');\n }\n } else if (roundUp === false) {\n if (date.get('s')) {\n date.subtract(1, 's');\n }\n }\n\n return date.unix();\n }\n\n metricFindQuery(findQuery: string | GraphiteQuery, optionalOptions?: any): Promise {\n const options: any = optionalOptions || {};\n\n const queryObject = convertToGraphiteQueryObject(findQuery);\n if (queryObject.queryType === GraphiteQueryType.Value || queryObject.queryType === GraphiteQueryType.MetricName) {\n return this.requestMetricRender(queryObject, options, queryObject.queryType);\n }\n\n let query = queryObject.target ?? '';\n\n // First attempt to check for tag-related functions (using empty wildcard for interpolation)\n let interpolatedQuery = this.templateSrv.replace(\n query,\n getSearchFilterScopedVar({ query, wildcardChar: '', options: optionalOptions })\n );\n\n // special handling for tag_values([,]*), this is used for template variables\n let allParams = interpolatedQuery.match(/^tag_values\\((.*)\\)$/);\n let expressions = allParams ? allParams[1].split(',').filter((p) => !!p) : undefined;\n if (expressions) {\n options.limit = 10000;\n return this.getTagValuesAutoComplete(expressions.slice(1), expressions[0], undefined, options);\n }\n\n // special handling for tags([,]*), this is used for template variables\n allParams = interpolatedQuery.match(/^tags\\((.*)\\)$/);\n expressions = allParams ? allParams[1].split(',').filter((p) => !!p) : undefined;\n if (expressions) {\n options.limit = 10000;\n return this.getTagsAutoComplete(expressions, undefined, options);\n }\n\n // If no tag-related query was found, perform metric-based search (using * as the wildcard for interpolation)\n let useExpand = query.match(/^expand\\((.*)\\)$/);\n query = useExpand ? useExpand[1] : query;\n\n interpolatedQuery = this.templateSrv.replace(\n query,\n getSearchFilterScopedVar({ query, wildcardChar: '*', options: optionalOptions })\n );\n\n let range;\n if (options.range) {\n range = {\n from: this.translateTime(options.range.from, false, options.timezone),\n until: this.translateTime(options.range.to, true, options.timezone),\n };\n }\n\n if (useExpand) {\n return this.requestMetricExpand(interpolatedQuery, options.requestId, range);\n } else {\n return this.requestMetricFind(interpolatedQuery, options.requestId, range);\n }\n }\n\n /**\n * Search for metrics matching giving pattern using /metrics/render endpoint.\n * It will return all possible values or names and parse them based on queryType.\n * For example:\n *\n * queryType: GraphiteQueryType.Value\n * query: groupByNode(movingAverage(apps.country.IE.counters.requests.count, 10), 2, 'sum')\n * result: 239.4, 233.4, 230.8, 230.4, 233.9, 238, 239.8, 236.8, 235.8\n *\n * queryType: GraphiteQueryType.MetricName\n * query: highestAverage(carbon.agents.*.*, 5)\n * result: carbon.agents.aa6338c54341-a.memUsage, carbon.agents.aa6338c54341-a.committedPoints, carbon.agents.aa6338c54341-a.updateOperations, carbon.agents.aa6338c54341-a.metricsReceived, carbon.agents.aa6338c54341-a.activeConnections\n */\n private async requestMetricRender(\n queryObject: GraphiteQuery,\n options: any,\n queryType: GraphiteQueryType\n ): Promise {\n const requestId: string = options.requestId ?? `Q${this.requestCounter++}`;\n const range: TimeRange = options.range ?? {\n from: dateTime().subtract(6, 'hour'),\n to: dateTime(),\n raw: {\n from: 'now - 6h',\n to: 'now',\n },\n };\n const queryReq: DataQueryRequest = {\n app: 'graphite-variable-editor',\n interval: '1s',\n intervalMs: 10000,\n startTime: Date.now(),\n targets: [{ ...queryObject }],\n timezone: 'browser',\n scopedVars: {},\n requestId,\n range,\n };\n const data: DataQueryResponse = await lastValueFrom(this.query(queryReq));\n\n let result: MetricFindValue[];\n\n if (queryType === GraphiteQueryType.Value) {\n result = data.data[0].fields[1].values\n .filter((f?: number) => !!f)\n .map((v: number) => ({\n text: v.toString(),\n value: v,\n expandable: false,\n }));\n } else if (queryType === GraphiteQueryType.MetricName) {\n result = data.data.map((series) => ({\n text: series.name,\n value: series.name,\n expandable: false,\n }));\n } else {\n result = [];\n }\n\n return Promise.resolve(result);\n }\n\n /**\n * Search for metrics matching giving pattern using /metrics/find endpoint. It will\n * return all possible values at the last level of the query, for example:\n *\n * metrics: prod.servers.001.cpu, prod.servers.002.cpu\n * query: *.servers.*\n * result: 001, 002\n *\n * For more complex searches use requestMetricExpand\n */\n private requestMetricFind(\n query: string,\n requestId: string,\n range?: { from: any; until: any }\n ): Promise {\n const httpOptions: any = {\n method: 'POST',\n url: '/metrics/find',\n params: {},\n data: `query=${query}`,\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n // for cancellations\n requestId: requestId,\n };\n\n if (range) {\n httpOptions.params.from = range.from;\n httpOptions.params.until = range.until;\n }\n\n return lastValueFrom(\n this.doGraphiteRequest(httpOptions).pipe(\n map((results: any) => {\n return _map(results.data, (metric) => {\n return {\n text: metric.text,\n expandable: metric.expandable ? true : false,\n };\n });\n })\n )\n );\n }\n\n /**\n * Search for metrics matching giving pattern using /metrics/expand endpoint.\n * The result will contain all metrics (with full name) matching provided query.\n * It's a more flexible version of /metrics/find endpoint (@see requestMetricFind)\n */\n private requestMetricExpand(\n query: string,\n requestId: string,\n range?: { from: any; until: any }\n ): Promise {\n const httpOptions: any = {\n method: 'GET',\n url: '/metrics/expand',\n params: { query },\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n // for cancellations\n requestId,\n };\n\n if (range) {\n httpOptions.params.from = range.from;\n httpOptions.params.until = range.until;\n }\n\n return lastValueFrom(\n this.doGraphiteRequest(httpOptions).pipe(\n map((results: any) => {\n return _map(results.data.results, (metric) => {\n return {\n text: metric,\n expandable: false,\n };\n });\n })\n )\n );\n }\n\n getTags(optionalOptions: any) {\n const options = optionalOptions || {};\n\n const httpOptions: any = {\n method: 'GET',\n url: '/tags',\n // for cancellations\n requestId: options.requestId,\n };\n\n if (options.range) {\n httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone);\n httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone);\n }\n\n return lastValueFrom(\n this.doGraphiteRequest(httpOptions).pipe(\n map((results: any) => {\n return _map(results.data, (tag) => {\n return {\n text: tag.tag,\n id: tag.id,\n };\n });\n })\n )\n );\n }\n\n getTagValues(options: any = {}) {\n const httpOptions: any = {\n method: 'GET',\n url: '/tags/' + this.templateSrv.replace(options.key),\n // for cancellations\n requestId: options.requestId,\n };\n\n if (options.range) {\n httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone);\n httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone);\n }\n\n return lastValueFrom(\n this.doGraphiteRequest(httpOptions).pipe(\n map((results: any) => {\n if (results.data && results.data.values) {\n return _map(results.data.values, (value) => {\n return {\n text: value.value,\n id: value.id,\n };\n });\n } else {\n return [];\n }\n })\n )\n );\n }\n\n getTagsAutoComplete(expressions: any[], tagPrefix: any, optionalOptions?: any) {\n const options = optionalOptions || {};\n\n const httpOptions: any = {\n method: 'GET',\n url: '/tags/autoComplete/tags',\n params: {\n expr: _map(expressions, (expression) => this.templateSrv.replace((expression || '').trim())),\n },\n // for cancellations\n requestId: options.requestId,\n };\n\n if (tagPrefix) {\n httpOptions.params.tagPrefix = tagPrefix;\n }\n if (options.limit) {\n httpOptions.params.limit = options.limit;\n }\n if (options.range) {\n httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone);\n httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone);\n }\n return lastValueFrom(this.doGraphiteRequest(httpOptions).pipe(mapToTags()));\n }\n\n getTagValuesAutoComplete(expressions: any[], tag: any, valuePrefix: any, optionalOptions: any) {\n const options = optionalOptions || {};\n\n const httpOptions: any = {\n method: 'GET',\n url: '/tags/autoComplete/values',\n params: {\n expr: _map(expressions, (expression) => this.templateSrv.replace((expression || '').trim())),\n tag: this.templateSrv.replace((tag || '').trim()),\n },\n // for cancellations\n requestId: options.requestId,\n };\n\n if (valuePrefix) {\n httpOptions.params.valuePrefix = valuePrefix;\n }\n if (options.limit) {\n httpOptions.params.limit = options.limit;\n }\n if (options.range) {\n httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone);\n httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone);\n }\n return lastValueFrom(this.doGraphiteRequest(httpOptions).pipe(mapToTags()));\n }\n\n getVersion(optionalOptions: any) {\n const options = optionalOptions || {};\n\n const httpOptions = {\n method: 'GET',\n url: '/version',\n requestId: options.requestId,\n };\n\n return lastValueFrom(\n this.doGraphiteRequest(httpOptions).pipe(\n map((results: any) => {\n if (results.data) {\n const semver = new SemVersion(results.data);\n return semver.isValid() ? results.data : '';\n }\n return '';\n }),\n catchError(() => {\n return of('');\n })\n )\n );\n }\n\n createFuncInstance(funcDef: string | FuncDef, options?: any): FuncInstance {\n return gfunc.createFuncInstance(funcDef, options, this.funcDefs);\n }\n\n getFuncDef(name: string) {\n return gfunc.getFuncDef(name, this.funcDefs);\n }\n\n waitForFuncDefsLoaded() {\n return this.getFuncDefs();\n }\n\n getFuncDefs() {\n if (this.funcDefsPromise !== null) {\n return this.funcDefsPromise;\n }\n\n if (!supportsFunctionIndex(this.graphiteVersion)) {\n this.funcDefs = gfunc.getFuncDefs(this.graphiteVersion);\n this.funcDefsPromise = Promise.resolve(this.funcDefs);\n return this.funcDefsPromise;\n }\n\n const httpOptions = {\n method: 'GET',\n url: '/functions',\n // add responseType because if this is not defined,\n // backend_srv defaults to json\n responseType: 'text',\n };\n\n return lastValueFrom(\n this.doGraphiteRequest(httpOptions).pipe(\n map((results: any) => {\n // Fix for a Graphite bug: https://github.com/graphite-project/graphite-web/issues/2609\n // There is a fix for it https://github.com/graphite-project/graphite-web/pull/2612 but\n // it was merged to master in July 2020 but it has never been released (the last Graphite\n // release was 1.1.7 - March 2020). The bug was introduced in Graphite 1.1.7, in versions\n // 1.1.0 - 1.1.6 /functions endpoint returns a valid JSON\n const fixedData = JSON.parse(results.data.replace(/\"default\": ?Infinity/g, '\"default\": 1e9999'));\n this.funcDefs = gfunc.parseFuncDefs(fixedData);\n return this.funcDefs;\n }),\n catchError((error) => {\n console.error('Fetching graphite functions error', error);\n this.funcDefs = gfunc.getFuncDefs(this.graphiteVersion);\n return of(this.funcDefs);\n })\n )\n );\n }\n\n testDatasource() {\n const query: DataQueryRequest = {\n app: 'graphite',\n interval: '10ms',\n intervalMs: 10,\n requestId: 'reqId',\n scopedVars: {},\n startTime: 0,\n timezone: 'browser',\n panelId: 3,\n rangeRaw: { from: 'now-1h', to: 'now' },\n range: {\n from: dateTime('now-1h'),\n to: dateTime('now'),\n raw: { from: 'now-1h', to: 'now' },\n },\n targets: [{ refId: 'A', target: 'constantLine(100)' }],\n maxDataPoints: 300,\n };\n\n return lastValueFrom(this.query(query)).then(() => ({ status: 'success', message: 'Data source is working' }));\n }\n\n doGraphiteRequest(options: {\n method?: string;\n url: any;\n requestId?: any;\n withCredentials?: any;\n headers?: any;\n inspect?: any;\n }) {\n if (this.basicAuth || this.withCredentials) {\n options.withCredentials = true;\n }\n if (this.basicAuth) {\n options.headers = options.headers || {};\n options.headers.Authorization = this.basicAuth;\n }\n\n options.url = this.url + options.url;\n options.inspect = { type: 'graphite' };\n\n return getBackendSrv()\n .fetch(options)\n .pipe(\n catchError((err) => {\n return throwError(reduceError(err));\n })\n );\n }\n\n buildGraphiteParams(options: any, scopedVars?: ScopedVars): string[] {\n const graphiteOptions = ['from', 'until', 'rawData', 'format', 'maxDataPoints', 'cacheTimeout'];\n const cleanOptions = [],\n targets: any = {};\n let target, targetValue, i;\n const regex = /\\#([A-Z])/g;\n const intervalFormatFixRegex = /'(\\d+)m'/gi;\n let hasTargets = false;\n\n options['format'] = 'json';\n\n function fixIntervalFormat(match: string) {\n return match.replace('m', 'min').replace('M', 'mon');\n }\n\n for (i = 0; i < options.targets.length; i++) {\n target = options.targets[i];\n if (!target.target) {\n continue;\n }\n\n if (!target.refId) {\n target.refId = this._seriesRefLetters[i];\n }\n\n targetValue = this.templateSrv.replace(target.target, scopedVars);\n targetValue = targetValue.replace(intervalFormatFixRegex, fixIntervalFormat);\n targets[target.refId] = targetValue;\n }\n\n function nestedSeriesRegexReplacer(match: any, g1: string | number) {\n return targets[g1] || match;\n }\n\n for (i = 0; i < options.targets.length; i++) {\n target = options.targets[i];\n if (!target.target) {\n continue;\n }\n\n targetValue = targets[target.refId];\n targetValue = targetValue.replace(regex, nestedSeriesRegexReplacer);\n targets[target.refId] = targetValue;\n\n if (!target.hide) {\n hasTargets = true;\n cleanOptions.push('target=' + encodeURIComponent(targetValue));\n }\n }\n\n each(options, (value, key) => {\n if (indexOf(graphiteOptions, key) === -1) {\n return;\n }\n if (value) {\n cleanOptions.push(key + '=' + encodeURIComponent(value));\n }\n });\n\n if (!hasTargets) {\n return [];\n }\n\n return cleanOptions;\n }\n}\n\nfunction supportsTags(version: string): boolean {\n return isVersionGtOrEq(version, '1.1');\n}\n\nfunction supportsFunctionIndex(version: string): boolean {\n return isVersionGtOrEq(version, '1.1');\n}\n\nfunction mapToTags(): OperatorFunction> {\n return pipe(\n map((results) => {\n if (results.data) {\n return _map(results.data, (value) => {\n return { text: value };\n });\n } else {\n return [];\n }\n })\n );\n}\n","import { DataSourcePlugin } from '@grafana/data';\n\nimport { GraphiteQueryEditor } from './components/GraphiteQueryEditor';\nimport { GraphiteVariableEditor } from './components/GraphiteVariableEditor';\nimport { MetricTankMetaInspector } from './components/MetricTankMetaInspector';\nimport { ConfigEditor } from './configuration/ConfigEditor';\nimport { GraphiteDatasource } from './datasource';\n\nexport const plugin = new DataSourcePlugin(GraphiteDatasource)\n .setQueryEditor(GraphiteQueryEditor)\n .setConfigEditor(ConfigEditor)\n .setVariableQueryEditor(GraphiteVariableEditor)\n .setMetadataInspector(MetricTankMetaInspector);\n"],"names":["init","timeRangeChanged","queriesChanged","queryChanged","segmentValueChanged","addNewTag","tagChanged","unpause","addFunction","removeFunction","moveFunction","updateFunctionParam","updateQuery","runQuery","toggleEditorMode","actions","unicodeLetterTable","identifierStartTable","i","identifierPartTable","Lexer","expression","list","token","match","id","index","type","char","isUnicodeLetter","code","isHexDigit","str","readUnicodeEscapeSequence","ch1","ch2","ch3","ch4","getIdentifierStart","chr","getIdentifierPart","value","length","bad","isDecimalDigit","isOctalDigit","isIdentifierStart","ch","quote","jump","reduceError","error","newMessage","isGraphiteParserError","e","Parser","curlySegment","curly","tokenValue","parts","node","segments","segment","name","param","text","currentToken","token1","token2","GraphiteQuery","datasource","target","templateSrv","scopedVars","astNode","err","arr","result","func","innerFunc","handleMultipleSeriesByTagsParams","newFunc","offset","arrayMove","targets","wrapFunction","metricPath","targetsByRefId","nestedSeriesRefRegex","targetWithNestedQueries","t","regex","refMatches","updated","g1","tagPattern","matches","tag","seriesByTagFuncIndex","newTagParam","renderTagString","tagIndex","excludeIndex","tagExpr","count","p","stringParams","acc","idx","paramsArr","GRAPHITE_TAG_OPERATORS","TAG_PREFIX","parseTarget","state","buildSegments","modifyLastSegment","checkOtherSegmentsIndex","checkOtherSegments","addSelectMetricSegment","fromIndex","currentFromIndex","path","handleMetricsAutoCompleteError","spliceSegments","emptySegments","addSeriesByTagFunc","tagParam","handleTargetChanged","smartlyHandleNewAliasByNode","pause","removeTagPrefix","oldTarget","query","newTarget","handleTagsAutoCompleteError","reducer","action","deps","segmentOrString","segmentIndex","newTag","createStore","onChange","DispatchContext","GraphiteStateContext","useDispatch","useGraphiteState","GraphiteQueryEditorContext","onRunQuery","queries","range","children","setState","needsRefresh","setNeedsRefresh","dispatch","previousRange","usePrevious","GraphiteQueryType","GraphiteType","mapStringsToSelectables","values","mapSegmentsToSelectables","mapFuncDefsToSelectables","funcDefs","categories","funcDef","createEditableParam","paramDef","additional","option","mapFuncInstanceToParams","params","convertToGraphiteQueryObject","AddGraphiteFunction","setValue","styles","getStyles","options","Segment","Button","theme","FunctionDescription","props","FunctionHelpButton","tooltip","Tooltip","Icon","FunctionEditorControls","onMoveLeft","onMoveRight","onRemove","FunctionEditor","renderContent","updatePopperPosition","TooltipContent","FunctionParamEditor","editableParam","onExpandedChange","autofocus","SegmentInput","GraphiteFunctionEditor","mouseOver","setIsMouseOver","expanded","setIsExpanded","InlineLabel","FunctionsSection","functions","SegmentSection","GraphiteTextEditor","rawQuery","QueryField","MAX_SUGGESTIONS","getAltSegments","prefix","altSegments","variable","removeTaggedEntry","addAltTagSegments","getAltSegmentsSelectables","getTagOperatorsSelectables","getTags","tagPrefix","tagExpressions","altTags","getTagsSelectables","getTagsAsSegments","tagsAsSegments","val","getTagsAsSegmentsSelectables","getTagValues","valuePrefix","tagKey","altValues","getTagValuesSelectables","tagSegments","s","MetricSegment","metricIndex","loadOptions","debouncedLoadOptions","onSegmentChanged","selectableValue","onSegmentChangedDebounced","SegmentAsync","MetricsSection","PlayButton","onClick","TagEditor","getTagsOptions","inputValue","debouncedGetTagsOptions","getTagValueOptions","debouncedGetTagValueOptions","TagsSection","tags","getTagsAsSegmentsOptions","debouncedGetTagsAsSegments","SeriesSection","sectionContent","GraphiteQueryEditor","GraphiteQueryEditorContent","GRAPHITE_QUERY_VARIABLE_TYPE_OPTIONS","GraphiteVariableEditor","InlineField","Select","Input","toInteger","toBooleanOrTimestamp","getRollupNotice","metaList","meta","archiveIndex","intervalString","parseSchemaRetentions","getRuntimeConsolidationNotice","runtimeNr","spec","vals","MetricTankMetaInspector","key","buckets","rollupNotice","runtimeNotice","normFunc","totalSeconds","bucket","lengthPercent","isActive","data","seriesMetas","series","metaItem","stylesFactory","config","borderColor","background","headerBg","GRAPHITE_VERSIONS","DEFAULT_GRAPHITE_VERSION","MappingsHelp","Alert","MappingsConfiguration","mappings","setMappings","mapping","InlineFieldRow","changeEvent","newMappings","_","fromString","metricNode","matcher","SHOW_MAPPINGS_HELP_KEY","graphiteVersions","version","graphiteTypes","label","ConfigEditor","onOptionsChange","currentVersion","item","DataSourceHttpSettings","FieldSet","Field","Switch","versionPattern","SemVersion","compared","isVersionGtOrEq","a","b","AnnotationEditor","setTarget","setTags","updateValue","onTagsChange","tagsInput","TagsInput","addFuncDef","optionalSeriesRefArgs","isVersionRelatedFunction","obj","graphiteVersion","FuncInstance","metricExp","replaceVariables","parameters","paramType","neverQuotedParams","neverQuotedFunctions","valueInterpolated","strValue","partVal","createFuncInstance","getFuncDef","getFuncDefs","funcs","parseFuncDefs","rawDefs","funcName","description","rawParam","migrateLegacyAnnotation","json","prepareAnnotation","resultingTarget","GRAPHITE_TAG_COMPARATORS","convertGlobToRegEx","GraphiteDatasource","instanceSettings","y","frame","supportsTags","graphiteQuery","labels","targetNodes","converted","streams","Observable","subscriber","events","ex","merge","graphOptions","of","httpOptions","map","stats","unit","tagString","expandedQueries","targetAnnotation","lastValueFrom","time","results","date","roundUp","timezone","findQuery","optionalOptions","queryObject","interpolatedQuery","allParams","expressions","useExpand","queryType","requestId","queryReq","f","v","metric","mapToTags","catchError","gfunc","supportsFunctionIndex","fixedData","throwError","graphiteOptions","cleanOptions","targetValue","intervalFormatFixRegex","hasTargets","fixIntervalFormat","nestedSeriesRegexReplacer","pipe"],"sourceRoot":""}