{
+ | {labelType && } |
{/* Key - value columns */}
{singleKey ? parsedKeys[0] : this.generateMultiVal(parsedKeys)} |
@@ -360,7 +384,7 @@ class UnThemedLogDetailsRow extends PureComponent {
{showFieldsStats && singleKey && singleVal && (
- |
+ |
{
}
}
+function LabelTypeBadge({ type, styles }: { type: string; styles: ReturnType }) {
+ return (
+
+
+ {type.substring(0, 1)}
+
+
+ );
+}
+
interface AsyncIconButtonProps extends Pick, 'onClick'> {
name: IconName;
isActive(): Promise;
diff --git a/public/app/features/logs/components/LogLabels.test.tsx b/public/app/features/logs/components/LogLabels.test.tsx
index af5217d110f..f736b0a595c 100644
--- a/public/app/features/logs/components/LogLabels.test.tsx
+++ b/public/app/features/logs/components/LogLabels.test.tsx
@@ -1,6 +1,7 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
+import { LOG_LINE_BODY_FIELD_NAME } from './LogDetailsBody';
import { LogLabels, LogLabelsList } from './LogLabels';
describe('', () => {
@@ -38,8 +39,9 @@ describe('', () => {
describe('', () => {
it('renders labels', () => {
- render();
+ render();
expect(screen.queryByText('bar')).toBeInTheDocument();
expect(screen.queryByText('42')).toBeInTheDocument();
+ expect(screen.queryByText('log line')).toBeInTheDocument();
});
});
diff --git a/public/app/features/logs/components/LogLabels.tsx b/public/app/features/logs/components/LogLabels.tsx
index 951597c887f..fa675287a72 100644
--- a/public/app/features/logs/components/LogLabels.tsx
+++ b/public/app/features/logs/components/LogLabels.tsx
@@ -4,6 +4,8 @@ import { memo, forwardRef, useMemo } from 'react';
import { GrafanaTheme2, Labels } from '@grafana/data';
import { Tooltip, useStyles2 } from '@grafana/ui';
+import { LOG_LINE_BODY_FIELD_NAME } from './LogDetailsBody';
+
// Levels are already encoded in color, filename is a Loki-ism
const HIDDEN_LABELS = ['detected_level', 'level', 'lvl', 'filename'];
@@ -59,7 +61,7 @@ export const LogLabelsList = memo(({ labels }: LogLabelsArrayProps) => {
{labels.map((label) => (
- {label}
+ {label === LOG_LINE_BODY_FIELD_NAME ? 'log line' : label}
))}
diff --git a/public/app/features/logs/components/LogRowMessageDisplayedFields.test.tsx b/public/app/features/logs/components/LogRowMessageDisplayedFields.test.tsx
index 05b48eabff1..4e0bdfccf4a 100644
--- a/public/app/features/logs/components/LogRowMessageDisplayedFields.test.tsx
+++ b/public/app/features/logs/components/LogRowMessageDisplayedFields.test.tsx
@@ -4,6 +4,7 @@ import userEvent from '@testing-library/user-event';
import { createTheme, LogLevel } from '@grafana/data';
import { IconButton } from '@grafana/ui';
+import { LOG_LINE_BODY_FIELD_NAME } from './LogDetailsBody';
import { LogRowMessageDisplayedFields, Props } from './LogRowMessageDisplayedFields';
import { createLogRow } from './__mocks__/logRow';
import { getLogRowStyles } from './getLogRowStyles';
@@ -42,7 +43,14 @@ const setup = (propOverrides: Partial = {}, detectedFields = ['place', 'p
describe('LogRowMessageDisplayedFields', () => {
it('renders diplayed fields from a log row', () => {
setup();
- expect(screen.queryByText('Logs are wonderful')).not.toBeInTheDocument();
+ expect(screen.queryByText(/Logs are wonderful/)).not.toBeInTheDocument();
+ expect(screen.getByText(/place=Earth/)).toBeInTheDocument();
+ expect(screen.getByText(/planet=Mars/)).toBeInTheDocument();
+ });
+
+ it('renders diplayed fields and body from a log row', () => {
+ setup({}, ['place', 'planet', LOG_LINE_BODY_FIELD_NAME]);
+ expect(screen.queryByText(/Logs are wonderful/)).toBeInTheDocument();
expect(screen.getByText(/place=Earth/)).toBeInTheDocument();
expect(screen.getByText(/planet=Mars/)).toBeInTheDocument();
});
diff --git a/public/app/features/logs/components/LogRowMessageDisplayedFields.tsx b/public/app/features/logs/components/LogRowMessageDisplayedFields.tsx
index 363fb97bb17..2cc495148a1 100644
--- a/public/app/features/logs/components/LogRowMessageDisplayedFields.tsx
+++ b/public/app/features/logs/components/LogRowMessageDisplayedFields.tsx
@@ -3,6 +3,7 @@ import { memo, ReactNode, useMemo } from 'react';
import { LogRowModel, Field, LinkModel, DataFrame } from '@grafana/data';
+import { LOG_LINE_BODY_FIELD_NAME } from './LogDetailsBody';
import { LogRowMenuCell } from './LogRowMenuCell';
import { LogRowStyles } from './getLogRowStyles';
import { getAllFields } from './logParser';
@@ -45,21 +46,26 @@ export const LogRowMessageDisplayedFields = memo((props: Props) => {
let line = '';
for (let i = 0; i < detectedFields.length; i++) {
const parsedKey = detectedFields[i];
+
+ if (parsedKey === LOG_LINE_BODY_FIELD_NAME) {
+ line += ` ${row.entry}`;
+ }
+
const field = fields.find((field) => {
const { keys } = field;
return keys[0] === parsedKey;
});
- if (field) {
+ if (field != null) {
line += ` ${parsedKey}=${field.values}`;
}
- if (row.labels[parsedKey] !== undefined && row.labels[parsedKey] !== null) {
+ if (row.labels[parsedKey] != null && row.labels[parsedKey] != null) {
line += ` ${parsedKey}=${row.labels[parsedKey]}`;
}
}
return line.trimStart();
- }, [detectedFields, fields, row.labels]);
+ }, [detectedFields, fields, row.entry, row.labels]);
const shouldShowMenu = useMemo(() => mouseIsOver || pinned, [mouseIsOver, pinned]);
diff --git a/public/app/features/logs/utils.ts b/public/app/features/logs/utils.ts
index 4d8f88c5641..e6db5c67038 100644
--- a/public/app/features/logs/utils.ts
+++ b/public/app/features/logs/utils.ts
@@ -343,3 +343,48 @@ export function createLogRowsMap() {
return false;
};
}
+
+function getLabelTypeFromFrame(labelKey: string, frame: DataFrame, index: number): null | string {
+ const typeField = frame.fields.find((field) => field.name === 'labelTypes')?.values[index];
+ if (!typeField) {
+ return null;
+ }
+ return typeField[labelKey] ?? null;
+}
+
+export function getLabelTypeFromRow(label: string, row: LogRowModel) {
+ if (!row.datasourceType) {
+ return null;
+ }
+ const idField = row.dataFrame.fields.find((field) => field.name === 'id');
+ if (!idField) {
+ return null;
+ }
+ const rowIndex = idField.values.findIndex((id) => id === row.rowId);
+ if (rowIndex < 0) {
+ return null;
+ }
+ const labelType = getLabelTypeFromFrame(label, row.dataFrame, rowIndex);
+ if (!labelType) {
+ return null;
+ }
+ return getDataSourceLabelType(labelType, row.datasourceType);
+}
+
+function getDataSourceLabelType(labelType: string, datasourceType: string) {
+ switch (datasourceType) {
+ case 'loki':
+ switch (labelType) {
+ case 'I':
+ return 'Indexed label';
+ case 'S':
+ return 'Structured metadata';
+ case 'P':
+ return 'Parsed label';
+ default:
+ return null;
+ }
+ default:
+ return null;
+ }
+}
diff --git a/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx b/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx
index 5ce78e36621..c82226fc763 100644
--- a/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx
+++ b/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx
@@ -13,9 +13,9 @@ import {
import { Stack, Text, TextLink } from '@grafana/ui';
import { Trans } from 'app/core/internationalization';
-import { getUnitFromMetric } from '../AutomaticMetricQueries/units';
import { MetricScene } from '../MetricScene';
import { StatusWrapper } from '../StatusWrapper';
+import { getUnitFromMetric } from '../autoQuery/units';
import { reportExploreMetrics } from '../interactions';
import { updateOtelJoinWithGroupLeft } from '../otel/util';
import { VAR_DATASOURCE_EXPR, VAR_GROUP_BY, VAR_OTEL_GROUP_LEFT } from '../shared';
diff --git a/public/app/features/trails/AutomaticMetricQueries/AutoQueryEngine.ts b/public/app/features/trails/AutomaticMetricQueries/AutoQueryEngine.ts
deleted file mode 100644
index 94d2287e126..00000000000
--- a/public/app/features/trails/AutomaticMetricQueries/AutoQueryEngine.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { getQueryGeneratorFor } from './query-generators/getQueryGeneratorFor';
-import { AutoQueryInfo } from './types';
-
-export function getAutoQueriesForMetric(metric: string): AutoQueryInfo {
- const metricParts = metric.split('_');
-
- const suffix = metricParts.at(-1);
-
- const generator = getQueryGeneratorFor(suffix);
-
- if (!generator) {
- throw new Error(`Unable to generate queries for metric "${metric}" due to issues with derived suffix "${suffix}"`);
- }
-
- return generator(metricParts);
-}
diff --git a/public/app/features/trails/AutomaticMetricQueries/graph-builders/heatmap.ts b/public/app/features/trails/AutomaticMetricQueries/graph-builders/heatmap.ts
deleted file mode 100644
index 252dba74701..00000000000
--- a/public/app/features/trails/AutomaticMetricQueries/graph-builders/heatmap.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PanelBuilders } from '@grafana/scenes';
-import { HeatmapColorMode } from 'app/plugins/panel/heatmap/types';
-
-import { CommonVizParams } from './types';
-
-export function heatmapGraphBuilder({ title, unit }: CommonVizParams) {
- return PanelBuilders.heatmap() //
- .setTitle(title)
- .setUnit(unit)
- .setOption('calculate', false)
- .setOption('color', {
- mode: HeatmapColorMode.Scheme,
- exponent: 0.5,
- scheme: 'Spectral',
- steps: 32,
- reverse: false,
- });
-}
diff --git a/public/app/features/trails/AutomaticMetricQueries/graph-builders/percentiles.ts b/public/app/features/trails/AutomaticMetricQueries/graph-builders/percentiles.ts
deleted file mode 100644
index 36578e56ffb..00000000000
--- a/public/app/features/trails/AutomaticMetricQueries/graph-builders/percentiles.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PanelBuilders } from '@grafana/scenes';
-import { SortOrder } from '@grafana/schema';
-import { TooltipDisplayMode } from '@grafana/ui';
-
-import { CommonVizParams } from './types';
-
-export function percentilesGraphBuilder({ title, unit }: CommonVizParams) {
- return PanelBuilders.timeseries()
- .setTitle(title)
- .setUnit(unit)
- .setCustomFieldConfig('fillOpacity', 9)
- .setOption('tooltip', { mode: TooltipDisplayMode.Multi, sort: SortOrder.Descending })
- .setOption('legend', { showLegend: false });
-}
diff --git a/public/app/features/trails/AutomaticMetricQueries/graph-builders/simple.ts b/public/app/features/trails/AutomaticMetricQueries/graph-builders/simple.ts
deleted file mode 100644
index d04766f23e4..00000000000
--- a/public/app/features/trails/AutomaticMetricQueries/graph-builders/simple.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PanelBuilders } from '@grafana/scenes';
-import { SortOrder } from '@grafana/schema';
-import { TooltipDisplayMode } from '@grafana/ui';
-
-import { CommonVizParams } from './types';
-
-export function simpleGraphBuilder({ title, unit }: CommonVizParams) {
- return PanelBuilders.timeseries() //
- .setTitle(title)
- .setUnit(unit)
- .setOption('legend', { showLegend: false })
- .setOption('tooltip', { mode: TooltipDisplayMode.Multi, sort: SortOrder.Descending })
- .setCustomFieldConfig('fillOpacity', 9);
-}
diff --git a/public/app/features/trails/AutomaticMetricQueries/graph-builders/types.ts b/public/app/features/trails/AutomaticMetricQueries/graph-builders/types.ts
deleted file mode 100644
index 5167b71f003..00000000000
--- a/public/app/features/trails/AutomaticMetricQueries/graph-builders/types.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export type CommonVizParams = {
- title: string;
- unit: string;
-};
diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/common/baseQuery.ts b/public/app/features/trails/AutomaticMetricQueries/query-generators/common/baseQuery.ts
deleted file mode 100644
index 5d0759e669d..00000000000
--- a/public/app/features/trails/AutomaticMetricQueries/query-generators/common/baseQuery.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { VAR_METRIC_EXPR, VAR_FILTERS_EXPR, VAR_OTEL_JOIN_QUERY_EXPR } from 'app/features/trails/shared';
-
-const GENERAL_BASE_QUERY = `${VAR_METRIC_EXPR}${VAR_FILTERS_EXPR}`;
-const GENERAL_RATE_BASE_QUERY = `rate(${GENERAL_BASE_QUERY}[$__rate_interval])`;
-
-export function getGeneralBaseQuery(rate: boolean) {
- return rate
- ? `${GENERAL_RATE_BASE_QUERY} ${VAR_OTEL_JOIN_QUERY_EXPR}`
- : `${GENERAL_BASE_QUERY} ${VAR_OTEL_JOIN_QUERY_EXPR}`;
-}
diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/default.test.ts b/public/app/features/trails/AutomaticMetricQueries/query-generators/default.test.ts
deleted file mode 100644
index cddc48925ec..00000000000
--- a/public/app/features/trails/AutomaticMetricQueries/query-generators/default.test.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import { VAR_GROUP_BY_EXP } from '../../shared';
-import { AutoQueryDef, AutoQueryInfo } from '../types';
-
-import { getGeneralBaseQuery } from './common/baseQuery';
-import { generateQueries } from './default';
-
-describe('generateQueries', () => {
- const agg = 'sum';
- const unit = 'mockunit';
-
- type QueryInfoKey = keyof AutoQueryInfo;
-
- function testRateIndependentAssertions(queryDef: AutoQueryDef, key: QueryInfoKey) {
- describe('regardless of rate', () => {
- test(`specified unit must be propagated`, () => expect(queryDef.unit).toBe(unit));
- test(`only one query is expected`, () => expect(queryDef.queries.length).toBe(1));
- const query = queryDef.queries[0];
- test(`specified agg function must be propagated in the query expr`, () => {
- const queryAggFunction = query.expr.split('(', 2)[0];
- expect(queryAggFunction).toBe(agg);
- });
- if (key === 'breakdown') {
- const expectedSuffix = `by(${VAR_GROUP_BY_EXP})`;
- test(`breakdown query must end with "${expectedSuffix}"`, () => {
- const suffix = query.expr.substring(query.expr.length - expectedSuffix.length);
- expect(suffix).toBe(expectedSuffix);
- });
- }
- });
- }
-
- function testRateSpecificAssertions(queryDef: AutoQueryDef, rate: boolean, key: QueryInfoKey) {
- const query = queryDef.queries[0];
- const firstParen = query.expr.indexOf('(');
- const expectedBaseQuery = getGeneralBaseQuery(rate);
- const detectedBaseQuery = query.expr.substring(firstParen + 1, firstParen + 1 + expectedBaseQuery.length);
-
- const inParentheses = rate ? 'overall per-second rate' : 'overall';
- const description = `\${metric} (${inParentheses})`;
-
- describe(`since rate is ${rate}`, () => {
- test(`base query must be "${expectedBaseQuery}"`, () => expect(detectedBaseQuery).toBe(expectedBaseQuery));
- if (key === 'main') {
- test(`main panel title contains expected description "${description}"`, () =>
- expect(queryDef.title).toContain(description));
- } else {
- test(`${key} panel title is just "\${metric}"`, () => expect(queryDef.title).toBe('${metric}'));
- test(`${key} panel title does not contain description "${description}"`, () =>
- expect(queryDef.title).not.toContain(description));
- }
-
- if (key === 'breakdown') {
- test(`breakdown query uses "{{\${groupby}}}" as legend`, () =>
- expect(query.legendFormat).toBe('{{${groupby}}}'));
- } else {
- test(`preview query uses "${description}" as legend`, () => expect(query.legendFormat).toBe(description));
- }
- });
- }
-
- for (const rate of [true, false]) {
- describe(`when rate is ${rate}`, () => {
- const queryInfo = generateQueries({ agg, unit, rate });
-
- let key: QueryInfoKey;
- for (key in queryInfo) {
- if (key !== 'variants') {
- const queryDef = queryInfo[key];
- describe(`queryInfo.${key}`, () => testRateIndependentAssertions(queryDef, key));
- describe(`queryInfo.${key}`, () => testRateSpecificAssertions(queryDef, rate, key));
- continue;
- }
-
- queryInfo[key].forEach((queryDef, index) => {
- describe(`queryInfo.${key}[${index}]`, () => testRateIndependentAssertions(queryDef, key));
- describe(`queryInfo.${key}[${index}]`, () => testRateSpecificAssertions(queryDef, rate, key));
- });
- }
- });
- }
-});
diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/default.ts b/public/app/features/trails/AutomaticMetricQueries/query-generators/default.ts
deleted file mode 100644
index b81feb30d62..00000000000
--- a/public/app/features/trails/AutomaticMetricQueries/query-generators/default.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from 'app/features/trails/shared';
-
-import { AutoQueryInfo } from '../types';
-import { getPerSecondRateUnit, getUnit } from '../units';
-
-import { getGeneralBaseQuery } from './common/baseQuery';
-import { generateCommonAutoQueryInfo } from './common/generator';
-
-/** These suffixes will set rate to true */
-const RATE_SUFFIXES = new Set(['count', 'total']);
-
-const UNSUPPORTED_SUFFIXES = new Set(['sum', 'bucket']);
-
-/** Non-default aggregation keyed by suffix */
-const SPECIFIC_AGGREGATIONS_FOR_SUFFIX: Record = {
- count: 'sum',
- total: 'sum',
-};
-
-function shouldCheckPreviousSuffixForUnit(suffix: string) {
- return suffix === 'total';
-}
-
-const aggLabels: Record = {
- avg: 'average',
- sum: 'overall',
-};
-
-function getAggLabel(agg: string) {
- return aggLabels[agg] || agg;
-}
-
-export type AutoQueryParameters = {
- agg: string;
- unit: string;
- rate: boolean;
-};
-
-export function generateQueries({ agg, rate, unit }: AutoQueryParameters): AutoQueryInfo {
- const baseQuery = getGeneralBaseQuery(rate);
-
- const aggregationDescription = rate ? `${getAggLabel(agg)} per-second rate` : `${getAggLabel(agg)}`;
-
- const description = `${VAR_METRIC_EXPR} (${aggregationDescription})`;
-
- const mainQueryExpr = `${agg}(${baseQuery})`;
- const breakdownQueryExpr = `${agg}(${baseQuery})by(${VAR_GROUP_BY_EXP})`;
-
- return generateCommonAutoQueryInfo({
- description,
- mainQueryExpr,
- breakdownQueryExpr,
- unit,
- });
-}
-
-export function createDefaultMetricQueryDefs(metricParts: string[]) {
- // Get the last part of the metric name
- const suffix = metricParts.at(-1);
-
- // If the suffix is null or is in the set of unsupported suffixes, throw an error because the metric should be delegated to a different generator (summary or histogram)
- if (suffix == null || UNSUPPORTED_SUFFIXES.has(suffix)) {
- throw new Error(`This function does not support a metric suffix of "${suffix}"`);
- }
-
- // Check if generating rate query and/or aggregation query
- const rate = RATE_SUFFIXES.has(suffix);
- const agg = SPECIFIC_AGGREGATIONS_FOR_SUFFIX[suffix] || 'avg';
-
- // Try to find the unit in the Prometheus metric name
- const unitSuffix = shouldCheckPreviousSuffixForUnit(suffix) ? metricParts.at(-2) : suffix;
-
- // Get the Grafana unit or Grafana rate unit
- const unit = rate ? getPerSecondRateUnit(unitSuffix) : getUnit(unitSuffix);
-
- const params = {
- agg,
- unit,
- rate,
- };
- return generateQueries(params);
-}
diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/getQueryGeneratorFor.ts b/public/app/features/trails/AutomaticMetricQueries/query-generators/getQueryGeneratorFor.ts
deleted file mode 100644
index e44bfc31e60..00000000000
--- a/public/app/features/trails/AutomaticMetricQueries/query-generators/getQueryGeneratorFor.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { AutoQueryInfo } from '../types';
-
-import { createDefaultMetricQueryDefs } from './default';
-import { createHistogramMetricQueryDefs } from './histogram';
-import { createSummaryMetricQueryDefs } from './summary';
-
-// TODO: when we have a known unit parameter, use that rather than having the generator functions infer from suffix
-export type MetricQueriesGenerator = (metricParts: string[]) => AutoQueryInfo;
-
-export function getQueryGeneratorFor(suffix?: string): MetricQueriesGenerator {
- if (suffix === 'sum') {
- return createSummaryMetricQueryDefs;
- }
-
- if (suffix === 'bucket') {
- return createHistogramMetricQueryDefs;
- }
-
- return createDefaultMetricQueryDefs;
-}
diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/histogram.ts b/public/app/features/trails/AutomaticMetricQueries/query-generators/histogram.ts
deleted file mode 100644
index 4fd492d5322..00000000000
--- a/public/app/features/trails/AutomaticMetricQueries/query-generators/histogram.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import { PromQuery } from '@grafana/prometheus';
-
-import { VAR_FILTERS_EXPR, VAR_GROUP_BY_EXP, VAR_METRIC_EXPR, VAR_OTEL_JOIN_QUERY_EXPR } from '../../shared';
-import { heatmapGraphBuilder } from '../graph-builders/heatmap';
-import { percentilesGraphBuilder } from '../graph-builders/percentiles';
-import { simpleGraphBuilder } from '../graph-builders/simple';
-import { AutoQueryDef } from '../types';
-import { getUnit } from '../units';
-
-export function createHistogramMetricQueryDefs(metricParts: string[]) {
- const title = `${VAR_METRIC_EXPR}`;
-
- const unitSuffix = metricParts.at(-2);
-
- const unit = getUnit(unitSuffix);
-
- const common = {
- title,
- unit,
- };
-
- const p50: AutoQueryDef = {
- ...common,
- variant: 'p50',
- queries: [percentileQuery(50)],
- vizBuilder: () => simpleGraphBuilder(p50),
- };
-
- const breakdown: AutoQueryDef = {
- ...common,
- variant: 'p50',
- queries: [percentileQuery(50, [VAR_GROUP_BY_EXP])],
- vizBuilder: () => simpleGraphBuilder(breakdown),
- };
-
- const percentiles: AutoQueryDef = {
- ...common,
- variant: 'percentiles',
- queries: [99, 90, 50].map((p) => percentileQuery(p)),
- vizBuilder: () => percentilesGraphBuilder(percentiles),
- };
-
- const heatmap: AutoQueryDef = {
- ...common,
- variant: 'heatmap',
- queries: [heatMapQuery()],
- vizBuilder: () => heatmapGraphBuilder(heatmap),
- };
-
- return { preview: heatmap, main: heatmap, variants: [percentiles, heatmap], breakdown: breakdown };
-}
-
-const BASE_QUERY = `rate(${VAR_METRIC_EXPR}${VAR_FILTERS_EXPR}[$__rate_interval])${VAR_OTEL_JOIN_QUERY_EXPR}`;
-
-function baseQuery(groupings: string[] = []) {
- const sumByList = ['le', ...groupings];
- return `sum by(${sumByList.join(', ')}) (${BASE_QUERY})`;
-}
-
-function heatMapQuery(groupings: string[] = []): PromQuery {
- return {
- refId: 'Heatmap',
- expr: baseQuery(groupings),
- format: 'heatmap',
- };
-}
-
-function percentileQuery(percentile: number, groupings: string[] = []) {
- const percent = percentile / 100;
-
- let legendFormat = `${percentile}th Percentile`;
-
- // For the breakdown view, show the label value variable we are grouping by
- if (groupings[0]) {
- legendFormat = `{{${groupings[0]}}}`;
- }
-
- return {
- refId: `Percentile${percentile}`,
- expr: `histogram_quantile(${percent}, ${baseQuery(groupings)})`,
- legendFormat,
- };
-}
diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/summary.ts b/public/app/features/trails/AutomaticMetricQueries/query-generators/summary.ts
deleted file mode 100644
index cd73d93db25..00000000000
--- a/public/app/features/trails/AutomaticMetricQueries/query-generators/summary.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from '../../shared';
-import { AutoQueryInfo } from '../types';
-import { getUnit } from '../units';
-
-import { getGeneralBaseQuery } from './common/baseQuery';
-import { generateCommonAutoQueryInfo } from './common/generator';
-
-export function createSummaryMetricQueryDefs(metricParts: string[]): AutoQueryInfo {
- const suffix = metricParts.at(-1);
- if (suffix !== 'sum') {
- throw new Error('createSummaryMetricQueryDefs is only to be used for metrics that end in "_sum"');
- }
-
- const unitSuffix = metricParts.at(-2);
- const unit = getUnit(unitSuffix);
-
- const rate = true;
- const baseQuery = getGeneralBaseQuery(rate);
-
- const subMetric = metricParts.slice(0, -1).join('_');
- const mainQueryExpr = createMeanExpr(`sum(${baseQuery})`);
- const breakdownQueryExpr = createMeanExpr(`sum(${baseQuery})by(${VAR_GROUP_BY_EXP})`);
-
- const operationDescription = `average`;
- const description = `${subMetric} (${operationDescription})`;
-
- function createMeanExpr(expr: string) {
- const numerator = expr.replace(VAR_METRIC_EXPR, `${subMetric}_sum`);
- const denominator = expr.replace(VAR_METRIC_EXPR, `${subMetric}_count`);
- return `${numerator}/${denominator}`;
- }
-
- return generateCommonAutoQueryInfo({
- description,
- mainQueryExpr,
- breakdownQueryExpr,
- unit,
- });
-}
diff --git a/public/app/features/trails/AutomaticMetricQueries/units.test.ts b/public/app/features/trails/AutomaticMetricQueries/units.test.ts
deleted file mode 100644
index 15f61988690..00000000000
--- a/public/app/features/trails/AutomaticMetricQueries/units.test.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { getUnitFromMetric } from './units';
-
-// Tests for units
-describe('getUnitFromMetric', () => {
- it('should return the last part of the metric if it is a valid unit', () => {
- expect(getUnitFromMetric('go_gc_gomemlimit_bytes')).toBe('bytes');
- expect(getUnitFromMetric('go_gc_duration_seconds')).toBe('seconds');
- });
-
- it('should return the second to last part of the metric if it is a valid unit', () => {
- expect(getUnitFromMetric('go_gc_heap_allocs_by_size_bytes_count')).toBe('bytes');
- expect(getUnitFromMetric('go_cpu_classes_gc_mark_assist_cpu_seconds_total')).toBe('seconds');
- });
-
- it('should return null if no valid unit is found', () => {
- expect(getUnitFromMetric('ALERTS')).toBe(null);
- });
-});
diff --git a/public/app/features/trails/AutomaticMetricQueries/units.ts b/public/app/features/trails/AutomaticMetricQueries/units.ts
deleted file mode 100644
index e025d41af2d..00000000000
--- a/public/app/features/trails/AutomaticMetricQueries/units.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-const DEFAULT_UNIT = 'short';
-
-// Get unit from metric name (e.g. "go_gc_duration_seconds" -> "seconds")
-export function getUnitFromMetric(metric: string) {
- const metricParts = metric.split('_');
- const suffix = metricParts.at(-1) ?? '';
- const secondToLastSuffix = metricParts.at(-2) ?? '';
- if (UNIT_LIST.includes(suffix)) {
- return suffix;
- } else if (UNIT_LIST.includes(secondToLastSuffix)) {
- return secondToLastSuffix;
- } else {
- return null;
- }
-}
-
-// Get Grafana unit for a panel (e.g. "go_gc_duration_seconds" -> "s")
-export function getUnit(metricPart: string | undefined) {
- return (metricPart && UNIT_MAP[metricPart]) || DEFAULT_UNIT;
-}
-
-const UNIT_MAP: Record = {
- bytes: 'bytes',
- seconds: 's',
-};
-
-const UNIT_LIST = ['bytes', 'seconds'];
-
-const RATE_UNIT_MAP: Record = {
- bytes: 'Bps', // bytes per second
- seconds: 'short', // seconds per second is unitless -- this may indicate a count of some resource that is active
-};
-
-const DEFAULT_RATE_UNIT = 'cps'; // Count per second
-
-export function getPerSecondRateUnit(metricPart: string | undefined) {
- return (metricPart && RATE_UNIT_MAP[metricPart]) || DEFAULT_RATE_UNIT;
-}
diff --git a/public/app/features/trails/Breakdown/LabelBreakdownScene.tsx b/public/app/features/trails/Breakdown/LabelBreakdownScene.tsx
index 0072bbafb85..1ab14b84aac 100644
--- a/public/app/features/trails/Breakdown/LabelBreakdownScene.tsx
+++ b/public/app/features/trails/Breakdown/LabelBreakdownScene.tsx
@@ -1,7 +1,7 @@
import init from '@bsull/augurs/outlier';
import { css } from '@emotion/css';
import { isNumber, max, min, throttle } from 'lodash';
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
import { DataFrame, FieldType, GrafanaTheme2, PanelData, SelectableValue } from '@grafana/data';
import { config } from '@grafana/runtime';
@@ -26,16 +26,16 @@ import {
VizPanel,
} from '@grafana/scenes';
import { DataQuery, SortOrder, TooltipDisplayMode } from '@grafana/schema';
-import { Button, Field, LoadingPlaceholder, useStyles2 } from '@grafana/ui';
+import { Alert, Button, Field, LoadingPlaceholder, useStyles2 } from '@grafana/ui';
import { Trans } from 'app/core/internationalization';
-import { getAutoQueriesForMetric } from '../AutomaticMetricQueries/AutoQueryEngine';
-import { AutoQueryDef } from '../AutomaticMetricQueries/types';
import { BreakdownLabelSelector } from '../BreakdownLabelSelector';
import { DataTrail } from '../DataTrail';
import { MetricScene } from '../MetricScene';
import { AddToExplorationButton } from '../MetricSelect/AddToExplorationsButton';
import { StatusWrapper } from '../StatusWrapper';
+import { getAutoQueriesForMetric } from '../autoQuery/getAutoQueriesForMetric';
+import { AutoQueryDef } from '../autoQuery/types';
import { reportExploreMetrics } from '../interactions';
import { updateOtelJoinWithGroupLeft } from '../otel/util';
import { getSortByPreference } from '../services/store';
@@ -47,6 +47,7 @@ import {
VAR_FILTERS,
VAR_GROUP_BY,
VAR_GROUP_BY_EXP,
+ VAR_MISSING_OTEL_TARGETS,
VAR_OTEL_GROUP_LEFT,
} from '../shared';
import { getColorByIndex, getTrailFor } from '../utils';
@@ -338,6 +339,14 @@ export class LabelBreakdownScene extends SceneObjectBase option.value !== ALL_VARIABLE_VALUE).unshift(all);
}
+ const [dismissOtelWarning, updateDismissOtelWarning] = useState(false);
+ const missingOtelTargets = sceneGraph.lookupVariable(VAR_MISSING_OTEL_TARGETS, trail)?.getValue();
+ if (missingOtelTargets && !dismissOtelWarning) {
+ reportExploreMetrics('missing_otel_labels_by_truncating_job_and_instance', {
+ metric: trail.state.metric,
+ });
+ }
+
useEffect(() => {
if (useOtelExperience) {
// this will update the group left variable
@@ -369,6 +378,22 @@ export class LabelBreakdownScene extends SceneObjectBase
)}
+ {missingOtelTargets && !dismissOtelWarning && (
+ updateDismissOtelWarning(true)}
+ className={styles.truncatedOTelResources}
+ >
+
+ This metric has too many job and instance label values to call the Prometheus label_values endpoint with
+ the match[] parameter. These label values are used to join the metric with target_info, which contains
+ the resource attributes. Please include more resource attributes filters.
+
+
+ )}
+
{body && }
@@ -400,6 +425,10 @@ function getStyles(theme: GrafanaTheme2) {
gap: theme.spacing(2),
justifyContent: 'space-between',
}),
+ truncatedOTelResources: css({
+ minWidth: '30vw',
+ flexGrow: 0,
+ }),
};
}
diff --git a/public/app/features/trails/DataTrail.tsx b/public/app/features/trails/DataTrail.tsx
index 0101498a33c..547de8ee9c0 100644
--- a/public/app/features/trails/DataTrail.tsx
+++ b/public/app/features/trails/DataTrail.tsx
@@ -65,6 +65,7 @@ import {
VAR_DATASOURCE,
VAR_DATASOURCE_EXPR,
VAR_FILTERS,
+ VAR_MISSING_OTEL_TARGETS,
VAR_OTEL_DEPLOYMENT_ENV,
VAR_OTEL_GROUP_LEFT,
VAR_OTEL_JOIN_QUERY,
@@ -732,6 +733,11 @@ function getVariableSet(
value: undefined,
hide: VariableHide.hideVariable,
}),
+ new ConstantVariable({
+ name: VAR_MISSING_OTEL_TARGETS,
+ hide: VariableHide.hideVariable,
+ value: false,
+ }),
],
});
}
diff --git a/public/app/features/trails/DataTrailBookmarks.tsx b/public/app/features/trails/DataTrailBookmarks.tsx
index 4793110cf03..edf449c9b1b 100644
--- a/public/app/features/trails/DataTrailBookmarks.tsx
+++ b/public/app/features/trails/DataTrailBookmarks.tsx
@@ -84,12 +84,8 @@ function getStyles(theme: GrafanaTheme2) {
header: css({
color: theme.colors.text.primary,
textAlign: 'center',
- /* H4 */
- fontFamily: 'Inter',
fontSize: '18px',
- fontStyle: 'normal',
- fontWeight: '400',
- lineHeight: '22px' /* 122.222% */,
+ lineHeight: '22px',
letterSpacing: '0.045px',
}),
horizontalLine: css({
diff --git a/public/app/features/trails/DataTrailCard.tsx b/public/app/features/trails/DataTrailCard.tsx
index b64afe1ed71..75967191964 100644
--- a/public/app/features/trails/DataTrailCard.tsx
+++ b/public/app/features/trails/DataTrailCard.tsx
@@ -59,7 +59,7 @@ export function DataTrailCard(props: Props) {
const { filters, metric, createdAt } = values;
return (
-
+
{truncateValue('', getMetricName(metric), 39)}
@@ -93,34 +93,18 @@ export function DataTrailCard(props: Props) {
{createdAt && dateTimeFormat(createdAt, { format: 'YYYY-MM-DD' })}
-
+
);
}
export function getStyles(theme: GrafanaTheme2) {
return {
- metricLabel: css({
- display: 'inline',
- color: theme.colors.text.primary,
- fontFamily: 'Inter',
- fontSize: '14px',
- fontStyle: 'normal',
- fontWeight: 400,
- }),
metricValue: css({
display: 'inline',
color: theme.colors.text.primary,
- fontFamily: 'Inter',
- fontSize: '14px',
- fontStyle: 'normal',
fontWeight: 500,
wordBreak: 'break-all',
}),
- tag: css({
- maxWidth: '260px',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- }),
card: css({
position: 'relative',
width: '318px',
@@ -159,19 +143,14 @@ export function getStyles(theme: GrafanaTheme2) {
primaryFont: css({
display: 'inline',
color: theme.colors.text.primary,
- fontFamily: 'Inter',
fontSize: '12px',
- fontStyle: 'normal',
fontWeight: '500',
- lineHeight: '18px' /* 150% */,
letterSpacing: '0.018px',
}),
secondaryFont: css({
display: 'inline',
color: theme.colors.text.secondary,
- fontFamily: 'Inter',
fontSize: '12px',
- fontStyle: 'normal',
fontWeight: '400',
lineHeight: '18px' /* 150% */,
letterSpacing: '0.018px',
@@ -181,9 +160,5 @@ export function getStyles(theme: GrafanaTheme2) {
bottom: theme.spacing(1),
right: theme.spacing(1),
}),
- wordwrap: css({
- overflow: 'hidden',
- overflowWrap: 'anywhere',
- }),
};
}
diff --git a/public/app/features/trails/DataTrailsApp.tsx b/public/app/features/trails/DataTrailsApp.tsx
index f7f0112e456..c47be4d1b0b 100644
--- a/public/app/features/trails/DataTrailsApp.tsx
+++ b/public/app/features/trails/DataTrailsApp.tsx
@@ -155,6 +155,7 @@ export function getDataTrailsApp() {
const getStyles = () => ({
topNavContainer: css({
width: '100%',
+ height: '100%',
display: 'flex',
flexDirection: 'row',
justifyItems: 'flex-start',
diff --git a/public/app/features/trails/DataTrailsHome.tsx b/public/app/features/trails/DataTrailsHome.tsx
index 5c7ee62b185..c0c4d6d5995 100644
--- a/public/app/features/trails/DataTrailsHome.tsx
+++ b/public/app/features/trails/DataTrailsHome.tsx
@@ -57,8 +57,8 @@ export class DataTrailsHome extends SceneObjectBase {
};
return (
-
-
+
+
{theme.isDark ? : }
@@ -87,10 +87,10 @@ export class DataTrailsHome extends SceneObjectBase {
-
+
-
+
);
};
}
diff --git a/public/app/features/trails/DataTrailsRecentMetrics.tsx b/public/app/features/trails/DataTrailsRecentMetrics.tsx
index cae68d8060a..921edcbd92e 100644
--- a/public/app/features/trails/DataTrailsRecentMetrics.tsx
+++ b/public/app/features/trails/DataTrailsRecentMetrics.tsx
@@ -64,12 +64,8 @@ function getStyles(theme: GrafanaTheme2) {
header: css({
color: theme.colors.text.primary,
textAlign: 'center',
- /* H4 */
- fontFamily: 'Inter',
fontSize: '18px',
- fontStyle: 'normal',
fontWeight: '400',
- lineHeight: '22px' /* 122.222% */,
letterSpacing: '0.045px',
}),
trailList: css({
diff --git a/public/app/features/trails/MetricGraphScene.tsx b/public/app/features/trails/MetricGraphScene.tsx
index 69003e9edc5..659d1aaf6c5 100644
--- a/public/app/features/trails/MetricGraphScene.tsx
+++ b/public/app/features/trails/MetricGraphScene.tsx
@@ -13,8 +13,8 @@ import {
} from '@grafana/scenes';
import { useStyles2 } from '@grafana/ui';
-import { AutoVizPanel } from './AutomaticMetricQueries/AutoVizPanel';
import { MetricActionBar } from './MetricScene';
+import { AutoVizPanel } from './autoQuery/components/AutoVizPanel';
import { getTrailSettings } from './utils';
export const MAIN_PANEL_MIN_HEIGHT = 280;
diff --git a/public/app/features/trails/MetricScene.tsx b/public/app/features/trails/MetricScene.tsx
index 13b3c820cf5..0e3cb6b48f7 100644
--- a/public/app/features/trails/MetricScene.tsx
+++ b/public/app/features/trails/MetricScene.tsx
@@ -18,13 +18,13 @@ import { getExploreUrl } from '../../core/utils/explore';
import { buildMetricOverviewScene } from './ActionTabs/MetricOverviewScene';
import { buildRelatedMetricsScene } from './ActionTabs/RelatedMetricsScene';
-import { getAutoQueriesForMetric } from './AutomaticMetricQueries/AutoQueryEngine';
-import { AutoQueryDef, AutoQueryInfo } from './AutomaticMetricQueries/types';
import { buildLabelBreakdownActionScene } from './Breakdown/LabelBreakdownScene';
import { MAIN_PANEL_MAX_HEIGHT, MAIN_PANEL_MIN_HEIGHT, MetricGraphScene } from './MetricGraphScene';
import { buildRelatedLogsScene } from './RelatedLogs/RelatedLogsScene';
import { ShareTrailButton } from './ShareTrailButton';
import { useBookmarkState } from './TrailStore/useBookmarkState';
+import { getAutoQueriesForMetric } from './autoQuery/getAutoQueriesForMetric';
+import { AutoQueryDef, AutoQueryInfo } from './autoQuery/types';
import { reportExploreMetrics } from './interactions';
import {
ActionViewDefinition,
diff --git a/public/app/features/trails/MetricSelect/previewPanel.ts b/public/app/features/trails/MetricSelect/previewPanel.ts
index 791c57ce949..dd5feb9391b 100644
--- a/public/app/features/trails/MetricSelect/previewPanel.ts
+++ b/public/app/features/trails/MetricSelect/previewPanel.ts
@@ -1,7 +1,7 @@
import { PromQuery } from '@grafana/prometheus';
import { SceneCSSGridItem, SceneQueryRunner, SceneVariableSet } from '@grafana/scenes';
-import { getAutoQueriesForMetric } from '../AutomaticMetricQueries/AutoQueryEngine';
+import { getAutoQueriesForMetric } from '../autoQuery/getAutoQueriesForMetric';
import { getVariablesWithMetricConstant, MDP_METRIC_PREVIEW, trailDS } from '../shared';
import { getColorByIndex } from '../utils';
diff --git a/public/app/features/trails/AutomaticMetricQueries/AutoVizPanel.tsx b/public/app/features/trails/autoQuery/components/AutoVizPanel.tsx
similarity index 87%
rename from public/app/features/trails/AutomaticMetricQueries/AutoVizPanel.tsx
rename to public/app/features/trails/autoQuery/components/AutoVizPanel.tsx
index 4912922b8e4..16774e2573c 100644
--- a/public/app/features/trails/AutomaticMetricQueries/AutoVizPanel.tsx
+++ b/public/app/features/trails/autoQuery/components/AutoVizPanel.tsx
@@ -1,11 +1,11 @@
import { SceneObjectState, SceneObjectBase, SceneComponentProps, VizPanel, SceneQueryRunner } from '@grafana/scenes';
-import { AddToExplorationButton } from '../MetricSelect/AddToExplorationsButton';
-import { MDP_METRIC_OVERVIEW, trailDS } from '../shared';
-import { getMetricSceneFor } from '../utils';
+import { AddToExplorationButton } from '../../MetricSelect/AddToExplorationsButton';
+import { MDP_METRIC_OVERVIEW, trailDS } from '../../shared';
+import { getMetricSceneFor } from '../../utils';
+import { AutoQueryDef } from '../types';
import { AutoVizPanelQuerySelector } from './AutoVizPanelQuerySelector';
-import { AutoQueryDef } from './types';
export interface AutoVizPanelState extends SceneObjectState {
panel?: VizPanel;
diff --git a/public/app/features/trails/AutomaticMetricQueries/AutoVizPanelQuerySelector.tsx b/public/app/features/trails/autoQuery/components/AutoVizPanelQuerySelector.tsx
similarity index 92%
rename from public/app/features/trails/AutomaticMetricQueries/AutoVizPanelQuerySelector.tsx
rename to public/app/features/trails/autoQuery/components/AutoVizPanelQuerySelector.tsx
index 4b31d552681..4a30036c3fa 100644
--- a/public/app/features/trails/AutomaticMetricQueries/AutoVizPanelQuerySelector.tsx
+++ b/public/app/features/trails/autoQuery/components/AutoVizPanelQuerySelector.tsx
@@ -1,9 +1,8 @@
import { SceneObjectState, SceneObjectBase, SceneComponentProps } from '@grafana/scenes';
import { RadioButtonGroup } from '@grafana/ui';
-import { getMetricSceneFor } from '../utils';
-
-import { AutoQueryDef } from './types';
+import { getMetricSceneFor } from '../../utils';
+import { AutoQueryDef } from '../types';
interface QuerySelectorState extends SceneObjectState {
queryDef: AutoQueryDef;
diff --git a/public/app/features/trails/AutomaticMetricQueries/AutoQueryEngine.test.ts b/public/app/features/trails/autoQuery/getAutoQueriesForMetric.test.ts
similarity index 83%
rename from public/app/features/trails/AutomaticMetricQueries/AutoQueryEngine.test.ts
rename to public/app/features/trails/autoQuery/getAutoQueriesForMetric.test.ts
index f9bda26a4aa..d110f539f54 100644
--- a/public/app/features/trails/AutomaticMetricQueries/AutoQueryEngine.test.ts
+++ b/public/app/features/trails/autoQuery/getAutoQueriesForMetric.test.ts
@@ -1,4 +1,7 @@
-import { getAutoQueriesForMetric } from './AutoQueryEngine';
+import { VAR_FILTERS_EXPR, VAR_METRIC_EXPR, VAR_OTEL_JOIN_QUERY_EXPR } from '../shared';
+
+import { getAutoQueriesForMetric } from './getAutoQueriesForMetric';
+import { generateBaseQuery } from './queryGenerators/baseQuery';
function expandExpr(shortenedExpr: string) {
return shortenedExpr.replace('...', '${metric}{${filters}}');
@@ -105,7 +108,7 @@ describe('getAutoQueriesForMetric', () => {
test('preview panel has heatmap query', () => {
const [{ expr }] = result.preview.queries;
- const expected = 'sum by(le) (rate(${metric}{${filters}}[$__rate_interval])${otel_join_query})';
+ const expected = 'sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query})';
expect(expr).toBe(expected);
});
@@ -161,9 +164,9 @@ describe('getAutoQueriesForMetric', () => {
],
// ***WE DEFAULT TO HEATMAP HERE
// Bucket
- ['PREFIX_bucket', 'sum by(le) (rate(...[$__rate_interval])${otel_join_query})', 'short', 1],
- ['PREFIX_seconds_bucket', 'sum by(le) (rate(...[$__rate_interval])${otel_join_query})', 's', 1],
- ['PREFIX_bytes_bucket', 'sum by(le) (rate(...[$__rate_interval])${otel_join_query})', 'bytes', 1],
+ ['PREFIX_bucket', 'sum by(le) (rate(...[$__rate_interval]) ${otel_join_query})', 'short', 1],
+ ['PREFIX_seconds_bucket', 'sum by(le) (rate(...[$__rate_interval]) ${otel_join_query})', 's', 1],
+ ['PREFIX_bytes_bucket', 'sum by(le) (rate(...[$__rate_interval]) ${otel_join_query})', 'bytes', 1],
])('Given metric %p expect %p with unit %p', (metric, expr, unit, queryCount) => {
const result = getAutoQueriesForMetric(metric);
@@ -202,9 +205,9 @@ describe('getAutoQueriesForMetric', () => {
'bytes',
],
// Bucket
- ['PREFIX_bucket', 'sum by(le) (rate(...[$__rate_interval])${otel_join_query})', 'short'],
- ['PREFIX_seconds_bucket', 'sum by(le) (rate(...[$__rate_interval])${otel_join_query})', 's'],
- ['PREFIX_bytes_bucket', 'sum by(le) (rate(...[$__rate_interval])${otel_join_query})', 'bytes'],
+ ['PREFIX_bucket', 'sum by(le) (rate(...[$__rate_interval]) ${otel_join_query})', 'short'],
+ ['PREFIX_seconds_bucket', 'sum by(le) (rate(...[$__rate_interval]) ${otel_join_query})', 's'],
+ ['PREFIX_bytes_bucket', 'sum by(le) (rate(...[$__rate_interval]) ${otel_join_query})', 'bytes'],
])('Given metric %p expect %p with unit %p', (metric, expr, unit) => {
const result = getAutoQueriesForMetric(metric);
@@ -247,17 +250,17 @@ describe('getAutoQueriesForMetric', () => {
// Bucket
[
'PREFIX_bucket',
- 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate(...[$__rate_interval])${otel_join_query}))',
+ 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate(...[$__rate_interval]) ${otel_join_query}))',
'short',
],
[
'PREFIX_seconds_bucket',
- 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate(...[$__rate_interval])${otel_join_query}))',
+ 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate(...[$__rate_interval]) ${otel_join_query}))',
's',
],
[
'PREFIX_bytes_bucket',
- 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate(...[$__rate_interval])${otel_join_query}))',
+ 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate(...[$__rate_interval]) ${otel_join_query}))',
'bytes',
],
])('Given metric %p expect %p with unit %p', (metric, expr, unit) => {
@@ -292,15 +295,15 @@ describe('getAutoQueriesForMetric', () => {
variant: 'percentiles',
unit: 'short',
exprs: [
- 'histogram_quantile(0.99, sum by(le) (rate(${metric}{${filters}}[$__rate_interval])${otel_join_query}))',
- 'histogram_quantile(0.9, sum by(le) (rate(${metric}{${filters}}[$__rate_interval])${otel_join_query}))',
- 'histogram_quantile(0.5, sum by(le) (rate(${metric}{${filters}}[$__rate_interval])${otel_join_query}))',
+ 'histogram_quantile(0.99, sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}))',
+ 'histogram_quantile(0.9, sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}))',
+ 'histogram_quantile(0.5, sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}))',
],
},
{
variant: 'heatmap',
unit: 'short',
- exprs: ['sum by(le) (rate(${metric}{${filters}}[$__rate_interval])${otel_join_query})'],
+ exprs: ['sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query})'],
},
],
],
@@ -311,15 +314,15 @@ describe('getAutoQueriesForMetric', () => {
variant: 'percentiles',
unit: 's',
exprs: [
- 'histogram_quantile(0.99, sum by(le) (rate(${metric}{${filters}}[$__rate_interval])${otel_join_query}))',
- 'histogram_quantile(0.9, sum by(le) (rate(${metric}{${filters}}[$__rate_interval])${otel_join_query}))',
- 'histogram_quantile(0.5, sum by(le) (rate(${metric}{${filters}}[$__rate_interval])${otel_join_query}))',
+ 'histogram_quantile(0.99, sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}))',
+ 'histogram_quantile(0.9, sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}))',
+ 'histogram_quantile(0.5, sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}))',
],
},
{
variant: 'heatmap',
unit: 's',
- exprs: ['sum by(le) (rate(${metric}{${filters}}[$__rate_interval])${otel_join_query})'],
+ exprs: ['sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query})'],
},
],
],
@@ -330,15 +333,15 @@ describe('getAutoQueriesForMetric', () => {
variant: 'percentiles',
unit: 'bytes',
exprs: [
- 'histogram_quantile(0.99, sum by(le) (rate(${metric}{${filters}}[$__rate_interval])${otel_join_query}))',
- 'histogram_quantile(0.9, sum by(le) (rate(${metric}{${filters}}[$__rate_interval])${otel_join_query}))',
- 'histogram_quantile(0.5, sum by(le) (rate(${metric}{${filters}}[$__rate_interval])${otel_join_query}))',
+ 'histogram_quantile(0.99, sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}))',
+ 'histogram_quantile(0.9, sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}))',
+ 'histogram_quantile(0.5, sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}))',
],
},
{
variant: 'heatmap',
unit: 'bytes',
- exprs: ['sum by(le) (rate(${metric}{${filters}}[$__rate_interval])${otel_join_query})'],
+ exprs: ['sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query})'],
},
],
],
@@ -371,3 +374,53 @@ describe('getAutoQueriesForMetric', () => {
);
});
});
+
+describe('generateBaseQuery', () => {
+ it('should generate a non-rate non-UTF8 base query', () => {
+ expect(generateBaseQuery({ isRateQuery: false, isUtf8Metric: false })).toBe(
+ `${VAR_METRIC_EXPR}{${VAR_FILTERS_EXPR}} ${VAR_OTEL_JOIN_QUERY_EXPR}`
+ );
+ });
+
+ it('should generate a rate non-UTF8 base query', () => {
+ expect(generateBaseQuery({ isRateQuery: true, isUtf8Metric: false })).toBe(
+ `rate(${VAR_METRIC_EXPR}{${VAR_FILTERS_EXPR}}[$__rate_interval]) ${VAR_OTEL_JOIN_QUERY_EXPR}`
+ );
+ });
+
+ it('should generate a non-rate UTF8 base query', () => {
+ expect(generateBaseQuery({ isRateQuery: false, isUtf8Metric: true })).toBe(
+ `{"${VAR_METRIC_EXPR}", ${VAR_FILTERS_EXPR}} ${VAR_OTEL_JOIN_QUERY_EXPR}`
+ );
+ });
+
+ it('should generate a rate UTF8 base query', () => {
+ expect(generateBaseQuery({ isRateQuery: true, isUtf8Metric: true })).toBe(
+ `rate({"${VAR_METRIC_EXPR}", ${VAR_FILTERS_EXPR}}[$__rate_interval]) ${VAR_OTEL_JOIN_QUERY_EXPR}`
+ );
+ });
+
+ it('should generate a grouped non-UTF8 rate query', () => {
+ expect(
+ generateBaseQuery({
+ isRateQuery: true,
+ isUtf8Metric: false,
+ groupings: ['le', 'job'],
+ })
+ ).toBe(
+ `sum by(le, job) (rate(${VAR_METRIC_EXPR}{${VAR_FILTERS_EXPR}}[$__rate_interval]) ${VAR_OTEL_JOIN_QUERY_EXPR})`
+ );
+ });
+
+ it('should generate a grouped UTF8 rate query', () => {
+ expect(
+ generateBaseQuery({
+ isRateQuery: true,
+ isUtf8Metric: true,
+ groupings: ['le', 'instance'],
+ })
+ ).toBe(
+ `sum by(le, instance) (rate({"${VAR_METRIC_EXPR}", ${VAR_FILTERS_EXPR}}[$__rate_interval]) ${VAR_OTEL_JOIN_QUERY_EXPR})`
+ );
+ });
+});
diff --git a/public/app/features/trails/autoQuery/getAutoQueriesForMetric.ts b/public/app/features/trails/autoQuery/getAutoQueriesForMetric.ts
new file mode 100644
index 00000000000..aa09aeb8486
--- /dev/null
+++ b/public/app/features/trails/autoQuery/getAutoQueriesForMetric.ts
@@ -0,0 +1,36 @@
+import { createDefaultMetricQueryDefs } from './queryGenerators/default';
+import { createHistogramMetricQueryDefs } from './queryGenerators/histogram';
+import { createSummaryMetricQueryDefs } from './queryGenerators/summary';
+import { AutoQueryContext, AutoQueryInfo } from './types';
+import { getUnit } from './units';
+
+export function getAutoQueriesForMetric(metric: string): AutoQueryInfo {
+ const isUtf8Metric = false;
+ const metricParts = metric.split('_');
+ const suffix = metricParts.at(-1);
+
+ // If the suffix is null or is in the set of unsupported suffixes, throw an error because the metric should be delegated to a different generator (summary or histogram)
+ if (suffix == null) {
+ throw new Error(`This function does not support a metric suffix of "${suffix}"`);
+ }
+
+ const unitSuffix = metricParts.at(-2);
+ const unit = getUnit(unitSuffix);
+ const ctx: AutoQueryContext = {
+ metricParts,
+ isUtf8Metric,
+ suffix,
+ unitSuffix,
+ unit,
+ };
+
+ if (suffix === 'sum') {
+ return createSummaryMetricQueryDefs(ctx);
+ }
+
+ if (suffix === 'bucket') {
+ return createHistogramMetricQueryDefs(ctx);
+ }
+
+ return createDefaultMetricQueryDefs(ctx);
+}
diff --git a/public/app/features/trails/autoQuery/graphBuilders.ts b/public/app/features/trails/autoQuery/graphBuilders.ts
new file mode 100644
index 00000000000..0843499b245
--- /dev/null
+++ b/public/app/features/trails/autoQuery/graphBuilders.ts
@@ -0,0 +1,41 @@
+import { PanelBuilders } from '@grafana/scenes';
+import { SortOrder, TooltipDisplayMode } from '@grafana/schema/dist/esm/index';
+
+import { HeatmapColorMode } from '../../../plugins/panel/heatmap/panelcfg.gen';
+
+export type CommonVizParams = {
+ title: string;
+ unit: string;
+};
+
+export function simpleGraphBuilder({ title, unit }: CommonVizParams) {
+ return PanelBuilders.timeseries() //
+ .setTitle(title)
+ .setUnit(unit)
+ .setOption('legend', { showLegend: false })
+ .setOption('tooltip', { mode: TooltipDisplayMode.Multi, sort: SortOrder.Descending })
+ .setCustomFieldConfig('fillOpacity', 9);
+}
+
+export function heatmapGraphBuilder({ title, unit }: CommonVizParams) {
+ return PanelBuilders.heatmap() //
+ .setTitle(title)
+ .setUnit(unit)
+ .setOption('calculate', false)
+ .setOption('color', {
+ mode: HeatmapColorMode.Scheme,
+ exponent: 0.5,
+ scheme: 'Spectral',
+ steps: 32,
+ reverse: false,
+ });
+}
+
+export function percentilesGraphBuilder({ title, unit }: CommonVizParams) {
+ return PanelBuilders.timeseries()
+ .setTitle(title)
+ .setUnit(unit)
+ .setCustomFieldConfig('fillOpacity', 9)
+ .setOption('tooltip', { mode: TooltipDisplayMode.Multi, sort: SortOrder.Descending })
+ .setOption('legend', { showLegend: false });
+}
diff --git a/public/app/features/trails/autoQuery/queryGenerators/baseQuery.test.ts b/public/app/features/trails/autoQuery/queryGenerators/baseQuery.test.ts
new file mode 100644
index 00000000000..dd7ae9a2fe5
--- /dev/null
+++ b/public/app/features/trails/autoQuery/queryGenerators/baseQuery.test.ts
@@ -0,0 +1,43 @@
+import { generateBaseQuery } from './baseQuery';
+
+describe('generateBaseQuery', () => {
+ it('should return base query without rate and groupings', () => {
+ const result = generateBaseQuery({});
+ expect(result).toBe('${metric}{${filters}} ${otel_join_query}');
+ });
+
+ it('should return rate base query without groupings', () => {
+ const result = generateBaseQuery({ isRateQuery: true });
+ expect(result).toBe('rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}');
+ });
+
+ it('should return base query with groupings', () => {
+ const result = generateBaseQuery({ groupings: ['job', 'instance'] });
+ expect(result).toBe('sum by(job, instance) (${metric}{${filters}} ${otel_join_query})');
+ });
+
+ it('should return rate base query with groupings', () => {
+ const result = generateBaseQuery({ isRateQuery: true, groupings: ['job', 'instance'] });
+ expect(result).toBe('sum by(job, instance) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query})');
+ });
+
+ it('should return UTF-8 base query without rate and groupings', () => {
+ const result = generateBaseQuery({ isUtf8Metric: true });
+ expect(result).toBe('{"${metric}", ${filters}} ${otel_join_query}');
+ });
+
+ it('should return UTF-8 rate base query without groupings', () => {
+ const result = generateBaseQuery({ isRateQuery: true, isUtf8Metric: true });
+ expect(result).toBe('rate({"${metric}", ${filters}}[$__rate_interval]) ${otel_join_query}');
+ });
+
+ it('should return UTF-8 base query with groupings', () => {
+ const result = generateBaseQuery({ isUtf8Metric: true, groupings: ['job', 'instance'] });
+ expect(result).toBe('sum by(job, instance) ({"${metric}", ${filters}} ${otel_join_query})');
+ });
+
+ it('should return UTF-8 rate base query with groupings', () => {
+ const result = generateBaseQuery({ isRateQuery: true, isUtf8Metric: true, groupings: ['job', 'instance'] });
+ expect(result).toBe('sum by(job, instance) (rate({"${metric}", ${filters}}[$__rate_interval]) ${otel_join_query})');
+ });
+});
diff --git a/public/app/features/trails/autoQuery/queryGenerators/baseQuery.ts b/public/app/features/trails/autoQuery/queryGenerators/baseQuery.ts
new file mode 100644
index 00000000000..c57d81b4908
--- /dev/null
+++ b/public/app/features/trails/autoQuery/queryGenerators/baseQuery.ts
@@ -0,0 +1,37 @@
+import { VAR_FILTERS_EXPR, VAR_METRIC_EXPR, VAR_OTEL_JOIN_QUERY_EXPR } from '../../shared';
+
+// For usual non-utf8-metrics we use filters in the curly braces
+// metric_name{filter_label="filter_value"}
+const BASE_QUERY_TEMPLATE = `${VAR_METRIC_EXPR}{${VAR_FILTERS_EXPR}}`;
+const RATE_BASE_QUERY_TEMPLATE = `rate(${BASE_QUERY_TEMPLATE}[$__rate_interval])`;
+
+// For utf8 metrics we need to put the metric name inside curly braces with filters
+// {"utf8.metric", filter_label="filter_val"}
+const BASE_QUERY_UTF8_METRIC_TEMPLATE = `{"${VAR_METRIC_EXPR}", ${VAR_FILTERS_EXPR}}`;
+const RATE_BASE_QUERY_UTF8_METRIC_TEMPLATE = `rate(${BASE_QUERY_UTF8_METRIC_TEMPLATE}[$__rate_interval])`;
+
+export function generateBaseQuery({
+ isRateQuery = false,
+ groupings = [],
+ isUtf8Metric = false,
+}: {
+ isRateQuery?: boolean;
+ groupings?: string[];
+ isUtf8Metric?: boolean;
+}): string {
+ // Determine base query template
+ const baseQuery = isUtf8Metric
+ ? isRateQuery
+ ? RATE_BASE_QUERY_UTF8_METRIC_TEMPLATE
+ : BASE_QUERY_UTF8_METRIC_TEMPLATE
+ : isRateQuery
+ ? RATE_BASE_QUERY_TEMPLATE
+ : BASE_QUERY_TEMPLATE;
+
+ // Apply groupings (e.g., `sum by(le, instance)`)
+ if (groupings.length > 0) {
+ return `sum by(${groupings.join(', ')}) (${baseQuery} ${VAR_OTEL_JOIN_QUERY_EXPR})`;
+ }
+
+ return `${baseQuery} ${VAR_OTEL_JOIN_QUERY_EXPR}`;
+}
diff --git a/public/app/features/trails/autoQuery/queryGenerators/common.test.ts b/public/app/features/trails/autoQuery/queryGenerators/common.test.ts
new file mode 100644
index 00000000000..3cf4d266df0
--- /dev/null
+++ b/public/app/features/trails/autoQuery/queryGenerators/common.test.ts
@@ -0,0 +1,80 @@
+import { VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from '../../shared';
+
+import { CommonQueryInfoParams, generateCommonAutoQueryInfo } from './common';
+
+describe('generateCommonAutoQueryInfo', () => {
+ const params: CommonQueryInfoParams = {
+ description: 'Test Description',
+ mainQueryExpr: 'rate(test_metric[5m])',
+ breakdownQueryExpr: 'sum by (label) (test_metric)',
+ unit: 'short',
+ };
+
+ it('should generate a valid AutoQueryInfo object with main, preview, and breakdown variants', () => {
+ const result = generateCommonAutoQueryInfo(params);
+
+ expect(result).toHaveProperty('main');
+ expect(result).toHaveProperty('preview');
+ expect(result).toHaveProperty('breakdown');
+ expect(result).toHaveProperty('variants');
+ });
+
+ it('should configure the main variant correctly', () => {
+ const result = generateCommonAutoQueryInfo(params);
+
+ const { main } = result;
+ expect(main).toMatchObject({
+ title: params.description,
+ unit: params.unit,
+ queries: [
+ {
+ refId: 'A',
+ expr: params.mainQueryExpr,
+ legendFormat: params.description,
+ },
+ ],
+ variant: 'main',
+ });
+ });
+
+ it('should configure the preview variant correctly', () => {
+ const result = generateCommonAutoQueryInfo(params);
+
+ const { preview } = result;
+ expect(preview).toMatchObject({
+ title: VAR_METRIC_EXPR,
+ unit: params.unit,
+ queries: [
+ {
+ refId: 'A',
+ expr: params.mainQueryExpr,
+ legendFormat: params.description,
+ },
+ ],
+ variant: 'preview',
+ });
+ });
+
+ it('should configure the breakdown variant correctly', () => {
+ const result = generateCommonAutoQueryInfo(params);
+
+ const { breakdown } = result;
+ expect(breakdown).toMatchObject({
+ title: VAR_METRIC_EXPR,
+ unit: params.unit,
+ queries: [
+ {
+ refId: 'A',
+ expr: params.breakdownQueryExpr,
+ legendFormat: `{{${VAR_GROUP_BY_EXP}}}`,
+ },
+ ],
+ variant: 'breakdown',
+ });
+ });
+
+ it('should return an empty variants array', () => {
+ const result = generateCommonAutoQueryInfo(params);
+ expect(result.variants).toEqual([]);
+ });
+});
diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/common/generator.ts b/public/app/features/trails/autoQuery/queryGenerators/common.ts
similarity index 80%
rename from public/app/features/trails/AutomaticMetricQueries/query-generators/common/generator.ts
rename to public/app/features/trails/autoQuery/queryGenerators/common.ts
index fbc7ff5d3cb..b0e82b4baa3 100644
--- a/public/app/features/trails/AutomaticMetricQueries/query-generators/common/generator.ts
+++ b/public/app/features/trails/autoQuery/queryGenerators/common.ts
@@ -1,5 +1,6 @@
-import { VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from '../../../shared';
-import { simpleGraphBuilder } from '../../graph-builders/simple';
+import { VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from '../../shared';
+import { simpleGraphBuilder } from '../graphBuilders';
+import { AutoQueryInfo } from '../types';
export type CommonQueryInfoParams = {
description: string;
@@ -13,9 +14,9 @@ export function generateCommonAutoQueryInfo({
mainQueryExpr,
breakdownQueryExpr,
unit,
-}: CommonQueryInfoParams) {
+}: CommonQueryInfoParams): AutoQueryInfo {
const common = {
- title: `${VAR_METRIC_EXPR}`,
+ title: VAR_METRIC_EXPR,
unit,
};
@@ -34,8 +35,7 @@ export function generateCommonAutoQueryInfo({
};
const preview = {
- ...main,
- title: `${VAR_METRIC_EXPR}`,
+ ...common,
queries: [{ ...mainQuery, legendFormat: description }],
vizBuilder: () => simpleGraphBuilder(preview),
variant: 'preview',
diff --git a/public/app/features/trails/autoQuery/queryGenerators/default.test.ts b/public/app/features/trails/autoQuery/queryGenerators/default.test.ts
new file mode 100644
index 00000000000..a5417eb1e4a
--- /dev/null
+++ b/public/app/features/trails/autoQuery/queryGenerators/default.test.ts
@@ -0,0 +1,41 @@
+import { AutoQueryContext } from '../types';
+
+import { createDefaultMetricQueryDefs } from './default';
+
+describe('createDefaultMetricQueryDefs', () => {
+ it('should generate correct AutoQueryInfo for rate query with UTF-8 metric', () => {
+ const context: AutoQueryContext = {
+ metricParts: ['http.requests', 'total'],
+ suffix: 'total',
+ isUtf8Metric: true,
+ unit: 'cps',
+ };
+
+ const result = createDefaultMetricQueryDefs(context);
+
+ expect(result.main.title).toBe('${metric} (overall per-second rate)');
+ expect(result.main.queries[0].expr).toBe(
+ 'sum(rate({"${metric}", ${filters}}[$__rate_interval]) ${otel_join_query})'
+ );
+ expect(result.breakdown.queries[0].expr).toBe(
+ 'sum(rate({"${metric}", ${filters}}[$__rate_interval]) ${otel_join_query})by(${groupby})'
+ );
+ expect(result.preview.unit).toBe('cps');
+ });
+
+ it('should generate correct AutoQueryInfo for non-rate query without UTF-8 metric', () => {
+ const context: AutoQueryContext = {
+ metricParts: ['cpu', 'usage', 'seconds'],
+ suffix: 'avg',
+ isUtf8Metric: false,
+ unit: 's',
+ };
+
+ const result = createDefaultMetricQueryDefs(context);
+
+ expect(result.main.title).toBe('${metric} (average)');
+ expect(result.main.queries[0].expr).toBe('avg(${metric}{${filters}} ${otel_join_query})');
+ expect(result.breakdown.queries[0].expr).toBe('avg(${metric}{${filters}} ${otel_join_query})by(${groupby})');
+ expect(result.preview.unit).toBe('short');
+ });
+});
diff --git a/public/app/features/trails/autoQuery/queryGenerators/default.ts b/public/app/features/trails/autoQuery/queryGenerators/default.ts
new file mode 100644
index 00000000000..bd8074429c0
--- /dev/null
+++ b/public/app/features/trails/autoQuery/queryGenerators/default.ts
@@ -0,0 +1,46 @@
+import { VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from '../../shared';
+import { AutoQueryContext, AutoQueryInfo } from '../types';
+import { getPerSecondRateUnit, getUnit } from '../units';
+
+import { generateBaseQuery } from './baseQuery';
+import { generateCommonAutoQueryInfo } from './common';
+
+const RATE_SUFFIXES = new Set(['count', 'total']);
+const SPECIFIC_AGGREGATIONS_FOR_SUFFIX: Record = {
+ count: 'sum',
+ total: 'sum',
+};
+const aggLabels: Record = {
+ avg: 'average',
+ sum: 'overall',
+};
+
+function getAggLabel(agg: string): string {
+ return aggLabels[agg] || agg;
+}
+
+export function createDefaultMetricQueryDefs(context: AutoQueryContext): AutoQueryInfo {
+ const { metricParts, suffix, isUtf8Metric } = context;
+ const unitSuffix = suffix === 'total' ? metricParts.at(-2) : suffix;
+
+ // Determine query type and unit
+ const isRateQuery = RATE_SUFFIXES.has(suffix);
+ const aggregation = SPECIFIC_AGGREGATIONS_FOR_SUFFIX[suffix] || 'avg';
+ const unit = isRateQuery ? getPerSecondRateUnit(unitSuffix) : getUnit(unitSuffix);
+
+ // Generate base query and descriptions
+ const baseQuery = generateBaseQuery({ isRateQuery, isUtf8Metric });
+ const aggregationDescription = `${getAggLabel(aggregation)}${isRateQuery ? ' per-second rate' : ''}`;
+ const description = `${VAR_METRIC_EXPR} (${aggregationDescription})`;
+
+ // Create query expressions
+ const mainQueryExpr = `${aggregation}(${baseQuery})`;
+ const breakdownQueryExpr = `${aggregation}(${baseQuery})by(${VAR_GROUP_BY_EXP})`;
+
+ return generateCommonAutoQueryInfo({
+ description,
+ mainQueryExpr,
+ breakdownQueryExpr,
+ unit,
+ });
+}
diff --git a/public/app/features/trails/autoQuery/queryGenerators/histogram.test.ts b/public/app/features/trails/autoQuery/queryGenerators/histogram.test.ts
new file mode 100644
index 00000000000..d02436389bf
--- /dev/null
+++ b/public/app/features/trails/autoQuery/queryGenerators/histogram.test.ts
@@ -0,0 +1,101 @@
+import { AutoQueryContext } from '../types';
+
+import { createHistogramMetricQueryDefs } from './histogram';
+
+describe('createHistogramMetricQueryDefs utf8=false', () => {
+ const ctx: AutoQueryContext = {
+ metricParts: ['test', 'latency', 'seconds', 'bucket'],
+ isUtf8Metric: false,
+ suffix: 'bucket',
+ unitSuffix: 'seconds',
+ unit: 's',
+ };
+
+ it('should create the correct title and unit for metricParts', () => {
+ const result = createHistogramMetricQueryDefs(ctx);
+ expect(result.preview.title).toBe('${metric}');
+ expect(result.preview.unit).toBe('s');
+ });
+
+ it('should generate correct p50 AutoQueryDef', () => {
+ const result = createHistogramMetricQueryDefs(ctx);
+ const p50Query = result.breakdown.queries[0];
+
+ expect(p50Query.expr).toBe(
+ 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}))'
+ );
+ expect(p50Query.legendFormat).toBe('{{${groupby}}}');
+ });
+
+ it('should generate correct percentiles AutoQueryDef', () => {
+ const result = createHistogramMetricQueryDefs(ctx);
+ const percentileQueries = result.variants[0].queries;
+
+ expect(percentileQueries[0].expr).toBe(
+ 'histogram_quantile(0.99, sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}))'
+ );
+ expect(percentileQueries[1].expr).toBe(
+ 'histogram_quantile(0.9, sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}))'
+ );
+ expect(percentileQueries[2].expr).toBe(
+ 'histogram_quantile(0.5, sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query}))'
+ );
+ });
+
+ it('should generate correct heatmap AutoQueryDef', () => {
+ const result = createHistogramMetricQueryDefs(ctx);
+ const heatmapQuery = result.preview.queries[0];
+
+ expect(heatmapQuery.expr).toBe('sum by(le) (rate(${metric}{${filters}}[$__rate_interval]) ${otel_join_query})');
+ expect(result.preview.variant).toBe('heatmap');
+ });
+});
+
+describe('createHistogramMetricQueryDefs utf8=true', () => {
+ const ctx: AutoQueryContext = {
+ metricParts: ['test', 'latency', 'seconds', 'bucket'],
+ isUtf8Metric: true,
+ suffix: 'bucket',
+ unitSuffix: 'seconds',
+ unit: 's',
+ };
+
+ it('should create the correct title and unit for metricParts', () => {
+ const result = createHistogramMetricQueryDefs(ctx);
+ expect(result.preview.title).toBe('${metric}');
+ expect(result.preview.unit).toBe('s');
+ });
+
+ it('should generate correct p50 AutoQueryDef', () => {
+ const result = createHistogramMetricQueryDefs(ctx);
+ const p50Query = result.breakdown.queries[0];
+
+ expect(p50Query.expr).toBe(
+ 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate({"${metric}", ${filters}}[$__rate_interval]) ${otel_join_query}))'
+ );
+ expect(p50Query.legendFormat).toBe('{{${groupby}}}');
+ });
+
+ it('should generate correct percentiles AutoQueryDef', () => {
+ const result = createHistogramMetricQueryDefs(ctx);
+ const percentileQueries = result.variants[0].queries;
+
+ expect(percentileQueries[0].expr).toBe(
+ 'histogram_quantile(0.99, sum by(le) (rate({"${metric}", ${filters}}[$__rate_interval]) ${otel_join_query}))'
+ );
+ expect(percentileQueries[1].expr).toBe(
+ 'histogram_quantile(0.9, sum by(le) (rate({"${metric}", ${filters}}[$__rate_interval]) ${otel_join_query}))'
+ );
+ expect(percentileQueries[2].expr).toBe(
+ 'histogram_quantile(0.5, sum by(le) (rate({"${metric}", ${filters}}[$__rate_interval]) ${otel_join_query}))'
+ );
+ });
+
+ it('should generate correct heatmap AutoQueryDef', () => {
+ const result = createHistogramMetricQueryDefs(ctx);
+ const heatmapQuery = result.preview.queries[0];
+
+ expect(heatmapQuery.expr).toBe('sum by(le) (rate({"${metric}", ${filters}}[$__rate_interval]) ${otel_join_query})');
+ expect(result.preview.variant).toBe('heatmap');
+ });
+});
diff --git a/public/app/features/trails/autoQuery/queryGenerators/histogram.ts b/public/app/features/trails/autoQuery/queryGenerators/histogram.ts
new file mode 100644
index 00000000000..c4fc17ad331
--- /dev/null
+++ b/public/app/features/trails/autoQuery/queryGenerators/histogram.ts
@@ -0,0 +1,77 @@
+import { VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from '../../shared';
+import { heatmapGraphBuilder, percentilesGraphBuilder, simpleGraphBuilder } from '../graphBuilders';
+import { AutoQueryContext, AutoQueryDef } from '../types';
+
+import { generateBaseQuery } from './baseQuery';
+
+export function createHistogramMetricQueryDefs(context: AutoQueryContext) {
+ const { unit } = context;
+
+ const common = {
+ title: VAR_METRIC_EXPR,
+ unit,
+ };
+
+ const p50: AutoQueryDef = {
+ ...common,
+ variant: 'p50',
+ queries: [percentileQuery(context, 50)],
+ vizBuilder: () => simpleGraphBuilder(p50),
+ };
+
+ const breakdown: AutoQueryDef = {
+ ...common,
+ variant: 'p50',
+ queries: [percentileQuery(context, 50, [VAR_GROUP_BY_EXP])],
+ vizBuilder: () => simpleGraphBuilder(breakdown),
+ };
+
+ const percentiles: AutoQueryDef = {
+ ...common,
+ variant: 'percentiles',
+ queries: [99, 90, 50].map((p) => percentileQuery(context, p)),
+ vizBuilder: () => percentilesGraphBuilder(percentiles),
+ };
+
+ const heatmap: AutoQueryDef = {
+ ...common,
+ variant: 'heatmap',
+ queries: [
+ {
+ refId: 'Heatmap',
+ expr: generateBaseQuery({
+ isRateQuery: true,
+ isUtf8Metric: context.isUtf8Metric,
+ groupings: ['le'],
+ }),
+ format: 'heatmap',
+ },
+ ],
+ vizBuilder: () => heatmapGraphBuilder(heatmap),
+ };
+
+ return { preview: heatmap, main: heatmap, variants: [percentiles, heatmap], breakdown: breakdown };
+}
+
+function percentileQuery(context: AutoQueryContext, percentile: number, groupings: string[] = []) {
+ const percent = percentile / 100;
+
+ let legendFormat = `${percentile}th Percentile`;
+
+ // For the breakdown view, show the label value variable we are grouping by
+ if (groupings[0]) {
+ legendFormat = `{{${groupings[0]}}}`;
+ }
+
+ const query = generateBaseQuery({
+ isRateQuery: true,
+ isUtf8Metric: context.isUtf8Metric,
+ groupings: ['le', ...groupings],
+ });
+
+ return {
+ refId: `Percentile${percentile}`,
+ expr: `histogram_quantile(${percent}, ${query})`,
+ legendFormat,
+ };
+}
diff --git a/public/app/features/trails/autoQuery/queryGenerators/summary.test.ts b/public/app/features/trails/autoQuery/queryGenerators/summary.test.ts
new file mode 100644
index 00000000000..48fcf4c55a1
--- /dev/null
+++ b/public/app/features/trails/autoQuery/queryGenerators/summary.test.ts
@@ -0,0 +1,49 @@
+import { AutoQueryContext } from '../types';
+
+import { createSummaryMetricQueryDefs } from './summary';
+
+describe('createSummaryMetricQueryDefs', () => {
+ it('should generate correct AutoQueryInfo with rate query and UTF-8 metric', () => {
+ const context: AutoQueryContext = {
+ metricParts: ['http.requests', 'sum'],
+ isUtf8Metric: true,
+ unit: 'ms',
+ suffix: 'sum',
+ };
+
+ const result = createSummaryMetricQueryDefs(context);
+
+ expect(result.preview.title).toBe('${metric}');
+ expect(result.main.title).toBe('http.requests (average)');
+ expect(result.breakdown.title).toBe('${metric}');
+ expect(result.preview.queries[0].expr).toBe(
+ 'sum(rate({"http.requests_sum", ${filters}}[$__rate_interval]) ${otel_join_query})/sum(rate({"http.requests_count", ${filters}}[$__rate_interval]) ${otel_join_query})'
+ );
+ expect(result.breakdown.queries[0].expr).toBe(
+ 'sum(rate({"http.requests_sum", ${filters}}[$__rate_interval]) ${otel_join_query})by(${groupby})/sum(rate({"http.requests_count", ${filters}}[$__rate_interval]) ${otel_join_query})by(${groupby})'
+ );
+ expect(result.preview.unit).toBe('ms');
+ });
+
+ it('should generate correct AutoQueryInfo without UTF-8 metric', () => {
+ const context: AutoQueryContext = {
+ metricParts: ['cpu', 'usage', 'seconds', 'sum'],
+ isUtf8Metric: false,
+ unit: 's',
+ suffix: 'sum',
+ };
+
+ const result = createSummaryMetricQueryDefs(context);
+
+ expect(result.preview.title).toBe('${metric}');
+ expect(result.main.title).toBe('cpu_usage_seconds (average)');
+ expect(result.breakdown.title).toBe('${metric}');
+ expect(result.preview.queries[0].expr).toBe(
+ 'sum(rate(cpu_usage_seconds_sum{${filters}}[$__rate_interval]) ${otel_join_query})/sum(rate(cpu_usage_seconds_count{${filters}}[$__rate_interval]) ${otel_join_query})'
+ );
+ expect(result.breakdown.queries[0].expr).toBe(
+ 'sum(rate(cpu_usage_seconds_sum{${filters}}[$__rate_interval]) ${otel_join_query})by(${groupby})/sum(rate(cpu_usage_seconds_count{${filters}}[$__rate_interval]) ${otel_join_query})by(${groupby})'
+ );
+ expect(result.preview.unit).toBe('s');
+ });
+});
diff --git a/public/app/features/trails/autoQuery/queryGenerators/summary.ts b/public/app/features/trails/autoQuery/queryGenerators/summary.ts
new file mode 100644
index 00000000000..589c8228ebb
--- /dev/null
+++ b/public/app/features/trails/autoQuery/queryGenerators/summary.ts
@@ -0,0 +1,27 @@
+import { VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from '../../shared';
+import { AutoQueryContext, AutoQueryInfo } from '../types';
+
+import { generateBaseQuery } from './baseQuery';
+import { generateCommonAutoQueryInfo } from './common';
+
+export function createSummaryMetricQueryDefs(context: AutoQueryContext): AutoQueryInfo {
+ const { metricParts, isUtf8Metric, unit } = context;
+ const subMetric = metricParts.slice(0, -1).join('_');
+ const description = `${subMetric} (average)`;
+ const baseQuery = generateBaseQuery({ isRateQuery: true, isUtf8Metric });
+ const mainQueryExpr = createMeanExpr(`sum(${baseQuery})`, subMetric);
+ const breakdownQueryExpr = createMeanExpr(`sum(${baseQuery})by(${VAR_GROUP_BY_EXP})`, subMetric);
+
+ return generateCommonAutoQueryInfo({
+ description,
+ mainQueryExpr,
+ breakdownQueryExpr,
+ unit,
+ });
+}
+
+function createMeanExpr(expr: string, subMetric: string): string {
+ const numerator = expr.replace(VAR_METRIC_EXPR, `${subMetric}_sum`);
+ const denominator = expr.replace(VAR_METRIC_EXPR, `${subMetric}_count`);
+ return `${numerator}/${denominator}`;
+}
diff --git a/public/app/features/trails/AutomaticMetricQueries/types.ts b/public/app/features/trails/autoQuery/types.ts
similarity index 75%
rename from public/app/features/trails/AutomaticMetricQueries/types.ts
rename to public/app/features/trails/autoQuery/types.ts
index e104dd6dab2..d6161524159 100644
--- a/public/app/features/trails/AutomaticMetricQueries/types.ts
+++ b/public/app/features/trails/autoQuery/types.ts
@@ -17,3 +17,11 @@ export interface AutoQueryInfo {
}
export type VizBuilder = () => VizPanelBuilder<{}, {}>;
+
+export type AutoQueryContext = {
+ metricParts: string[];
+ isUtf8Metric: boolean;
+ unit: string;
+ suffix: string;
+ unitSuffix?: string;
+};
diff --git a/public/app/features/trails/autoQuery/units.test.ts b/public/app/features/trails/autoQuery/units.test.ts
new file mode 100644
index 00000000000..d039253b33c
--- /dev/null
+++ b/public/app/features/trails/autoQuery/units.test.ts
@@ -0,0 +1,94 @@
+import { DEFAULT_RATE_UNIT, DEFAULT_UNIT, getPerSecondRateUnit, getUnit, getUnitFromMetric } from './units';
+
+describe('getUnitFromMetric', () => {
+ it('should return null for an empty string input', () => {
+ expect(getUnitFromMetric('')).toBe(null);
+ });
+
+ it('should return the last part of the metric if it is a valid unit', () => {
+ expect(getUnitFromMetric('go_gc_gomemlimit_bytes')).toBe('bytes');
+ expect(getUnitFromMetric('go_gc_duration_seconds')).toBe('seconds');
+ });
+
+ it('should return the second to last part of the metric if it is a valid unit', () => {
+ expect(getUnitFromMetric('go_gc_heap_allocs_by_size_bytes_count')).toBe('bytes');
+ expect(getUnitFromMetric('go_cpu_classes_gc_mark_assist_cpu_seconds_total')).toBe('seconds');
+ });
+
+ it('should return null if no valid unit is found', () => {
+ expect(getUnitFromMetric('ALERTS')).toBe(null);
+ expect(getUnitFromMetric('utf8 metric with.dot')).toBe(null);
+ });
+
+ it('should handle metrics with extra underscores', () => {
+ expect(getUnitFromMetric('go_gc__duration__seconds')).toBe('seconds');
+ });
+
+ it('should return null if the metric ends with an invalid unit', () => {
+ expect(getUnitFromMetric('go_gc_duration_invalidunit')).toBe(null);
+ });
+
+ it('should return the last unit if the metric contains only valid units', () => {
+ expect(getUnitFromMetric('bytes_seconds')).toBe('seconds');
+ });
+});
+
+describe('getUnit', () => {
+ it('should return the mapped unit for a valid metric part', () => {
+ expect(getUnit('bytes')).toBe('bytes');
+ expect(getUnit('seconds')).toBe('s');
+ });
+
+ it('should return the default unit if the metric part is undefined', () => {
+ expect(getUnit(undefined)).toBe(DEFAULT_UNIT);
+ });
+
+ it('should return the default unit if the metric part is an empty string', () => {
+ expect(getUnit('')).toBe(DEFAULT_UNIT);
+ });
+
+ it('should return the default unit if the metric part is not in UNIT_MAP', () => {
+ expect(getUnit('invalidPart')).toBe(DEFAULT_UNIT);
+ });
+
+ it('should handle case sensitivity correctly', () => {
+ expect(getUnit('BYTES')).toBe(DEFAULT_UNIT);
+ expect(getUnit('Seconds')).toBe(DEFAULT_UNIT);
+ });
+
+ it('should not throw errors for unusual input', () => {
+ expect(() => getUnit('123')).not.toThrow();
+ expect(() => getUnit('some_random_string')).not.toThrow();
+ expect(() => getUnit(undefined)).not.toThrow();
+ });
+});
+
+describe('getPerSecondRateUnit', () => {
+ it('should return the mapped rate unit for a valid metric part', () => {
+ expect(getPerSecondRateUnit('bytes')).toBe('Bps');
+ expect(getPerSecondRateUnit('seconds')).toBe('short');
+ });
+
+ it('should return the default rate unit if the metric part is undefined', () => {
+ expect(getPerSecondRateUnit(undefined)).toBe(DEFAULT_RATE_UNIT);
+ });
+
+ it('should return the default rate unit if the metric part is an empty string', () => {
+ expect(getPerSecondRateUnit('')).toBe(DEFAULT_RATE_UNIT);
+ });
+
+ it('should return the default rate unit if the metric part is not in RATE_UNIT_MAP', () => {
+ expect(getPerSecondRateUnit('invalidPart')).toBe(DEFAULT_RATE_UNIT);
+ });
+
+ it('should handle case sensitivity correctly', () => {
+ expect(getPerSecondRateUnit('BYTES')).toBe(DEFAULT_RATE_UNIT);
+ expect(getPerSecondRateUnit('Seconds')).toBe(DEFAULT_RATE_UNIT);
+ });
+
+ it('should not throw errors for unusual input', () => {
+ expect(() => getPerSecondRateUnit('123')).not.toThrow();
+ expect(() => getPerSecondRateUnit('some_random_string')).not.toThrow();
+ expect(() => getPerSecondRateUnit(undefined)).not.toThrow();
+ });
+});
diff --git a/public/app/features/trails/autoQuery/units.ts b/public/app/features/trails/autoQuery/units.ts
new file mode 100644
index 00000000000..ea9b819b4a7
--- /dev/null
+++ b/public/app/features/trails/autoQuery/units.ts
@@ -0,0 +1,35 @@
+export const DEFAULT_UNIT = 'short';
+export const DEFAULT_RATE_UNIT = 'cps'; // Count per second
+
+const UNIT_MAP: Record = { bytes: 'bytes', seconds: 's' };
+const UNIT_LIST = Object.keys(UNIT_MAP);
+const RATE_UNIT_MAP: Record = {
+ bytes: 'Bps', // bytes per second
+ // seconds per second is unitless
+ // this may indicate a count of some resource that is active
+ seconds: 'short',
+};
+
+// Get unit from metric name (e.g. "go_gc_duration_seconds" -> "seconds")
+export function getUnitFromMetric(metric: string) {
+ if (!metric) {
+ return null;
+ }
+
+ const metricParts = metric.toLowerCase().split('_').slice(-2); // Get last two parts
+ for (let i = metricParts.length - 1; i >= 0; i--) {
+ if (UNIT_LIST.includes(metricParts[i])) {
+ return metricParts[i];
+ }
+ }
+ return null;
+}
+
+// Get Grafana unit for a panel (e.g. "go_gc_duration_seconds" -> "s")
+export function getUnit(metricPart: string | undefined) {
+ return (metricPart && UNIT_MAP[metricPart]) || DEFAULT_UNIT;
+}
+
+export function getPerSecondRateUnit(metricPart: string | undefined) {
+ return (metricPart && RATE_UNIT_MAP[metricPart]) || DEFAULT_RATE_UNIT;
+}
diff --git a/public/app/features/trails/interactions.ts b/public/app/features/trails/interactions.ts
index 7abe3ba2ef4..181b321dfc7 100644
--- a/public/app/features/trails/interactions.ts
+++ b/public/app/features/trails/interactions.ts
@@ -123,6 +123,9 @@ type Interactions = {
sortBy: string
};
wasm_not_supported: {},
+ missing_otel_labels_by_truncating_job_and_instance: {
+ metric?: string;
+ },
};
const PREFIX = 'grafana_explore_metrics_';
diff --git a/public/app/features/trails/otel/api.test.ts b/public/app/features/trails/otel/api.test.ts
index 104fb4de2cd..b5469478c5a 100644
--- a/public/app/features/trails/otel/api.test.ts
+++ b/public/app/features/trails/otel/api.test.ts
@@ -9,6 +9,19 @@ import {
getFilteredResourceAttributes,
} from './api';
+jest.mock('./util', () => ({
+ ...jest.requireActual('./util'),
+ limitOtelMatchTerms: jest.fn().mockImplementation(() => {
+ return {
+ jobsRegex: 'job=~"job1|job2"',
+ instancesRegex: 'instance=~"instance1|instance2"',
+ // this flag is used when the values exceed 2000 characters
+ // in this mock we are not including more, just flipping the flag
+ missingOtelTargets: true,
+ };
+ }),
+}));
+
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
config: {
@@ -72,7 +85,7 @@ describe('OTEL API', () => {
to: 'now',
};
- afterAll(() => {
+ afterEach(() => {
jest.clearAllMocks();
});
@@ -117,11 +130,16 @@ describe('OTEL API', () => {
describe('getFilteredResourceAttributes', () => {
it('should fetch and filter OTEL resources with excluded filters', async () => {
- const resources = await getFilteredResourceAttributes(dataSourceUid, timeRange, 'metric', ['job']);
+ const { attributes } = await getFilteredResourceAttributes(dataSourceUid, timeRange, 'metric', ['job']);
// promotedResourceAttribute will be filtered out because even though it is a resource attribute, it is also a metric label and wee prioritize metric labels
- expect(resources).not.toEqual(['promotedResourceAttribute', 'resourceAttribute']);
+ expect(attributes).not.toEqual(['promotedResourceAttribute', 'resourceAttribute']);
// the resource attributes returned are the ones only present on target_info
- expect(resources).toEqual(['resourceAttribute']);
+ expect(attributes).toEqual(['resourceAttribute']);
+ });
+
+ it('should return a boolean true if the job and instance list for matching is truncated', async () => {
+ const { missingOtelTargets } = await getFilteredResourceAttributes(dataSourceUid, timeRange, 'metric', ['job']);
+ expect(missingOtelTargets).toBe(true);
});
});
});
diff --git a/public/app/features/trails/otel/api.ts b/public/app/features/trails/otel/api.ts
index 6e1dc9cec61..b1c1b8a09f8 100644
--- a/public/app/features/trails/otel/api.ts
+++ b/public/app/features/trails/otel/api.ts
@@ -5,7 +5,7 @@ import { config, getBackendSrv } from '@grafana/runtime';
import { callSuggestionsApi } from '../utils';
import { OtelResponse, LabelResponse, OtelTargetType } from './types';
-import { sortResources } from './util';
+import { limitOtelMatchTerms, sortResources } from './util';
const OTEL_RESOURCE_EXCLUDED_FILTERS = ['__name__', 'deployment_environment']; // name is handled by metric search metrics bar
/**
@@ -242,14 +242,16 @@ export async function getFilteredResourceAttributes(
// OTel metrics require unique identifies for the resource. Job+instance is the unique identifier.
// If there are none, we cannot join on a target_info resource
if (metricResources.jobs.length === 0 || metricResources.instances.length === 0) {
- return [];
+ return { attributes: [], missingOtelTargets: false };
}
// The URL for the labels endpoint
const url = `/api/datasources/uid/${datasourceUid}/resources/api/v1/labels`;
// The match param for the metric to get all possible labels for this metric
- const metricMatchParam = `${metric}{job=~"${metricResources.jobs.join('|')}",instance=~"${metricResources.instances.join('|')}"}`;
+ const metricMatchTerms = limitOtelMatchTerms([], metricResources.jobs, metricResources.instances);
+
+ let metricMatchParam = `${metric}{${metricMatchTerms.jobsRegex},${metricMatchTerms.instancesRegex}}`;
const start = getPrometheusTime(timeRange.from, false);
const end = getPrometheusTime(timeRange.to, true);
@@ -272,7 +274,7 @@ export async function getFilteredResourceAttributes(
const metricLabels = metricResponse.data ?? [];
// only get the resource attributes filtered by job and instance values present on the metric
- const targetInfoMatchParam = `target_info{job=~"${metricResources.jobs.join('|')}",instance=~"${metricResources.instances.join('|')}"}`;
+ let targetInfoMatchParam = `target_info{${metricMatchTerms.jobsRegex},${metricMatchTerms.instancesRegex}}`;
const targetInfoParams: Record = {
start,
@@ -303,5 +305,5 @@ export async function getFilteredResourceAttributes(
// return a string array
const resourceAttributes = sortedResourceAttributes.map((el) => el.text);
- return resourceAttributes;
+ return { attributes: resourceAttributes, missingOtelTargets: metricMatchTerms.missingOtelTargets };
}
diff --git a/public/app/features/trails/otel/util.ts b/public/app/features/trails/otel/util.ts
index ef935bc9017..f4cdae14d36 100644
--- a/public/app/features/trails/otel/util.ts
+++ b/public/app/features/trails/otel/util.ts
@@ -5,6 +5,7 @@ import { DataTrail } from '../DataTrail';
import {
VAR_DATASOURCE_EXPR,
VAR_FILTERS,
+ VAR_MISSING_OTEL_TARGETS,
VAR_OTEL_DEPLOYMENT_ENV,
VAR_OTEL_GROUP_LEFT,
VAR_OTEL_JOIN_QUERY,
@@ -174,6 +175,8 @@ export function limitOtelMatchTerms(
// stop before the total count reaches 2000
// show a warning that there are missing OTel targets and
// the user must select more OTel resource attributes
+ const jobCheck: { [key: string]: boolean } = {};
+ const instanceCheck: { [key: string]: boolean } = {};
for (let i = 0; i < jobsList.length; i++) {
// use or character for the count
const orChars = i === 0 ? 0 : 2;
@@ -191,9 +194,12 @@ export function limitOtelMatchTerms(
jobsRegex += `${jobsList[i]}`;
instancesRegex += `${instancesList[i]}`;
} else {
- jobsRegex += `|${jobsList[i]}`;
- instancesRegex += `|${instancesList[i]}`;
+ // check to make sure we aren't duplicating job or instance
+ jobsRegex += jobCheck[jobsList[i]] ? '' : `|${jobsList[i]}`;
+ instancesRegex += instanceCheck[instancesList[i]] ? '' : `|${instancesList[i]}`;
}
+ jobCheck[jobsList[i]] = true;
+ instanceCheck[instancesList[i]] = true;
} else {
missingOtelTargets = true;
break;
@@ -239,7 +245,12 @@ export async function updateOtelJoinWithGroupLeft(trail: DataTrail, metric: stri
}
const otelGroupLeft = sceneGraph.lookupVariable(VAR_OTEL_GROUP_LEFT, trail);
const otelJoinQueryVariable = sceneGraph.lookupVariable(VAR_OTEL_JOIN_QUERY, trail);
- if (!(otelGroupLeft instanceof ConstantVariable) || !(otelJoinQueryVariable instanceof ConstantVariable)) {
+ const missingOtelTargetsVariable = sceneGraph.lookupVariable(VAR_MISSING_OTEL_TARGETS, trail);
+ if (
+ !(otelGroupLeft instanceof ConstantVariable) ||
+ !(otelJoinQueryVariable instanceof ConstantVariable) ||
+ !(missingOtelTargetsVariable instanceof ConstantVariable)
+ ) {
return;
}
// Remove the group left
@@ -272,7 +283,12 @@ export async function updateOtelJoinWithGroupLeft(trail: DataTrail, metric: stri
excludeFilterKeys = excludeFilterKeys.concat(['job', 'instance']);
}
const datasourceUid = sceneGraph.interpolate(trail, VAR_DATASOURCE_EXPR);
- const attributes = await getFilteredResourceAttributes(datasourceUid, timeRange, metric, excludeFilterKeys);
+ const { attributes, missingOtelTargets } = await getFilteredResourceAttributes(
+ datasourceUid,
+ timeRange,
+ metric,
+ excludeFilterKeys
+ );
// here we start to add the attributes to the group left
if (attributes.length > 0) {
// update the group left variable that contains all the filtered resource attributes
@@ -283,6 +299,8 @@ export async function updateOtelJoinWithGroupLeft(trail: DataTrail, metric: stri
// update the join query that is interpolated in all queries
otelJoinQueryVariable.setState({ value: otelJoinQuery });
}
+ // used to show a warning in label breakdown that the user must select more OTel resource attributes
+ missingOtelTargetsVariable.setState({ value: missingOtelTargets });
}
/**
diff --git a/public/app/features/trails/otel/utils.test.ts b/public/app/features/trails/otel/utils.test.ts
index b6edb714bbf..ff54ef6f7ee 100644
--- a/public/app/features/trails/otel/utils.test.ts
+++ b/public/app/features/trails/otel/utils.test.ts
@@ -21,7 +21,9 @@ jest.mock('./api', () => ({
totalOtelResources: jest.fn(() => ({ job: 'oteldemo', instance: 'instance' })),
getDeploymentEnvironments: jest.fn(() => ['production', 'staging']),
isOtelStandardization: jest.fn(() => true),
- getFilteredResourceAttributes: jest.fn().mockResolvedValue(['resourceAttribute']),
+ getFilteredResourceAttributes: jest
+ .fn()
+ .mockResolvedValue({ attributes: ['resourceAttribute'], missingOtelTargets: false }),
}));
describe('sortResources', () => {
diff --git a/public/app/features/trails/shared.ts b/public/app/features/trails/shared.ts
index 5ec6b86f6a9..aab8bc4c736 100644
--- a/public/app/features/trails/shared.ts
+++ b/public/app/features/trails/shared.ts
@@ -15,7 +15,7 @@ export const TRAILS_ROUTE = '/explore/metrics/trail';
export const HOME_ROUTE = '/explore/metrics';
export const VAR_FILTERS = 'filters';
-export const VAR_FILTERS_EXPR = '{${filters}}';
+export const VAR_FILTERS_EXPR = '${filters}';
export const VAR_METRIC = 'metric';
export const VAR_METRIC_EXPR = '${metric}';
export const VAR_GROUP_BY = 'groupby';
@@ -34,6 +34,8 @@ export const VAR_OTEL_GROUP_BY = 'otel_groupby';
export const VAR_OTEL_GROUP_BY_EXPR = '${otel_groupby}';
export const VAR_OTEL_GROUP_LEFT = 'otel_group_left';
export const VAR_OTEL_GROUP_LEFT_EXPR = '${otel_group_left}';
+export const VAR_MISSING_OTEL_TARGETS = 'missing_otel_targets';
+export const VAR_MISSING_OTEL_TARGETS_EXPR = '${missing_otel_targets}';
export const LOGS_METRIC = '$__logs__';
export const KEY_SQR_METRIC_VIZ_QUERY = 'sqr-metric-viz-query';
diff --git a/public/app/plugins/datasource/azuremonitor/package.json b/public/app/plugins/datasource/azuremonitor/package.json
index 76e07e77b51..d9b89b0c373 100644
--- a/public/app/plugins/datasource/azuremonitor/package.json
+++ b/public/app/plugins/datasource/azuremonitor/package.json
@@ -33,7 +33,7 @@
"@testing-library/user-event": "14.5.2",
"@types/jest": "29.5.14",
"@types/lodash": "4.17.13",
- "@types/node": "20.16.15",
+ "@types/node": "22.10.2",
"@types/prismjs": "1.26.5",
"@types/react": "18.3.3",
"@types/react-dom": "18.2.25",
diff --git a/public/app/plugins/datasource/cloud-monitoring/package.json b/public/app/plugins/datasource/cloud-monitoring/package.json
index 871b77b7cbe..fae06f0ce64 100644
--- a/public/app/plugins/datasource/cloud-monitoring/package.json
+++ b/public/app/plugins/datasource/cloud-monitoring/package.json
@@ -35,7 +35,7 @@
"@types/debounce-promise": "3.1.9",
"@types/jest": "29.5.14",
"@types/lodash": "4.17.13",
- "@types/node": "20.16.15",
+ "@types/node": "22.10.2",
"@types/prismjs": "1.26.5",
"@types/react": "18.3.3",
"@types/react-dom": "18.2.25",
diff --git a/public/app/plugins/datasource/elasticsearch/datasource.test.ts b/public/app/plugins/datasource/elasticsearch/datasource.test.ts
index beddbe42c05..c653ac8a132 100644
--- a/public/app/plugins/datasource/elasticsearch/datasource.test.ts
+++ b/public/app/plugins/datasource/elasticsearch/datasource.test.ts
@@ -12,7 +12,7 @@ import {
TimeRange,
toUtc,
} from '@grafana/data';
-import { FetchResponse, reportInteraction, getBackendSrv, setBackendSrv, BackendSrv } from '@grafana/runtime';
+import { FetchResponse, reportInteraction, getBackendSrv, setBackendSrv, BackendSrv, config } from '@grafana/runtime';
import { ElasticDatasource } from './datasource';
import { createElasticDatasource, createElasticQuery, mockResponseFrames } from './mocks';
@@ -1253,4 +1253,191 @@ describe('ElasticDatasource', () => {
});
});
});
+
+ describe('getFieldsFieldCap', () => {
+ const originalFeatureToggleValue = config.featureToggles.elasticsearchCrossClusterSearch;
+
+ afterEach(() => {
+ config.featureToggles.elasticsearchCrossClusterSearch = originalFeatureToggleValue;
+ });
+ const getFieldsMockData = {
+ fields: {
+ '@timestamp_millis': {
+ date: {
+ type: 'date',
+ metadata_field: false,
+ },
+ },
+ classification_terms: {
+ keyword: {
+ type: 'keyword',
+ metadata_field: false,
+ },
+ },
+ ip_address: {
+ ip: {
+ type: 'ip',
+ metadata_field: false,
+ },
+ },
+ 'justification_blob.criterion.keyword': {
+ keyword: {
+ type: 'keyword',
+ metadata_field: false,
+ },
+ },
+ 'justification_blob.criterion': {
+ text: {
+ type: 'text',
+ metadata_field: false,
+ },
+ },
+ justification_blob: {
+ object: {
+ type: 'object',
+ metadata_field: false,
+ },
+ },
+ 'justification_blob.shallow.jsi.sdb.dsel2.bootlegged-gille.botness': {
+ float: {
+ type: 'float',
+ metadata_field: false,
+ },
+ },
+ 'justification_blob.shallow.jsi.sdb.dsel2.bootlegged-gille.general_algorithm_score': {
+ float: {
+ type: 'float',
+ metadata_field: false,
+ },
+ },
+ 'justification_blob.shallow.jsi.sdb.dsel2.uncombed-boris.botness': {
+ float: {
+ type: 'float',
+ metadata_field: false,
+ },
+ },
+ 'justification_blob.shallow.jsi.sdb.dsel2.uncombed-boris.general_algorithm_score': {
+ float: {
+ type: 'float',
+ metadata_field: false,
+ },
+ },
+ overall_vote_score: {
+ float: {
+ type: 'float',
+ metadata_field: false,
+ },
+ },
+ _index: {
+ _index: {
+ type: '_index',
+ metadata_field: true,
+ },
+ },
+ },
+ indices: ['[test-]YYYY.MM.DD'],
+ };
+
+ it('should not retry when ES is down', async () => {
+ config.featureToggles.elasticsearchCrossClusterSearch = true;
+ const twoDaysBefore = toUtc().subtract(2, 'day').format('YYYY.MM.DD');
+ const ds = createElasticDatasource({
+ jsonData: { interval: 'Daily' },
+ });
+
+ ds.getResource = jest.fn().mockImplementation((options) => {
+ if (options.url === `test-${twoDaysBefore}/_field_caps`) {
+ return of({
+ data: {},
+ });
+ }
+ return throwError({ status: 500 });
+ });
+
+ const timeRange = { from: 1, to: 2 } as unknown as TimeRange;
+ await expect(ds.getFields(undefined, timeRange)).toEmitValuesWith((received) => {
+ expect(received.length).toBe(1);
+ expect(received[0]).toStrictEqual({ status: 500 });
+ expect(ds.getResource).toBeCalledTimes(1);
+ });
+ });
+
+ it('should not retry more than 7 indices', async () => {
+ config.featureToggles.elasticsearchCrossClusterSearch = true;
+ const ds = createElasticDatasource({
+ jsonData: { interval: 'Daily' },
+ });
+
+ ds.getResource = jest.fn().mockImplementation(() => {
+ return throwError({ status: 404 });
+ });
+
+ const timeRange = createTimeRange(dateTime().subtract(2, 'week'), dateTime());
+
+ await expect(ds.getFields(undefined, timeRange)).toEmitValuesWith((received) => {
+ expect(received.length).toBe(1);
+ expect(received[0]).toStrictEqual('Could not find an available index for this time range.');
+ expect(ds.getResource).toBeCalledTimes(7);
+ });
+ });
+
+ it('should return nested fields', async () => {
+ config.featureToggles.elasticsearchCrossClusterSearch = true;
+ const ds = createElasticDatasource({
+ jsonData: { interval: 'Daily' },
+ });
+
+ ds.getResource = jest.fn().mockResolvedValue(getFieldsMockData);
+
+ await expect(ds.getFields()).toEmitValuesWith((received) => {
+ expect(received.length).toBe(1);
+
+ const fieldObjects = received[0];
+ const fields = map(fieldObjects, 'text');
+ expect(fields).toEqual([
+ '@timestamp_millis',
+ 'classification_terms',
+ 'ip_address',
+ 'justification_blob.criterion.keyword',
+ 'justification_blob.criterion',
+ 'justification_blob.shallow.jsi.sdb.dsel2.bootlegged-gille.botness',
+ 'justification_blob.shallow.jsi.sdb.dsel2.bootlegged-gille.general_algorithm_score',
+ 'justification_blob.shallow.jsi.sdb.dsel2.uncombed-boris.botness',
+ 'justification_blob.shallow.jsi.sdb.dsel2.uncombed-boris.general_algorithm_score',
+ 'overall_vote_score',
+ ]);
+ });
+ });
+ it('should return number fields', async () => {
+ config.featureToggles.elasticsearchCrossClusterSearch = true;
+ ds.getResource = jest.fn().mockResolvedValue(getFieldsMockData);
+
+ await expect(ds.getFields(['number'])).toEmitValuesWith((received) => {
+ expect(received.length).toBe(1);
+
+ const fieldObjects = received[0];
+ const fields = map(fieldObjects, 'text');
+ expect(fields).toEqual([
+ 'justification_blob.shallow.jsi.sdb.dsel2.bootlegged-gille.botness',
+ 'justification_blob.shallow.jsi.sdb.dsel2.bootlegged-gille.general_algorithm_score',
+ 'justification_blob.shallow.jsi.sdb.dsel2.uncombed-boris.botness',
+ 'justification_blob.shallow.jsi.sdb.dsel2.uncombed-boris.general_algorithm_score',
+ 'overall_vote_score',
+ ]);
+ });
+ });
+
+ it('should return date fields', async () => {
+ config.featureToggles.elasticsearchCrossClusterSearch = true;
+ ds.getResource = jest.fn().mockResolvedValue(getFieldsMockData);
+
+ await expect(ds.getFields(['date'])).toEmitValuesWith((received) => {
+ expect(received.length).toBe(1);
+
+ const fieldObjects = received[0];
+ const fields = map(fieldObjects, 'text');
+ expect(fields).toEqual(['@timestamp_millis']);
+ });
+ });
+ });
});
diff --git a/public/app/plugins/datasource/elasticsearch/datasource.ts b/public/app/plugins/datasource/elasticsearch/datasource.ts
index 584f970fef8..3cc574ab770 100644
--- a/public/app/plugins/datasource/elasticsearch/datasource.ts
+++ b/public/app/plugins/datasource/elasticsearch/datasource.ts
@@ -1,4 +1,4 @@
-import { cloneDeep, first as _first, isNumber, isObject, isString, map as _map, find } from 'lodash';
+import { cloneDeep, first as _first, isNumber, isString, map as _map, find, isObject } from 'lodash';
import { from, generate, lastValueFrom, Observable, of } from 'rxjs';
import { catchError, first, map, mergeMap, skipWhile, throwIfEmpty, tap } from 'rxjs/operators';
import { SemVer } from 'semver';
@@ -46,6 +46,7 @@ import {
BackendSrvRequest,
TemplateSrv,
getTemplateSrv,
+ config,
} from '@grafana/runtime';
import { IndexPattern, intervalMap } from './IndexPattern';
@@ -197,7 +198,7 @@ export class ElasticDatasource
indexList = [this.indexPattern.getIndexForToday()];
}
- const url = '_mapping';
+ const url = config.featureToggles.elasticsearchCrossClusterSearch ? '_field_caps' : '_mapping';
const indexUrlList = indexList.map((index) => {
// make sure `index` does not end with a slash
@@ -743,6 +744,10 @@ export class ElasticDatasource
* or fix the implementation.
*/
getFields(type?: string[], range?: TimeRange): Observable {
+ if (config.featureToggles.elasticsearchCrossClusterSearch) {
+ return this.getFieldsCrossCluster(type, range);
+ }
+
const typeMap: Record = {
float: 'number',
double: 'number',
@@ -823,6 +828,67 @@ export class ElasticDatasource
);
}
+ getFieldsCrossCluster(type?: string[], range?: TimeRange): Observable {
+ const typeMap: Record = {
+ float: 'number',
+ double: 'number',
+ integer: 'number',
+ long: 'number',
+ date: 'date',
+ date_nanos: 'date',
+ string: 'string',
+ text: 'string',
+ scaled_float: 'number',
+ nested: 'nested',
+ histogram: 'number',
+ };
+ return this.requestAllIndices(range).pipe(
+ map((result) => {
+ interface FieldInfo {
+ metadata_field: string;
+ }
+ const shouldAddField = (obj: Record>) => {
+ // equal query type filter, or via type map translation
+ for (const objField in obj) {
+ if (objField === 'object') {
+ continue;
+ }
+ if (obj[objField].metadata_field) {
+ continue;
+ }
+
+ if (!type || type.length === 0) {
+ return true;
+ }
+
+ if (type.includes(objField) || type.includes(typeMap[objField])) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ const fields: Record = {};
+
+ const fieldsData = result['fields'];
+ for (const fieldName in fieldsData) {
+ const fieldInfo = fieldsData[fieldName];
+ if (shouldAddField(fieldInfo)) {
+ fields[fieldName] = {
+ text: fieldName,
+ type: fieldInfo.type,
+ };
+ }
+ }
+
+ // transform to array
+ return _map(fields, (value) => {
+ return value;
+ });
+ })
+ );
+ }
+
/**
* Get values for a given field.
* Used for example in getTagValues.
diff --git a/public/app/plugins/datasource/elasticsearch/hooks/useFields.ts b/public/app/plugins/datasource/elasticsearch/hooks/useFields.ts
index 8f36406a53d..df9d1271431 100644
--- a/public/app/plugins/datasource/elasticsearch/hooks/useFields.ts
+++ b/public/app/plugins/datasource/elasticsearch/hooks/useFields.ts
@@ -60,7 +60,7 @@ export const useFields = (type: AggregationType | string[]) => {
let rawFields: MetricFindValue[];
return async (q?: string) => {
- // _mapping doesn't support filtering, we avoid sending a request everytime q changes
+ // TODO: use _field_caps to support filtering
if (!rawFields) {
rawFields = await lastValueFrom(datasource.getFields(filter, range));
}
diff --git a/public/app/plugins/datasource/grafana-postgresql-datasource/package.json b/public/app/plugins/datasource/grafana-postgresql-datasource/package.json
index 1cbbf04b7a9..c8dd82e5398 100644
--- a/public/app/plugins/datasource/grafana-postgresql-datasource/package.json
+++ b/public/app/plugins/datasource/grafana-postgresql-datasource/package.json
@@ -23,7 +23,7 @@
"@testing-library/user-event": "14.5.2",
"@types/jest": "29.5.14",
"@types/lodash": "4.17.13",
- "@types/node": "20.16.15",
+ "@types/node": "22.10.2",
"@types/react": "18.3.3",
"ts-node": "10.9.2",
"typescript": "5.5.4",
diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json
index abea7a88eed..46381b60009 100644
--- a/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json
+++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json
@@ -27,7 +27,7 @@
"@testing-library/user-event": "14.5.2",
"@types/jest": "29.5.14",
"@types/lodash": "4.17.13",
- "@types/node": "20.16.15",
+ "@types/node": "22.10.2",
"@types/prismjs": "1.26.5",
"@types/react": "18.3.3",
"@types/react-dom": "18.2.25",
diff --git a/public/app/plugins/datasource/grafana-testdata-datasource/package.json b/public/app/plugins/datasource/grafana-testdata-datasource/package.json
index 74696b23884..98d66817aeb 100644
--- a/public/app/plugins/datasource/grafana-testdata-datasource/package.json
+++ b/public/app/plugins/datasource/grafana-testdata-datasource/package.json
@@ -30,7 +30,7 @@
"@types/d3-random": "^3.0.2",
"@types/jest": "29.5.14",
"@types/lodash": "4.17.13",
- "@types/node": "20.16.15",
+ "@types/node": "22.10.2",
"@types/react": "18.3.3",
"@types/react-dom": "18.2.25",
"@types/uuid": "9.0.8",
diff --git a/public/app/plugins/datasource/jaeger/package.json b/public/app/plugins/datasource/jaeger/package.json
index ea8ce4d9a84..69c34d37e15 100644
--- a/public/app/plugins/datasource/jaeger/package.json
+++ b/public/app/plugins/datasource/jaeger/package.json
@@ -31,7 +31,7 @@
"@types/jest": "29.5.14",
"@types/lodash": "4.17.13",
"@types/logfmt": "^1.2.3",
- "@types/node": "20.16.15",
+ "@types/node": "22.10.2",
"@types/react": "18.3.3",
"@types/react-dom": "18.2.25",
"@types/react-window": "1.8.8",
diff --git a/public/app/plugins/datasource/mssql/package.json b/public/app/plugins/datasource/mssql/package.json
index c4ea78cfbbc..3fa83058650 100644
--- a/public/app/plugins/datasource/mssql/package.json
+++ b/public/app/plugins/datasource/mssql/package.json
@@ -23,7 +23,7 @@
"@testing-library/user-event": "14.5.2",
"@types/jest": "29.5.14",
"@types/lodash": "4.17.13",
- "@types/node": "20.16.15",
+ "@types/node": "22.10.2",
"@types/react": "18.3.3",
"ts-node": "10.9.2",
"typescript": "5.5.4",
diff --git a/public/app/plugins/datasource/mysql/package.json b/public/app/plugins/datasource/mysql/package.json
index b3bd70432d5..f4eba056126 100644
--- a/public/app/plugins/datasource/mysql/package.json
+++ b/public/app/plugins/datasource/mysql/package.json
@@ -23,7 +23,7 @@
"@testing-library/user-event": "14.5.2",
"@types/jest": "29.5.14",
"@types/lodash": "4.17.13",
- "@types/node": "20.16.15",
+ "@types/node": "22.10.2",
"@types/react": "18.3.3",
"ts-node": "10.9.2",
"typescript": "5.5.4",
diff --git a/public/app/plugins/datasource/parca/package.json b/public/app/plugins/datasource/parca/package.json
index 3657e81b193..2db8b77cd13 100644
--- a/public/app/plugins/datasource/parca/package.json
+++ b/public/app/plugins/datasource/parca/package.json
@@ -23,7 +23,7 @@
"@testing-library/react": "16.0.1",
"@testing-library/user-event": "14.5.2",
"@types/lodash": "4.17.13",
- "@types/node": "20.16.15",
+ "@types/node": "22.10.2",
"@types/react": "18.3.3",
"@types/react-dom": "18.2.25",
"ts-node": "10.9.2",
diff --git a/public/app/plugins/datasource/tempo/package.json b/public/app/plugins/datasource/tempo/package.json
index 68f3ebae996..bd64af43cc8 100644
--- a/public/app/plugins/datasource/tempo/package.json
+++ b/public/app/plugins/datasource/tempo/package.json
@@ -46,7 +46,7 @@
"@testing-library/user-event": "14.5.2",
"@types/jest": "29.5.14",
"@types/lodash": "4.17.13",
- "@types/node": "20.16.15",
+ "@types/node": "22.10.2",
"@types/prismjs": "1.26.5",
"@types/react": "18.3.3",
"@types/react-dom": "18.2.25",
diff --git a/public/app/plugins/datasource/zipkin/package.json b/public/app/plugins/datasource/zipkin/package.json
index 14cc700877a..27bb1b61f48 100644
--- a/public/app/plugins/datasource/zipkin/package.json
+++ b/public/app/plugins/datasource/zipkin/package.json
@@ -26,7 +26,7 @@
"@testing-library/react": "16.0.1",
"@types/jest": "29.5.14",
"@types/lodash": "4.17.13",
- "@types/node": "20.16.15",
+ "@types/node": "22.10.2",
"@types/react": "18.3.3",
"@types/react-dom": "18.2.25",
"ts-node": "10.9.2",
diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json
index 97de1f1933a..08f14d2cebe 100644
--- a/public/locales/de-DE/grafana.json
+++ b/public/locales/de-DE/grafana.json
@@ -479,6 +479,10 @@
},
"rule-list": {
"configure-datasource": "",
+ "filter-view": {
+ "no-more-results": "",
+ "no-rules-found": ""
+ },
"new-alert-rule": ""
},
"rule-state": {
@@ -1754,6 +1758,12 @@
"infinite-scroll": {
"older-logs": ""
},
+ "log-details": {
+ "fields": "",
+ "links": "",
+ "log-line": "",
+ "no-details": ""
+ },
"log-row-message": {
"ellipsis": "",
"more": ""
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index d1b9458d42c..5bff044da72 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -1290,6 +1290,7 @@
"breakdown": {
"clearFilter": "Clear filter",
"labelSelect": "Select",
+ "missing-otel-labels": "This metric has too many job and instance label values to call the Prometheus label_values endpoint with the match[] parameter. These label values are used to join the metric with target_info, which contains the resource attributes. Please include more resource attributes filters.",
"noMatchingValue": "No values found matching; {{filter}}",
"sortBy": "Sort by"
},
@@ -1758,6 +1759,12 @@
"infinite-scroll": {
"older-logs": "Older logs"
},
+ "log-details": {
+ "fields": "Fields",
+ "links": "Links",
+ "log-line": "Log line",
+ "no-details": "No details available"
+ },
"log-row-message": {
"ellipsis": "… ",
"more": "more"
diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json
index 9c2dd653fd2..3fea3cd61d0 100644
--- a/public/locales/es-ES/grafana.json
+++ b/public/locales/es-ES/grafana.json
@@ -479,6 +479,10 @@
},
"rule-list": {
"configure-datasource": "",
+ "filter-view": {
+ "no-more-results": "",
+ "no-rules-found": ""
+ },
"new-alert-rule": ""
},
"rule-state": {
@@ -1754,6 +1758,12 @@
"infinite-scroll": {
"older-logs": ""
},
+ "log-details": {
+ "fields": "",
+ "links": "",
+ "log-line": "",
+ "no-details": ""
+ },
"log-row-message": {
"ellipsis": "",
"more": ""
diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json
index 9bf42b88871..4c336a2a73e 100644
--- a/public/locales/fr-FR/grafana.json
+++ b/public/locales/fr-FR/grafana.json
@@ -479,6 +479,10 @@
},
"rule-list": {
"configure-datasource": "",
+ "filter-view": {
+ "no-more-results": "",
+ "no-rules-found": ""
+ },
"new-alert-rule": ""
},
"rule-state": {
@@ -1754,6 +1758,12 @@
"infinite-scroll": {
"older-logs": ""
},
+ "log-details": {
+ "fields": "",
+ "links": "",
+ "log-line": "",
+ "no-details": ""
+ },
"log-row-message": {
"ellipsis": "",
"more": ""
diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json
index aeb77531dbb..fe64f55e4d4 100644
--- a/public/locales/pseudo-LOCALE/grafana.json
+++ b/public/locales/pseudo-LOCALE/grafana.json
@@ -1290,6 +1290,7 @@
"breakdown": {
"clearFilter": "Cľęäř ƒįľŧęř",
"labelSelect": "Ŝęľęčŧ",
+ "missing-otel-labels": "Ŧĥįş męŧřįč ĥäş ŧőő mäʼny ĵőþ äʼnđ įʼnşŧäʼnčę ľäþęľ väľūęş ŧő čäľľ ŧĥę Přőmęŧĥęūş ľäþęľ_väľūęş ęʼnđpőįʼnŧ ŵįŧĥ ŧĥę mäŧčĥ[] päřämęŧęř. Ŧĥęşę ľäþęľ väľūęş äřę ūşęđ ŧő ĵőįʼn ŧĥę męŧřįč ŵįŧĥ ŧäřģęŧ_įʼnƒő, ŵĥįčĥ čőʼnŧäįʼnş ŧĥę řęşőūřčę äŧŧřįþūŧęş. Pľęäşę įʼnčľūđę mőřę řęşőūřčę äŧŧřįþūŧęş ƒįľŧęřş.",
"noMatchingValue": "Ńő väľūęş ƒőūʼnđ mäŧčĥįʼnģ; {{filter}}",
"sortBy": "Ŝőřŧ þy"
},
@@ -1758,6 +1759,12 @@
"infinite-scroll": {
"older-logs": "Øľđęř ľőģş"
},
+ "log-details": {
+ "fields": "Fįęľđş",
+ "links": "Ŀįʼnĸş",
+ "log-line": "Ŀőģ ľįʼnę",
+ "no-details": "Ńő đęŧäįľş äväįľäþľę"
+ },
"log-row-message": {
"ellipsis": "… ",
"more": "mőřę"
diff --git a/public/locales/pt-BR/grafana.json b/public/locales/pt-BR/grafana.json
index 88ba779bff8..61c86964bf3 100644
--- a/public/locales/pt-BR/grafana.json
+++ b/public/locales/pt-BR/grafana.json
@@ -479,6 +479,10 @@
},
"rule-list": {
"configure-datasource": "",
+ "filter-view": {
+ "no-more-results": "",
+ "no-rules-found": ""
+ },
"new-alert-rule": ""
},
"rule-state": {
@@ -1754,6 +1758,12 @@
"infinite-scroll": {
"older-logs": ""
},
+ "log-details": {
+ "fields": "",
+ "links": "",
+ "log-line": "",
+ "no-details": ""
+ },
"log-row-message": {
"ellipsis": "",
"more": ""
diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json
index 7fa6ccf0c88..410e4cdfae4 100644
--- a/public/locales/zh-Hans/grafana.json
+++ b/public/locales/zh-Hans/grafana.json
@@ -475,6 +475,10 @@
},
"rule-list": {
"configure-datasource": "",
+ "filter-view": {
+ "no-more-results": "",
+ "no-rules-found": ""
+ },
"new-alert-rule": ""
},
"rule-state": {
@@ -1744,6 +1748,12 @@
"infinite-scroll": {
"older-logs": ""
},
+ "log-details": {
+ "fields": "",
+ "links": "",
+ "log-line": "",
+ "no-details": ""
+ },
"log-row-message": {
"ellipsis": "",
"more": ""
diff --git a/scripts/cli/reportBettererStats.ts b/scripts/cli/reportBettererStats.mjs
similarity index 74%
rename from scripts/cli/reportBettererStats.ts
rename to scripts/cli/reportBettererStats.mjs
index 135f98c7752..0f1eda1d646 100644
--- a/scripts/cli/reportBettererStats.ts
+++ b/scripts/cli/reportBettererStats.mjs
@@ -1,7 +1,8 @@
+// @ts-check
import { betterer } from '@betterer/betterer';
-import { camelCase } from 'lodash';
+import _ from 'lodash';
-function logStat(name: string, value: number) {
+function logStat(name, value) {
// Note that this output format must match the parsing in ci-frontend-metrics.sh
// which expects the two values to be separated by a space
console.log(`${name} ${value}`);
@@ -12,11 +13,11 @@ async function main() {
for (const testResults of results.resultSummaries) {
const countByMessage = {};
- const name = camelCase(testResults.name);
+ const name = _.camelCase(testResults.name);
Object.values(testResults.details)
.flatMap((v) => v)
.forEach((detail) => {
- const message = camelCase(detail.message);
+ const message = _.camelCase(detail.message);
const metricName = `${name}_${message}`;
if (metricName in countByMessage) {
countByMessage[metricName]++;
@@ -25,7 +26,7 @@ async function main() {
}
});
- for (const [metricName, count] of Object.entries(countByMessage)) {
+ for (const [metricName, count] of Object.entries(countByMessage)) {
logStat(metricName, count);
}
}
diff --git a/scripts/drone/variables.star b/scripts/drone/variables.star
index 1ab4fc7590d..bd9536ee040 100644
--- a/scripts/drone/variables.star
+++ b/scripts/drone/variables.star
@@ -6,4 +6,4 @@ grabpl_version = "v3.1.1"
golang_version = "1.23.1"
# nodejs_version should match what's in ".nvmrc", but without the v prefix.
-nodejs_version = "20.9.0"
+nodejs_version = "22.11.0"
diff --git a/yarn.lock b/yarn.lock
index 75f2246a467..a3045d4ab9f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1412,9 +1412,9 @@ __metadata:
languageName: node
linkType: hard
-"@babel/preset-react@npm:7.25.9":
- version: 7.25.9
- resolution: "@babel/preset-react@npm:7.25.9"
+"@babel/preset-react@npm:7.26.3":
+ version: 7.26.3
+ resolution: "@babel/preset-react@npm:7.26.3"
dependencies:
"@babel/helper-plugin-utils": "npm:^7.25.9"
"@babel/helper-validator-option": "npm:^7.25.9"
@@ -1424,7 +1424,7 @@ __metadata:
"@babel/plugin-transform-react-pure-annotations": "npm:^7.25.9"
peerDependencies:
"@babel/core": ^7.0.0-0
- checksum: 10/3c9daf47cf51568d96984d21b9f83992590c0e91f16a333f999100bb3c2c200730cde6806ed37fd2c999e0a63becefc881740b8f765b5a4aff4efc674e3e4197
+ checksum: 10/88cb78c402b79f32389ee06451da51698d5b1da7641d9a47482883f537fe5441a138bd4c077d8533fd6d557406b08911c47b94402cea843db598e020bdd9a373
languageName: node
linkType: hard
@@ -1858,16 +1858,16 @@ __metadata:
languageName: node
linkType: hard
-"@emotion/cache@npm:^11.11.0, @emotion/cache@npm:^11.13.5, @emotion/cache@npm:^11.4.0":
- version: 11.13.5
- resolution: "@emotion/cache@npm:11.13.5"
+"@emotion/cache@npm:^11.11.0, @emotion/cache@npm:^11.13.5, @emotion/cache@npm:^11.14.0, @emotion/cache@npm:^11.4.0":
+ version: 11.14.0
+ resolution: "@emotion/cache@npm:11.14.0"
dependencies:
"@emotion/memoize": "npm:^0.9.0"
"@emotion/sheet": "npm:^1.4.0"
"@emotion/utils": "npm:^1.4.2"
"@emotion/weak-memoize": "npm:^0.4.0"
stylis: "npm:4.2.0"
- checksum: 10/d91139453d279cfd6f6f38180d3af2fdcee8c0fc6d9a6faa2cdce9a1211b294a8019ef45365bf1171e0687d1744a70ff760637b88ed46f7a9fe74db9dc36f4df
+ checksum: 10/52336b28a27b07dde8fcdfd80851cbd1487672bbd4db1e24cca1440c95d8a6a968c57b0453c2b7c88d9b432b717f99554dbecc05b5cdef27933299827e69fd8e
languageName: node
linkType: hard
@@ -1938,15 +1938,15 @@ __metadata:
languageName: node
linkType: hard
-"@emotion/react@npm:11.13.5, @emotion/react@npm:^11.8.1":
- version: 11.13.5
- resolution: "@emotion/react@npm:11.13.5"
+"@emotion/react@npm:11.14.0, @emotion/react@npm:^11.8.1":
+ version: 11.14.0
+ resolution: "@emotion/react@npm:11.14.0"
dependencies:
"@babel/runtime": "npm:^7.18.3"
"@emotion/babel-plugin": "npm:^11.13.5"
- "@emotion/cache": "npm:^11.13.5"
+ "@emotion/cache": "npm:^11.14.0"
"@emotion/serialize": "npm:^1.3.3"
- "@emotion/use-insertion-effect-with-fallbacks": "npm:^1.1.0"
+ "@emotion/use-insertion-effect-with-fallbacks": "npm:^1.2.0"
"@emotion/utils": "npm:^1.4.2"
"@emotion/weak-memoize": "npm:^0.4.0"
hoist-non-react-statics: "npm:^3.3.1"
@@ -1955,7 +1955,7 @@ __metadata:
peerDependenciesMeta:
"@types/react":
optional: true
- checksum: 10/c85bde9c6144be6f243e44d6834b16df8151b91c5136ea28d9d917155d64d1bbfba000ae98866fd9f5089546f0b0655c20b9e4280cdc5ccf39acc4a2fde37a39
+ checksum: 10/3356c1d66f37f4e7abf88a2be843f6023b794b286c9c99a0aaf1cd1b2b7c50f8d80a2ef77183da737de70150f638e698ff4a2a38ab2d922f868615f1d5761c37
languageName: node
linkType: hard
@@ -1986,12 +1986,12 @@ __metadata:
languageName: node
linkType: hard
-"@emotion/use-insertion-effect-with-fallbacks@npm:^1.1.0":
- version: 1.1.0
- resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.1.0"
+"@emotion/use-insertion-effect-with-fallbacks@npm:^1.2.0":
+ version: 1.2.0
+ resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.2.0"
peerDependencies:
react: ">=16.8.0"
- checksum: 10/33a10f44a873b3f5ccd2a1a3d13c2f34ed628f5a2be1ccf28540a86535a14d3a930afcbef209d48346a22ec60ff48f43c86ee9c846b9480d23a55a17145da66c
+ checksum: 10/2374999db8d53ef661d61ed1026c42a849632e4f03826f7eba0314c1d92ae342161d737f5045453aa46dd4008e13ccefeba68d3165b667dfad8e5784fcb0c643
languageName: node
linkType: hard
@@ -2601,9 +2601,9 @@ __metadata:
linkType: hard
"@faker-js/faker@npm:^9.0.0":
- version: 9.2.0
- resolution: "@faker-js/faker@npm:9.2.0"
- checksum: 10/c233aaa41ebc4f4bed36c1304c399c424b9e5abca9cb8fe54ce73d4162ec7acd66f294b90c85ed7a5df774400e44f0a10364bdd41f49e72cb519a48b5948939f
+ version: 9.3.0
+ resolution: "@faker-js/faker@npm:9.3.0"
+ checksum: 10/fa0718766dc46dea06602b1acdccdf3714e5a076f02eab6dbe26d7dc421ccc43a2e7e08767f7832de963fa2a273c2d9e5d4a2a504247ea5fba119a713efc145c
languageName: node
linkType: hard
@@ -2800,7 +2800,7 @@ __metadata:
"@testing-library/user-event": "npm:14.5.2"
"@types/jest": "npm:29.5.14"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/prismjs": "npm:1.26.5"
"@types/react": "npm:18.3.3"
"@types/react-dom": "npm:18.2.25"
@@ -2842,7 +2842,7 @@ __metadata:
"@testing-library/user-event": "npm:14.5.2"
"@types/jest": "npm:29.5.14"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/react": "npm:18.3.3"
lodash: "npm:4.17.21"
react: "npm:18.2.0"
@@ -2872,7 +2872,7 @@ __metadata:
"@testing-library/user-event": "npm:14.5.2"
"@types/jest": "npm:29.5.14"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/prismjs": "npm:1.26.5"
"@types/react": "npm:18.3.3"
"@types/react-dom": "npm:18.2.25"
@@ -2914,7 +2914,7 @@ __metadata:
"@types/d3-random": "npm:^3.0.2"
"@types/jest": "npm:29.5.14"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/react": "npm:18.3.3"
"@types/react-dom": "npm:18.2.25"
"@types/uuid": "npm:9.0.8"
@@ -2955,7 +2955,7 @@ __metadata:
"@types/jest": "npm:29.5.14"
"@types/lodash": "npm:4.17.13"
"@types/logfmt": "npm:^1.2.3"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/react": "npm:18.3.3"
"@types/react-dom": "npm:18.2.25"
"@types/react-window": "npm:1.8.8"
@@ -2995,7 +2995,7 @@ __metadata:
"@testing-library/user-event": "npm:14.5.2"
"@types/jest": "npm:29.5.14"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/react": "npm:18.3.3"
lodash: "npm:4.17.21"
react: "npm:18.2.0"
@@ -3026,7 +3026,7 @@ __metadata:
"@testing-library/user-event": "npm:14.5.2"
"@types/jest": "npm:29.5.14"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/react": "npm:18.3.3"
lodash: "npm:4.17.21"
react: "npm:18.2.0"
@@ -3054,7 +3054,7 @@ __metadata:
"@testing-library/react": "npm:16.0.1"
"@testing-library/user-event": "npm:14.5.2"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/react": "npm:18.3.3"
"@types/react-dom": "npm:18.2.25"
lodash: "npm:4.17.21"
@@ -3092,7 +3092,7 @@ __metadata:
"@types/debounce-promise": "npm:3.1.9"
"@types/jest": "npm:29.5.14"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/prismjs": "npm:1.26.5"
"@types/react": "npm:18.3.3"
"@types/react-dom": "npm:18.2.25"
@@ -3147,7 +3147,7 @@ __metadata:
"@testing-library/user-event": "npm:14.5.2"
"@types/jest": "npm:29.5.14"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/prismjs": "npm:1.26.5"
"@types/react": "npm:18.3.3"
"@types/react-dom": "npm:18.2.25"
@@ -3197,7 +3197,7 @@ __metadata:
"@testing-library/react": "npm:16.0.1"
"@types/jest": "npm:29.5.14"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/react": "npm:18.3.3"
"@types/react-dom": "npm:18.2.25"
lodash: "npm:4.17.21"
@@ -3256,7 +3256,7 @@ __metadata:
"@types/dompurify": "npm:^3.0.0"
"@types/history": "npm:4.7.11"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/papaparse": "npm:5.3.15"
"@types/react": "npm:18.3.3"
"@types/react-dom": "npm:18.2.25"
@@ -3303,7 +3303,7 @@ __metadata:
dependencies:
"@grafana/tsconfig": "npm:^2.0.0"
"@rollup/plugin-node-resolve": "npm:15.3.0"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/semver": "npm:7.5.8"
esbuild: "npm:0.24.0"
rimraf: "npm:6.0.1"
@@ -3450,7 +3450,7 @@ __metadata:
dependencies:
"@babel/core": "npm:7.26.0"
"@babel/preset-env": "npm:7.26.0"
- "@babel/preset-react": "npm:7.25.9"
+ "@babel/preset-react": "npm:7.26.3"
"@emotion/css": "npm:11.13.5"
"@grafana/data": "npm:11.5.0-pre"
"@grafana/tsconfig": "npm:^2.0.0"
@@ -3464,7 +3464,7 @@ __metadata:
"@types/d3": "npm:^7"
"@types/jest": "npm:^29.5.4"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/react": "npm:18.3.3"
"@types/react-virtualized-auto-sizer": "npm:1.0.4"
"@types/tinycolor2": "npm:1.4.6"
@@ -3548,7 +3548,7 @@ __metadata:
"@testing-library/react": "npm:16.0.1"
"@testing-library/user-event": "npm:14.5.2"
"@types/jest": "npm:^29.5.4"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/react": "npm:18.3.3"
"@types/systemjs": "npm:6.15.1"
jest: "npm:^29.6.4"
@@ -3589,16 +3589,16 @@ __metadata:
linkType: soft
"@grafana/plugin-e2e@npm:^1.11.0":
- version: 1.13.0
- resolution: "@grafana/plugin-e2e@npm:1.13.0"
+ version: 1.13.1
+ resolution: "@grafana/plugin-e2e@npm:1.13.1"
dependencies:
- "@grafana/e2e-selectors": "npm:^11.5.0-211617"
+ "@grafana/e2e-selectors": "npm:^11.5.0-212629"
semver: "npm:^7.5.4"
uuid: "npm:^11.0.2"
yaml: "npm:^2.3.4"
peerDependencies:
"@playwright/test": ^1.41.2
- checksum: 10/d6493935b6364eac389d5db69f245ca8f28656a0b77339d8d2ba89c0a8f7dc4c6a4c7fff62f2de8e9789e1319fe5991f131e0b864cfcf1385ddb04956efb5855
+ checksum: 10/22916f41828c1fd446073f7c9fa8b6c4517fc7dd4bfa8428cde3d8a5b99aac9ef08e73815b5c885001114a398d1be43c41948875430626e37c2be471d9999034
languageName: node
linkType: hard
@@ -3638,7 +3638,7 @@ __metadata:
"@types/jest": "npm:29.5.14"
"@types/jquery": "npm:3.5.32"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/pluralize": "npm:^0.0.33"
"@types/prismjs": "npm:1.26.5"
"@types/react": "npm:18.3.3"
@@ -3770,7 +3770,7 @@ __metadata:
"@svgr/plugin-prettier": "npm:^8.1.0"
"@svgr/plugin-svgo": "npm:^8.1.0"
"@types/babel__core": "npm:^7"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/react": "npm:18.3.3"
"@types/react-dom": "npm:18.2.25"
esbuild: "npm:0.24.0"
@@ -3869,7 +3869,7 @@ __metadata:
"@testing-library/user-event": "npm:14.5.2"
"@types/jest": "npm:^29.5.4"
"@types/lodash": "npm:4.17.13"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/react": "npm:18.3.3"
"@types/react-dom": "npm:18.2.25"
"@types/react-virtualized-auto-sizer": "npm:1.0.4"
@@ -3908,7 +3908,7 @@ __metadata:
dependencies:
"@babel/core": "npm:7.26.0"
"@emotion/css": "npm:11.13.5"
- "@emotion/react": "npm:11.13.5"
+ "@emotion/react": "npm:11.14.0"
"@emotion/serialize": "npm:1.3.3"
"@faker-js/faker": "npm:^9.0.0"
"@floating-ui/react": "npm:0.26.28"
@@ -3921,10 +3921,10 @@ __metadata:
"@leeoniya/ufuzzy": "npm:1.0.17"
"@monaco-editor/react": "npm:4.6.0"
"@popperjs/core": "npm:2.11.8"
- "@react-aria/dialog": "npm:3.5.19"
- "@react-aria/focus": "npm:3.18.4"
- "@react-aria/overlays": "npm:3.23.4"
- "@react-aria/utils": "npm:3.25.3"
+ "@react-aria/dialog": "npm:3.5.20"
+ "@react-aria/focus": "npm:3.19.0"
+ "@react-aria/overlays": "npm:3.24.0"
+ "@react-aria/utils": "npm:3.26.0"
"@rollup/plugin-node-resolve": "npm:15.3.0"
"@storybook/addon-a11y": "npm:^8.4.2"
"@storybook/addon-actions": "npm:^8.4.2"
@@ -3956,15 +3956,15 @@ __metadata:
"@types/jquery": "npm:3.5.32"
"@types/lodash": "npm:4.17.13"
"@types/mock-raf": "npm:1.0.6"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/prismjs": "npm:1.26.5"
"@types/react": "npm:18.3.3"
- "@types/react-color": "npm:3.0.12"
+ "@types/react-color": "npm:3.0.13"
"@types/react-dom": "npm:18.2.25"
"@types/react-highlight-words": "npm:0.20.0"
"@types/react-table": "npm:7.7.20"
"@types/react-test-renderer": "npm:18.3.0"
- "@types/react-transition-group": "npm:4.4.11"
+ "@types/react-transition-group": "npm:4.4.12"
"@types/react-window": "npm:1.8.8"
"@types/slate": "npm:0.47.11"
"@types/slate-plain-serializer": "npm:0.7.5"
@@ -4184,40 +4184,40 @@ __metadata:
languageName: node
linkType: hard
-"@internationalized/date@npm:^3.5.6":
- version: 3.5.6
- resolution: "@internationalized/date@npm:3.5.6"
+"@internationalized/date@npm:^3.6.0":
+ version: 3.6.0
+ resolution: "@internationalized/date@npm:3.6.0"
dependencies:
"@swc/helpers": "npm:^0.5.0"
- checksum: 10/54734b53ca74a32aae368a8f963324352b1fd5b13029b6e82555307b8f2ff355658c90e82a4f38f154a3edf874387d1efd26fc80f2edd068ce04f48f6467f26c
+ checksum: 10/862a34376ff1e36da0afa4a667b62183d7a480eb7c5b919b0a6e1450f4162bc8a6c8941e073a4fb0c6fce6d9420894051235f9e604ee1162fae7b59972a64d88
languageName: node
linkType: hard
-"@internationalized/message@npm:^3.1.5":
- version: 3.1.5
- resolution: "@internationalized/message@npm:3.1.5"
+"@internationalized/message@npm:^3.1.6":
+ version: 3.1.6
+ resolution: "@internationalized/message@npm:3.1.6"
dependencies:
"@swc/helpers": "npm:^0.5.0"
intl-messageformat: "npm:^10.1.0"
- checksum: 10/210951fd8055af4db70d465e49bcbbdf2545ed223b936af9c1f18b745a51689ecb0ca49cbd5ee2dbfeccce2447808b7fe309bd12ee81f7e09283f20bf04200e9
+ checksum: 10/31611a81bba7c0b18fad02ccb363632cbef3a0cc6e6e564578dfc7ae42cfde48b0ec18b89bff24390bb748df63d40cd2028483068e7d1492aee0533648a71e04
languageName: node
linkType: hard
-"@internationalized/number@npm:^3.5.4":
- version: 3.5.4
- resolution: "@internationalized/number@npm:3.5.4"
+"@internationalized/number@npm:^3.6.0":
+ version: 3.6.0
+ resolution: "@internationalized/number@npm:3.6.0"
dependencies:
"@swc/helpers": "npm:^0.5.0"
- checksum: 10/16641aecb58c075a6322dc6b36a2c6e521845296f81b86a128d015f072d1af998289b71b4d8b9521e7576bdeabfaf8067a3e741b0116c8595d82a4461c1ae03b
+ checksum: 10/76428f75fd289008580108beef02816f94aaca37d48c9e26c2274fdbb81e8f32c4247ff624b8f854384b18ce2556cc8d817ccf1bc39b0fd87b0da1f3d911209b
languageName: node
linkType: hard
-"@internationalized/string@npm:^3.2.4":
- version: 3.2.4
- resolution: "@internationalized/string@npm:3.2.4"
+"@internationalized/string@npm:^3.2.5":
+ version: 3.2.5
+ resolution: "@internationalized/string@npm:3.2.5"
dependencies:
"@swc/helpers": "npm:^0.5.0"
- checksum: 10/5fdb7f0bf7fa7055cdf62ded4efd6849d3db9cf0e6d53f349889e2ec9517b9135ad38a6bb8dcf25142c69c381618c0dd1a6a072117dd7cf2867ce17374f0f835
+ checksum: 10/7a20359796545cd5724aa546018554b81ea168c58738e98884c54ef0de2fba7b802504c79678d690709b0a435478912aa771c426feed72aae1d93ee782b7ac5d
languageName: node
linkType: hard
@@ -6270,129 +6270,129 @@ __metadata:
languageName: node
linkType: hard
-"@react-aria/dialog@npm:3.5.19":
- version: 3.5.19
- resolution: "@react-aria/dialog@npm:3.5.19"
+"@react-aria/dialog@npm:3.5.20":
+ version: 3.5.20
+ resolution: "@react-aria/dialog@npm:3.5.20"
dependencies:
- "@react-aria/focus": "npm:^3.18.4"
- "@react-aria/overlays": "npm:^3.23.4"
- "@react-aria/utils": "npm:^3.25.3"
- "@react-types/dialog": "npm:^3.5.13"
- "@react-types/shared": "npm:^3.25.0"
+ "@react-aria/focus": "npm:^3.19.0"
+ "@react-aria/overlays": "npm:^3.24.0"
+ "@react-aria/utils": "npm:^3.26.0"
+ "@react-types/dialog": "npm:^3.5.14"
+ "@react-types/shared": "npm:^3.26.0"
"@swc/helpers": "npm:^0.5.0"
peerDependencies:
- react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
- react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
- checksum: 10/44eba70f927cbdb003da37021ee6dde363abfa3c179437d83e19c72931c0fab12db20dbd603578d372df534ddce119e22b6908935544ce7a696523aa71b72e2e
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+ react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+ checksum: 10/a3e18df7495fc659d46c0ae61ae6addcd55fc6e512f0562c3e31d9e43cb5a95f7f49f6e840894b594839533681701606af804e70ff4c4b22ac4c7f0224f3c817
languageName: node
linkType: hard
-"@react-aria/focus@npm:3.18.4, @react-aria/focus@npm:^3.18.4":
- version: 3.18.4
- resolution: "@react-aria/focus@npm:3.18.4"
+"@react-aria/focus@npm:3.19.0, @react-aria/focus@npm:^3.19.0":
+ version: 3.19.0
+ resolution: "@react-aria/focus@npm:3.19.0"
dependencies:
- "@react-aria/interactions": "npm:^3.22.4"
- "@react-aria/utils": "npm:^3.25.3"
- "@react-types/shared": "npm:^3.25.0"
+ "@react-aria/interactions": "npm:^3.22.5"
+ "@react-aria/utils": "npm:^3.26.0"
+ "@react-types/shared": "npm:^3.26.0"
"@swc/helpers": "npm:^0.5.0"
clsx: "npm:^2.0.0"
peerDependencies:
- react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
- checksum: 10/912cd8a98cbe978240991bec8077c7956ca03ee78cb10152c7a1131a53fb622a5c9b87a4047909f032a7550c37ed9ec50488437a17c761c5c852b721cbaa0bd2
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+ checksum: 10/4b507c382ce83b698637464094257af23abdfa1a096486cdf898d0e8db120dcd87035b59dab8855180cc1671e89d410b8812936100a1502ead01f80b52dfa4e9
languageName: node
linkType: hard
-"@react-aria/i18n@npm:^3.12.3":
- version: 3.12.3
- resolution: "@react-aria/i18n@npm:3.12.3"
+"@react-aria/i18n@npm:^3.12.4":
+ version: 3.12.4
+ resolution: "@react-aria/i18n@npm:3.12.4"
dependencies:
- "@internationalized/date": "npm:^3.5.6"
- "@internationalized/message": "npm:^3.1.5"
- "@internationalized/number": "npm:^3.5.4"
- "@internationalized/string": "npm:^3.2.4"
- "@react-aria/ssr": "npm:^3.9.6"
- "@react-aria/utils": "npm:^3.25.3"
- "@react-types/shared": "npm:^3.25.0"
+ "@internationalized/date": "npm:^3.6.0"
+ "@internationalized/message": "npm:^3.1.6"
+ "@internationalized/number": "npm:^3.6.0"
+ "@internationalized/string": "npm:^3.2.5"
+ "@react-aria/ssr": "npm:^3.9.7"
+ "@react-aria/utils": "npm:^3.26.0"
+ "@react-types/shared": "npm:^3.26.0"
"@swc/helpers": "npm:^0.5.0"
peerDependencies:
- react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
- checksum: 10/54f111d9a9da68edcb8b821f7c8ead92f0c4d85307dbabee78bc5c89f5a19cdfa406b1e40b7c6f9dc26f7cedce4c9c5a10f8dcdae289e5a404c07b6fdda98aba
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+ checksum: 10/3a62d641ed23b96378d82f252cecf17071ca842a2d964e35c9764a7ad1f7f4815472e8f9bf54fe094d15e81f3fb4b982b2b2949298f79ef1a7e2e6b538e1a12a
languageName: node
linkType: hard
-"@react-aria/interactions@npm:^3.22.4":
- version: 3.22.4
- resolution: "@react-aria/interactions@npm:3.22.4"
+"@react-aria/interactions@npm:^3.22.5":
+ version: 3.22.5
+ resolution: "@react-aria/interactions@npm:3.22.5"
dependencies:
- "@react-aria/ssr": "npm:^3.9.6"
- "@react-aria/utils": "npm:^3.25.3"
- "@react-types/shared": "npm:^3.25.0"
+ "@react-aria/ssr": "npm:^3.9.7"
+ "@react-aria/utils": "npm:^3.26.0"
+ "@react-types/shared": "npm:^3.26.0"
"@swc/helpers": "npm:^0.5.0"
peerDependencies:
- react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
- checksum: 10/095d084bd642b47a5cc2a846fa50e0953682ddcad694cc78df344b1f235e292945746692f84d27f465f7ff0117b485c3f5b69f050be196df0c3e7343d3239551
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+ checksum: 10/058fbbfd295471bde94cbe6501d5ebb64d1371c455e43c8c55daf6edd02983f73eb1eb716dd03b96c275aed26fa16a355a35f3daffefc54904576d5d26b4458e
languageName: node
linkType: hard
-"@react-aria/overlays@npm:3.23.4, @react-aria/overlays@npm:^3.23.4":
- version: 3.23.4
- resolution: "@react-aria/overlays@npm:3.23.4"
+"@react-aria/overlays@npm:3.24.0, @react-aria/overlays@npm:^3.24.0":
+ version: 3.24.0
+ resolution: "@react-aria/overlays@npm:3.24.0"
dependencies:
- "@react-aria/focus": "npm:^3.18.4"
- "@react-aria/i18n": "npm:^3.12.3"
- "@react-aria/interactions": "npm:^3.22.4"
- "@react-aria/ssr": "npm:^3.9.6"
- "@react-aria/utils": "npm:^3.25.3"
- "@react-aria/visually-hidden": "npm:^3.8.17"
- "@react-stately/overlays": "npm:^3.6.11"
- "@react-types/button": "npm:^3.10.0"
- "@react-types/overlays": "npm:^3.8.10"
- "@react-types/shared": "npm:^3.25.0"
+ "@react-aria/focus": "npm:^3.19.0"
+ "@react-aria/i18n": "npm:^3.12.4"
+ "@react-aria/interactions": "npm:^3.22.5"
+ "@react-aria/ssr": "npm:^3.9.7"
+ "@react-aria/utils": "npm:^3.26.0"
+ "@react-aria/visually-hidden": "npm:^3.8.18"
+ "@react-stately/overlays": "npm:^3.6.12"
+ "@react-types/button": "npm:^3.10.1"
+ "@react-types/overlays": "npm:^3.8.11"
+ "@react-types/shared": "npm:^3.26.0"
"@swc/helpers": "npm:^0.5.0"
peerDependencies:
- react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
- react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
- checksum: 10/6f641a1f7c1976758dd062c4345d2e8882575e2b645a4e4693f051699bae48c48979db3539e77c387aee64600d7a555b94687cd01449094cf875ce80ec6aa2ed
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+ react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+ checksum: 10/32bb00a435af82dfb57561737082fb0ac2361954fe217be25c84011bdaf23e683201b0142ba3d1137c90be713894b94c68ae8db4dc269cb09f449673ffa7dcb6
languageName: node
linkType: hard
-"@react-aria/ssr@npm:^3.9.6":
- version: 3.9.6
- resolution: "@react-aria/ssr@npm:3.9.6"
+"@react-aria/ssr@npm:^3.9.7":
+ version: 3.9.7
+ resolution: "@react-aria/ssr@npm:3.9.7"
dependencies:
"@swc/helpers": "npm:^0.5.0"
peerDependencies:
- react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
- checksum: 10/ea6b290346ce1e119ed9233fc0e34693d52ab9dc2509f07ab10710409b89484a544b7f26c1438802e97f3fb634844ae54638850cdd95caca0d1f5571781bf982
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+ checksum: 10/a5c8e9ffee1dfd3c5b9f66051a7faab11d53ba001ac7f476b61fa4b38fd8b4835c1a85ff2157ec25fb5b63beb88fbae9e80610fa065a30cbe30875fcbca3114b
languageName: node
linkType: hard
-"@react-aria/utils@npm:3.25.3, @react-aria/utils@npm:^3.25.3":
- version: 3.25.3
- resolution: "@react-aria/utils@npm:3.25.3"
+"@react-aria/utils@npm:3.26.0, @react-aria/utils@npm:^3.26.0":
+ version: 3.26.0
+ resolution: "@react-aria/utils@npm:3.26.0"
dependencies:
- "@react-aria/ssr": "npm:^3.9.6"
- "@react-stately/utils": "npm:^3.10.4"
- "@react-types/shared": "npm:^3.25.0"
+ "@react-aria/ssr": "npm:^3.9.7"
+ "@react-stately/utils": "npm:^3.10.5"
+ "@react-types/shared": "npm:^3.26.0"
"@swc/helpers": "npm:^0.5.0"
clsx: "npm:^2.0.0"
peerDependencies:
- react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
- checksum: 10/86aed35da5cb0d48d949e40bf8226d5a6d6c92a8cdc60e3e12d524d1f3cc91ab6b54c5e1642823773cbb889fb61af7da22e89488b704b56fc5f4d8d59da7519b
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+ checksum: 10/484cfbc553b3313d291a78bbfd3fcc435ce36b7fc5408935be027f22f41e73b69306d67aa0bcf5e010f0059d4a6776217110f95012001699fc9d37da54963c7b
languageName: node
linkType: hard
-"@react-aria/visually-hidden@npm:^3.8.17":
- version: 3.8.17
- resolution: "@react-aria/visually-hidden@npm:3.8.17"
+"@react-aria/visually-hidden@npm:^3.8.18":
+ version: 3.8.18
+ resolution: "@react-aria/visually-hidden@npm:3.8.18"
dependencies:
- "@react-aria/interactions": "npm:^3.22.4"
- "@react-aria/utils": "npm:^3.25.3"
- "@react-types/shared": "npm:^3.25.0"
+ "@react-aria/interactions": "npm:^3.22.5"
+ "@react-aria/utils": "npm:^3.26.0"
+ "@react-types/shared": "npm:^3.26.0"
"@swc/helpers": "npm:^0.5.0"
peerDependencies:
- react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
- checksum: 10/d9cdc97d80e750f4582cc3f2bcc0bde12b6c70fbd408ca9ebba2ef654ab27b0181cdd072a8b30ae36f14d9ec16a79c2d38d7cbe834c96fad693daebc7be41616
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+ checksum: 10/4f068dbeeaf3751c526bf7d1467d9eeca43a6e665d5e9245b3556e9c5a43f28eb3c14ff56fffeb0eecbf6282eae0cc17824249bfba9199d97d23374ee733e82e
languageName: node
linkType: hard
@@ -6430,31 +6430,31 @@ __metadata:
languageName: node
linkType: hard
-"@react-stately/overlays@npm:^3.6.11":
- version: 3.6.11
- resolution: "@react-stately/overlays@npm:3.6.11"
+"@react-stately/overlays@npm:^3.6.12":
+ version: 3.6.12
+ resolution: "@react-stately/overlays@npm:3.6.12"
dependencies:
- "@react-stately/utils": "npm:^3.10.4"
- "@react-types/overlays": "npm:^3.8.10"
+ "@react-stately/utils": "npm:^3.10.5"
+ "@react-types/overlays": "npm:^3.8.11"
"@swc/helpers": "npm:^0.5.0"
peerDependencies:
- react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
- checksum: 10/98190b4b0ced5c94d924cf97b5d43a6e28f68aa44de7bb789c20354f30f00309c86089fb6948b5ec9d09f01605b5a412fb246545b7ee9bc34e3183e7261a2805
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+ checksum: 10/78c448964fea85d148a6d45c723d26df765ca049445077b85a318b1a10f1b888d543318a251ff80f28e9109da2c722160be4657eae5dae0c2865f24039fecb11
languageName: node
linkType: hard
-"@react-stately/utils@npm:^3.10.4":
- version: 3.10.4
- resolution: "@react-stately/utils@npm:3.10.4"
+"@react-stately/utils@npm:^3.10.5":
+ version: 3.10.5
+ resolution: "@react-stately/utils@npm:3.10.5"
dependencies:
"@swc/helpers": "npm:^0.5.0"
peerDependencies:
- react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
- checksum: 10/8a56b4d0cf8d5a7a692d6f94ffff63feac2d7078fbc5642b94b0afcaaf7c8f7f4682cfe546f98265034c52576c198be5502cff3f9b145137884e50eb9ffb96d5
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+ checksum: 10/76133eb64fa945216e51d8a81a0ebd06eeb78aa2d9c91d79eeb80ff44c70a6b0d6d940618b31b499fee8216640b3bf6183391151dc1769e756b56ff6b5e167ec
languageName: node
linkType: hard
-"@react-types/button@npm:3.10.1, @react-types/button@npm:^3.10.0":
+"@react-types/button@npm:3.10.1, @react-types/button@npm:^3.10.1":
version: 3.10.1
resolution: "@react-types/button@npm:3.10.1"
dependencies:
@@ -6465,15 +6465,15 @@ __metadata:
languageName: node
linkType: hard
-"@react-types/dialog@npm:^3.5.13":
- version: 3.5.13
- resolution: "@react-types/dialog@npm:3.5.13"
+"@react-types/dialog@npm:^3.5.14":
+ version: 3.5.14
+ resolution: "@react-types/dialog@npm:3.5.14"
dependencies:
- "@react-types/overlays": "npm:^3.8.10"
- "@react-types/shared": "npm:^3.25.0"
+ "@react-types/overlays": "npm:^3.8.11"
+ "@react-types/shared": "npm:^3.26.0"
peerDependencies:
- react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
- checksum: 10/c7ee923c326b7377660400ec794ef98330388250aa32df23f5c22a63e483bcad221283de26181bf2a5cab2c20d517f528bf351c9786ac9fece3e3726e4ae07c3
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+ checksum: 10/4339a86c862832860eeb9be102a77cd1119d1ef968bb51603d8d789ee26404a28fde047203c4b579ee0ba2975cf36a8991cfc750449222c0fa150eed84464499
languageName: node
linkType: hard
@@ -6489,7 +6489,7 @@ __metadata:
languageName: node
linkType: hard
-"@react-types/overlays@npm:3.8.11, @react-types/overlays@npm:^3.8.10, @react-types/overlays@npm:^3.8.11":
+"@react-types/overlays@npm:3.8.11, @react-types/overlays@npm:^3.8.11":
version: 3.8.11
resolution: "@react-types/overlays@npm:3.8.11"
dependencies:
@@ -6509,7 +6509,7 @@ __metadata:
languageName: node
linkType: hard
-"@react-types/shared@npm:^3.25.0, @react-types/shared@npm:^3.26.0":
+"@react-types/shared@npm:^3.26.0":
version: 3.26.0
resolution: "@react-types/shared@npm:3.26.0"
peerDependencies:
@@ -8409,7 +8409,7 @@ __metadata:
"@grafana/schema": "workspace:*"
"@grafana/ui": "workspace:*"
"@types/lodash": "npm:4.17.7"
- "@types/node": "npm:20.14.14"
+ "@types/node": "npm:22.10.2"
"@types/prismjs": "npm:1.26.4"
"@types/react": "npm:18.3.3"
"@types/react-dom": "npm:18.2.25"
@@ -8441,7 +8441,7 @@ __metadata:
"@grafana/schema": "workspace:*"
"@grafana/ui": "workspace:*"
"@types/lodash": "npm:4.17.7"
- "@types/node": "npm:20.14.14"
+ "@types/node": "npm:22.10.2"
"@types/prismjs": "npm:1.26.4"
"@types/react": "npm:18.3.3"
"@types/react-dom": "npm:18.2.25"
@@ -9511,30 +9511,12 @@ __metadata:
languageName: node
linkType: hard
-"@types/node@npm:*, @types/node@npm:>=10.0.0, @types/node@npm:>=13.7.0, @types/node@npm:>=13.7.4, @types/node@npm:^22.0.0":
- version: 22.10.1
- resolution: "@types/node@npm:22.10.1"
+"@types/node@npm:*, @types/node@npm:22.10.2, @types/node@npm:>=10.0.0, @types/node@npm:>=13.7.0, @types/node@npm:>=13.7.4, @types/node@npm:^22.0.0":
+ version: 22.10.2
+ resolution: "@types/node@npm:22.10.2"
dependencies:
undici-types: "npm:~6.20.0"
- checksum: 10/c802a526da2f3fa3ccefd00a71244e7cb825329951719e79e8fec62b1dbc2855388c830489770611584665ce10be23c05ed585982038b24924e1ba2c2cce03fd
- languageName: node
- linkType: hard
-
-"@types/node@npm:20.14.14":
- version: 20.14.14
- resolution: "@types/node@npm:20.14.14"
- dependencies:
- undici-types: "npm:~5.26.4"
- checksum: 10/035bc347e3de04888d537801e23eb4b4f99522975ca002dbfef978edd853710031b7cd43bf022670d6aba4ed5d4ac75ea1b5ff77ff8f80998bffd943b7bcef48
- languageName: node
- linkType: hard
-
-"@types/node@npm:20.16.15":
- version: 20.16.15
- resolution: "@types/node@npm:20.16.15"
- dependencies:
- undici-types: "npm:~6.19.2"
- checksum: 10/f4706676fdff05c7fb188399da578bbcce9260f889cfd767f1c3ee4fb33c369eb1ffa24161651ea3ef5b6a33e2e9b66f393949cab2be77d019277a1e16a7af36
+ checksum: 10/451adfefed4add58b069407173e616220fd4aaa3307cdde1bb701aa053b65b54ced8483db2f870dcedec7a58cb3b06101fbc19d85852716672ec1fd3660947fa
languageName: node
linkType: hard
@@ -9644,13 +9626,14 @@ __metadata:
languageName: node
linkType: hard
-"@types/react-color@npm:3.0.12":
- version: 3.0.12
- resolution: "@types/react-color@npm:3.0.12"
+"@types/react-color@npm:3.0.13":
+ version: 3.0.13
+ resolution: "@types/react-color@npm:3.0.13"
dependencies:
- "@types/react": "npm:*"
"@types/reactcss": "npm:*"
- checksum: 10/d8ed71d297d026faded787588720ee5e158edfa45ee23c5e517d8bb1441ccaa25f8a0c88069fd63829f7263bbbdc78a7ae2df5957061847a2d2c6351c46c13dd
+ peerDependencies:
+ "@types/react": "*"
+ checksum: 10/2e1500c915d4ae3916935993320e77c13d687bca8833525d011024e355c1ccfc6c4091fb4563ae1f92c591494c3a9fa3acb1e58a60a5d967ded87600f94e5d8b
languageName: node
linkType: hard
@@ -9729,12 +9712,12 @@ __metadata:
languageName: node
linkType: hard
-"@types/react-transition-group@npm:4.4.11, @types/react-transition-group@npm:^4.4.0":
- version: 4.4.11
- resolution: "@types/react-transition-group@npm:4.4.11"
- dependencies:
- "@types/react": "npm:*"
- checksum: 10/a7f4de6e5f57d9fcdea027e22873c633f96a803c96d422db8b99a45c36a9cceb7882d152136bbc31c7158fc1827e37aea5070d369724bb71dd11b5687332bc4d
+"@types/react-transition-group@npm:4.4.12, @types/react-transition-group@npm:^4.4.0":
+ version: 4.4.12
+ resolution: "@types/react-transition-group@npm:4.4.12"
+ peerDependencies:
+ "@types/react": "*"
+ checksum: 10/ea14bc84f529a3887f9954b753843820ac8a3c49fcdfec7840657ecc6a8800aad98afdbe4b973eb96c7252286bde38476fcf64b1c09527354a9a9366e516d9a2
languageName: node
linkType: hard
@@ -17551,7 +17534,7 @@ __metadata:
"@cypress/webpack-preprocessor": "npm:6.0.2"
"@emotion/css": "npm:11.13.5"
"@emotion/eslint-plugin": "npm:11.12.0"
- "@emotion/react": "npm:11.13.5"
+ "@emotion/react": "npm:11.14.0"
"@fingerprintjs/fingerprintjs": "npm:^3.4.2"
"@floating-ui/react": "npm:0.26.28"
"@formatjs/intl-durationformat": "npm:^0.6.0"
@@ -17599,10 +17582,10 @@ __metadata:
"@playwright/test": "npm:1.48.2"
"@pmmmwh/react-refresh-webpack-plugin": "npm:0.5.15"
"@popperjs/core": "npm:2.11.8"
- "@react-aria/dialog": "npm:3.5.19"
- "@react-aria/focus": "npm:3.18.4"
- "@react-aria/overlays": "npm:3.23.4"
- "@react-aria/utils": "npm:3.25.3"
+ "@react-aria/dialog": "npm:3.5.20"
+ "@react-aria/focus": "npm:3.19.0"
+ "@react-aria/overlays": "npm:3.24.0"
+ "@react-aria/utils": "npm:3.26.0"
"@react-awesome-query-builder/ui": "npm:6.6.4"
"@react-types/button": "npm:3.10.1"
"@react-types/menu": "npm:3.9.13"
@@ -17646,7 +17629,7 @@ __metadata:
"@types/lodash": "npm:4.17.13"
"@types/logfmt": "npm:^1.2.3"
"@types/lucene": "npm:^2"
- "@types/node": "npm:20.16.15"
+ "@types/node": "npm:22.10.2"
"@types/node-forge": "npm:^1"
"@types/ol-ext": "npm:@siedlerchr/types-ol-ext@3.2.4"
"@types/pluralize": "npm:^0.0.33"
@@ -17660,7 +17643,7 @@ __metadata:
"@types/react-router-dom": "npm:5.3.3"
"@types/react-table": "npm:7.7.20"
"@types/react-test-renderer": "npm:18.3.0"
- "@types/react-transition-group": "npm:4.4.11"
+ "@types/react-transition-group": "npm:4.4.12"
"@types/react-virtualized-auto-sizer": "npm:1.0.4"
"@types/react-window": "npm:1.8.8"
"@types/react-window-infinite-loader": "npm:^1"
@@ -29808,20 +29791,6 @@ __metadata:
languageName: node
linkType: hard
-"undici-types@npm:~5.26.4":
- version: 5.26.5
- resolution: "undici-types@npm:5.26.5"
- checksum: 10/0097779d94bc0fd26f0418b3a05472410408877279141ded2bd449167be1aed7ea5b76f756562cb3586a07f251b90799bab22d9019ceba49c037c76445f7cddd
- languageName: node
- linkType: hard
-
-"undici-types@npm:~6.19.2":
- version: 6.19.8
- resolution: "undici-types@npm:6.19.8"
- checksum: 10/cf0b48ed4fc99baf56584afa91aaffa5010c268b8842f62e02f752df209e3dea138b372a60a963b3b2576ed932f32329ce7ddb9cb5f27a6c83040d8cd74b7a70
- languageName: node
- linkType: hard
-
"undici-types@npm:~6.20.0":
version: 6.20.0
resolution: "undici-types@npm:6.20.0"
| |