mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Field: getFieldTitle as field / series display identity and use it in all field name matchers & field / series name displays (#24024)
* common title handling * show labels * update comment * Update changelog for v7.0.0-beta1 (#24007) Co-Authored-By: Marcus Efraimsson <marcus.efraimsson@gmail.com> Co-Authored-By: Andrej Ocenas <mr.ocenas@gmail.com> Co-Authored-By: Hugo Häggmark <hugo.haggmark@grafana.com> Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com> * verify-repo-update: Fix Dockerfile.deb (#24030) Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CircleCI: Upgrade build pipeline tool (#24021) * CircleCI: Upgrade build pipeline tool * Devenv: ignore enterprise (#24037) * Add header icon to Add data source page (#24033) * latest.json: Update testing version (#24038) Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix login page redirected from password reset (#24032) * Storybook: Rewrite stories to CSF (#23989) * ColorPicker to CSF format * Convert stories to CSF * Do not export ClipboardButton * Update ConfirmButton * Remove unused imports * Fix feedback * changelog enterprise 7.0.0-beta1 (#24039) * CircleCI: Bump grafana/build-container revision (#24043) Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Changelog: Updates changelog with more feature details (#24040) * Changelog: Updates changelog with more feature details * spell fix * spell fix * Updates * Readme update * Updates * Select: fixes so component loses focus on selecting value or pressing outside of input. (#24008) * changed the value container to a class component to get it to work with focus (maybe something with context?). * added e2e tests to verify that the select focus is working as it should. * fixed according to feedback. * updated snapshot. * Devenv: add remote renderer to grafana (#24050) * NewPanelEditor: minor UI twekas (#24042) * Forward ref for tabs, use html props * Inspect: add inspect label to drawer title * Add tooltips to sidebar pane tabs, copy changes * Remove unused import * Place tooltips over tabs * Inspector: dont show transformations select if there is only one data frame * Review * Changelog: Add a breaking change (#24051) Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * CircleCI: Unpin grafana/docs-base (#24054) Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Search: close overlay on Esc press (#24003) * Search: Close on Esc * Search: Increase bottom padding for the last item in section * Search: Move closing search to keybindingsSrv * Search: Fix folder view * Search: Do not move folders if already in folder * Docs: Adds deprecation notice to changelog and docs for scripted dashboards (#24060) * Update CHANGELOG.md (#24047) Fix typo Co-authored-by: Daniel Lee <dan.limerick@gmail.com> * Documentation: Alternative Team Sync Wording (#23960) * Alternative wording for team sync docs Signed-off-by: Joe Elliott <number101010@gmail.com> * Update docs/sources/auth/team-sync.md Co-Authored-By: Diana Payton <52059945+oddlittlebird@users.noreply.github.com> Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com> * Fix misspell issues (#23905) * Fix misspell issues See, $ golangci-lint run --timeout 10m --disable-all -E misspell ./... Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com> * Fix codespell issues See, $ codespell -S './.git*' -L 'uint,thru,pres,unknwon,serie,referer,uptodate,durationm' Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com> * ci please? * non-empty commit - ci? * Trigger build Co-authored-by: bergquist <carl.bergquist@gmail.com> Co-authored-by: Kyle Brandt <kyle@grafana.com> * fix compile error * better series display * better display * now with prometheus and loki * a few more tests * Improvements and tests * thinking * More advanced and smart default title generation * Another fix * Progress but dam this will be hard * Reverting the time series Value field name change * revert revert going in circles * add a field state object * Use state title when converting back to legacy format * Improved the join (series to columsn) transformer * Got tests running again * Rewrite of seriesToColums that simplifies and fixing tests * Fixed the tricky problem of multiple time field when not used in join * Prometheus: Restoring prometheus formatting * Graphite: Disable Grafana's series naming * fixed imports * Fixed tests and made rename transform change title instead * Fixing more tests * fix more tests * fixed import issue * Fixed more circular dependencies * Renamed to getFieldTitle * More rename * Review feedback * Fix for showing field title in calculate field transformer * fieldOverride: Make it clear that state title after applying defaults & overrides * Fixed ts issue * Update packages/grafana-ui/src/components/TransformersUI/OrganizeFieldsTransformerEditor.tsx Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com> Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com> Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com> Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Leonard Gram <leo@xlson.com> Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com> Co-authored-by: Torkel Ödegaard <torkel@grafana.com> Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com> Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com> Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> Co-authored-by: Richard Hartmann <RichiH@users.noreply.github.com> Co-authored-by: Daniel Lee <dan.limerick@gmail.com> Co-authored-by: Joe Elliott <joe.elliott@grafana.com> Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com> Co-authored-by: Mario Trangoni <mario@mariotrangoni.de> Co-authored-by: bergquist <carl.bergquist@gmail.com> Co-authored-by: Kyle Brandt <kyle@grafana.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
SelectableValue,
|
||||
toCSV,
|
||||
transformDataFrame,
|
||||
getFrameDisplayTitle,
|
||||
} from '@grafana/data';
|
||||
import { Button, Field, Icon, Select, Table } from '@grafana/ui';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
@@ -105,7 +106,7 @@ export class InspectDataTab extends PureComponent<Props, State> {
|
||||
const choices = dataFrames.map((frame, index) => {
|
||||
return {
|
||||
value: index,
|
||||
label: `${frame.name} (${index})`,
|
||||
label: `${getFrameDisplayTitle(frame)} (${index})`,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -169,9 +169,9 @@ export function getProcessedDataFrames(results?: DataQueryResponseData[]): DataF
|
||||
for (const result of results) {
|
||||
const dataFrame = guessFieldTypes(toDataFrame(result));
|
||||
|
||||
// clear out any cached calcs
|
||||
// clear out the cached info
|
||||
for (const field of dataFrame.fields) {
|
||||
field.calcs = null;
|
||||
field.state = null;
|
||||
}
|
||||
|
||||
dataFrames.push(dataFrame);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import '../datasource';
|
||||
import { CloudWatchDatasource } from '../datasource';
|
||||
import * as redux from 'app/store/store';
|
||||
import { DataSourceInstanceSettings, dateMath } from '@grafana/data';
|
||||
import { DataSourceInstanceSettings, dateMath, getFrameDisplayTitle } from '@grafana/data';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { CustomVariable } from 'app/features/templating/all';
|
||||
import { CloudWatchQuery, CloudWatchMetricsQuery } from '../types';
|
||||
@@ -233,7 +233,7 @@ describe('CloudWatchDatasource', () => {
|
||||
|
||||
it('should return series list', done => {
|
||||
ctx.ds.query(query).then((result: any) => {
|
||||
expect(result.data[0].name).toBe(response.results.A.series[0].name);
|
||||
expect(getFrameDisplayTitle(result.data[0])).toBe(response.results.A.series[0].name);
|
||||
expect(result.data[0].fields[1].values.buffer[0]).toBe(response.results.A.series[0].points[0][0]);
|
||||
done();
|
||||
});
|
||||
@@ -249,7 +249,7 @@ describe('CloudWatchDatasource', () => {
|
||||
it('should be built correctly if theres one search expressions returned in meta for a given query row', done => {
|
||||
response.results['A'].meta.gmdMeta = [{ Expression: `REMOVE_EMPTY(SEARCH('some expression'))`, Period: '300' }];
|
||||
ctx.ds.query(query).then((result: any) => {
|
||||
expect(result.data[0].name).toBe(response.results.A.series[0].name);
|
||||
expect(getFrameDisplayTitle(result.data[0])).toBe(response.results.A.series[0].name);
|
||||
expect(result.data[0].fields[1].config.links[0].title).toBe('View in CloudWatch console');
|
||||
expect(decodeURIComponent(result.data[0].fields[1].config.links[0].url)).toContain(
|
||||
`region=us-east-1#metricsV2:graph={"view":"timeSeries","stacked":false,"title":"A","start":"2016-12-31T15:00:00.000Z","end":"2016-12-31T16:00:00.000Z","region":"us-east-1","metrics":[{"expression":"REMOVE_EMPTY(SEARCH(\'some expression\'))"}]}`
|
||||
@@ -264,7 +264,7 @@ describe('CloudWatchDatasource', () => {
|
||||
{ Expression: `REMOVE_EMPTY(SEARCH('second expression'))` },
|
||||
];
|
||||
ctx.ds.query(query).then((result: any) => {
|
||||
expect(result.data[0].name).toBe(response.results.A.series[0].name);
|
||||
expect(getFrameDisplayTitle(result.data[0])).toBe(response.results.A.series[0].name);
|
||||
expect(result.data[0].fields[1].config.links[0].title).toBe('View in CloudWatch console');
|
||||
expect(decodeURIComponent(result.data[0].fields[0].config.links[0].url)).toContain(
|
||||
`region=us-east-1#metricsV2:graph={"view":"timeSeries","stacked":false,"title":"A","start":"2016-12-31T15:00:00.000Z","end":"2016-12-31T16:00:00.000Z","region":"us-east-1","metrics":[{"expression":"REMOVE_EMPTY(SEARCH(\'first expression\'))"},{"expression":"REMOVE_EMPTY(SEARCH(\'second expression\'))"}]}`
|
||||
@@ -276,7 +276,7 @@ describe('CloudWatchDatasource', () => {
|
||||
it('should be built correctly if the query is a metric stat query', done => {
|
||||
response.results['A'].meta.gmdMeta = [{ Period: '300' }];
|
||||
ctx.ds.query(query).then((result: any) => {
|
||||
expect(result.data[0].name).toBe(response.results.A.series[0].name);
|
||||
expect(getFrameDisplayTitle(result.data[0])).toBe(response.results.A.series[0].name);
|
||||
expect(result.data[0].fields[1].config.links[0].title).toBe('View in CloudWatch console');
|
||||
expect(decodeURIComponent(result.data[0].fields[0].config.links[0].url)).toContain(
|
||||
`region=us-east-1#metricsV2:graph={\"view\":\"timeSeries\",\"stacked\":false,\"title\":\"A\",\"start\":\"2016-12-31T15:00:00.000Z\",\"end\":\"2016-12-31T16:00:00.000Z\",\"region\":\"us-east-1\",\"metrics\":[[\"AWS/EC2\",\"CPUUtilization\",\"InstanceId\",\"i-12345678\",{\"stat\":\"Average\",\"period\":\"300\"}]]}`
|
||||
@@ -517,7 +517,7 @@ describe('CloudWatchDatasource', () => {
|
||||
|
||||
it('should return series list', done => {
|
||||
ctx.ds.query(query).then((result: any) => {
|
||||
expect(result.data[0].name).toBe(response.results.A.series[0].name);
|
||||
expect(getFrameDisplayTitle(result.data[0])).toBe(response.results.A.series[0].name);
|
||||
expect(result.data[0].fields[1].values.buffer[0]).toBe(response.results.A.series[0].points[0][0]);
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -929,9 +929,10 @@ describe('ElasticResponse', () => {
|
||||
const hist: KeyValue<number> = {};
|
||||
const histogramResults = new MutableDataFrame(result.data[1]);
|
||||
rows = new DataFrameView(histogramResults);
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows.get(i);
|
||||
hist[row.Time] = row.Count;
|
||||
hist[row.Time] = row.Value;
|
||||
}
|
||||
|
||||
response.responses[0].aggregations['2'].buckets.forEach((bucket: any) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Datasource from '../datasource';
|
||||
import { DataFrame, toUtc } from '@grafana/data';
|
||||
import { DataFrame, toUtc, getFrameDisplayTitle } from '@grafana/data';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
|
||||
|
||||
@@ -175,7 +175,7 @@ describe('AppInsightsDatasource', () => {
|
||||
return ctx.ds.query(options).then((results: any) => {
|
||||
expect(results.data.length).toBe(1);
|
||||
const data = results.data[0] as DataFrame;
|
||||
expect(data.name).toEqual('PrimaryResult');
|
||||
expect(getFrameDisplayTitle(data)).toEqual('PrimaryResult');
|
||||
expect(data.fields[0].values.length).toEqual(1);
|
||||
expect(data.fields[0].values.get(0)).toEqual(1558278660000);
|
||||
expect(data.fields[1].values.get(0)).toEqual(2.2075);
|
||||
@@ -218,7 +218,7 @@ describe('AppInsightsDatasource', () => {
|
||||
return ctx.ds.query(options).then((results: any) => {
|
||||
expect(results.data.length).toBe(1);
|
||||
const data = results.data[0] as DataFrame;
|
||||
expect(data.name).toEqual('paritionA');
|
||||
expect(getFrameDisplayTitle(data)).toEqual('paritionA');
|
||||
expect(data.fields[0].values.length).toEqual(1);
|
||||
expect(data.fields[0].values.get(0)).toEqual(1558278660000);
|
||||
expect(data.fields[1].values.get(0)).toEqual(2.2075);
|
||||
@@ -279,7 +279,7 @@ describe('AppInsightsDatasource', () => {
|
||||
return ctx.ds.query(options).then((results: any) => {
|
||||
expect(results.data.length).toBe(1);
|
||||
const data = results.data[0] as DataFrame;
|
||||
expect(data.name).toEqual('exceptions/server');
|
||||
expect(getFrameDisplayTitle(data)).toEqual('exceptions/server');
|
||||
expect(data.fields[0].values.get(0)).toEqual(1558278660000);
|
||||
expect(data.fields[1].values.get(0)).toEqual(2.2075);
|
||||
});
|
||||
@@ -322,7 +322,7 @@ describe('AppInsightsDatasource', () => {
|
||||
return ctx.ds.query(options).then((results: any) => {
|
||||
expect(results.data.length).toBe(1);
|
||||
const data = results.data[0] as DataFrame;
|
||||
expect(data.name).toEqual('exceptions/server');
|
||||
expect(getFrameDisplayTitle(data)).toEqual('exceptions/server');
|
||||
expect(data.fields[0].values.length).toEqual(2);
|
||||
expect(data.fields[0].values.get(0)).toEqual(1504108800000);
|
||||
expect(data.fields[1].values.get(0)).toEqual(3);
|
||||
@@ -376,14 +376,14 @@ describe('AppInsightsDatasource', () => {
|
||||
return ctx.ds.query(options).then((results: any) => {
|
||||
expect(results.data.length).toBe(2);
|
||||
let data = results.data[0] as DataFrame;
|
||||
expect(data.name).toEqual('exceptions/server{client/city="Miami"}');
|
||||
expect(getFrameDisplayTitle(data)).toEqual('exceptions/server{client/city="Miami"}');
|
||||
expect(data.fields[1].values.length).toEqual(2);
|
||||
expect(data.fields[0].values.get(0)).toEqual(1504108800000);
|
||||
expect(data.fields[1].values.get(0)).toEqual(10);
|
||||
expect(data.fields[0].values.get(1)).toEqual(1504112400000);
|
||||
expect(data.fields[1].values.get(1)).toEqual(20);
|
||||
data = results.data[1] as DataFrame;
|
||||
expect(data.name).toEqual('exceptions/server{client/city="San Antonio"}');
|
||||
expect(getFrameDisplayTitle(data)).toEqual('exceptions/server{client/city="San Antonio"}');
|
||||
expect(data.fields[1].values.length).toEqual(2);
|
||||
expect(data.fields[0].values.get(0)).toEqual(1504108800000);
|
||||
expect(data.fields[1].values.get(0)).toEqual(1);
|
||||
|
||||
@@ -2,7 +2,7 @@ import AzureMonitorDatasource from '../datasource';
|
||||
import FakeSchemaData from './__mocks__/schema';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { KustoSchema, AzureLogsVariable } from '../types';
|
||||
import { toUtc } from '@grafana/data';
|
||||
import { toUtc, getFrameDisplayTitle } from '@grafana/data';
|
||||
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
@@ -183,10 +183,11 @@ describe('AzureLogAnalyticsDatasource', () => {
|
||||
it('should return a list of datapoints', () => {
|
||||
return ctx.ds.query(options).then((results: any) => {
|
||||
expect(results.data.length).toBe(1);
|
||||
expect(results.data[0].name).toEqual('grafana-vm');
|
||||
expect(getFrameDisplayTitle(results.data[0])).toEqual('grafana-vm');
|
||||
expect(results.data[0].fields.length).toBe(2);
|
||||
expect(results.data[0].name).toBe('grafana-vm');
|
||||
expect(results.data[0].fields[0].name).toBe('Time');
|
||||
expect(results.data[0].fields[1].name).toBe('grafana-vm');
|
||||
expect(results.data[0].fields[1].name).toBe('Value');
|
||||
expect(results.data[0].fields[0].values.toArray().length).toBe(6);
|
||||
expect(results.data[0].fields[0].values.get(0)).toEqual(1587633300000);
|
||||
expect(results.data[0].fields[1].values.get(0)).toEqual(2017.25);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import AzureMonitorDatasource from '../datasource';
|
||||
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { toUtc, DataFrame } from '@grafana/data';
|
||||
import { toUtc, DataFrame, getFrameDisplayTitle } from '@grafana/data';
|
||||
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
@@ -137,7 +137,7 @@ describe('AzureMonitorDatasource', () => {
|
||||
return ctx.ds.query(options).then((results: any) => {
|
||||
expect(results.data.length).toBe(1);
|
||||
const data = results.data[0] as DataFrame;
|
||||
expect(data.name).toEqual('Percentage CPU');
|
||||
expect(getFrameDisplayTitle(data)).toEqual('Percentage CPU');
|
||||
expect(data.fields[0].values.get(0)).toEqual(1558278660000);
|
||||
expect(data.fields[1].values.get(0)).toEqual(2.2075);
|
||||
expect(data.fields[0].values.get(1)).toEqual(1558278720000);
|
||||
|
||||
@@ -123,6 +123,9 @@ export class GraphiteDatasource extends DataSourceApi<GraphiteQuery, GraphiteOpt
|
||||
for (let i = 0; i < series.length; i++) {
|
||||
const s = series[i];
|
||||
|
||||
// Disables Grafana own series naming
|
||||
s.title = s.target;
|
||||
|
||||
for (let y = 0; y < s.datapoints.length; y++) {
|
||||
s.datapoints[y][1] *= 1000;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { GraphiteDatasource } from '../datasource';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { dateTime } from '@grafana/data';
|
||||
import { dateTime, getFrameDisplayTitle } from '@grafana/data';
|
||||
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
@@ -91,8 +91,8 @@ describe('graphiteDatasource', () => {
|
||||
});
|
||||
|
||||
expect(result.data.length).toBe(2);
|
||||
expect(result.data[0].name).toBe('seriesA');
|
||||
expect(result.data[1].name).toBe('seriesB');
|
||||
expect(getFrameDisplayTitle(result.data[0])).toBe('seriesA');
|
||||
expect(getFrameDisplayTitle(result.data[1])).toBe('seriesB');
|
||||
expect(result.data[0].length).toBe(2);
|
||||
expect(result.data[0].meta.notices.length).toBe(1);
|
||||
expect(result.data[0].meta.notices[0].text).toBe('Data is rolled up, aggregated over 2h using Average function');
|
||||
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
DataSourceInstanceSettings,
|
||||
dateTime,
|
||||
LoadingState,
|
||||
toDataFrame,
|
||||
getFieldTitle,
|
||||
} from '@grafana/data';
|
||||
import { PromOptions, PromQuery } from './types';
|
||||
import templateSrv from 'app/features/templating/template_srv';
|
||||
@@ -586,8 +588,9 @@ describe('PrometheusDatasource', () => {
|
||||
});
|
||||
|
||||
it('should return series list', async () => {
|
||||
const frame = toDataFrame(results.data[0]);
|
||||
expect(results.data.length).toBe(1);
|
||||
expect(results.data[0].target).toBe('test{job="testjob"}');
|
||||
expect(getFieldTitle(frame.fields[1])).toBe('test{job="testjob"}');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -730,8 +733,10 @@ describe('PrometheusDatasource', () => {
|
||||
});
|
||||
|
||||
it('should return series list', () => {
|
||||
const frame = toDataFrame(results.data[0]);
|
||||
expect(results.data.length).toBe(1);
|
||||
expect(results.data[0].target).toBe('test{job="testjob"}');
|
||||
expect(frame.name).toBe('test{job="testjob"}');
|
||||
expect(getFieldTitle(frame.fields[1])).toBe('Value');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1634,8 +1639,9 @@ describe('PrometheusDatasource for POST', () => {
|
||||
});
|
||||
|
||||
it('should return series list', () => {
|
||||
const frame = toDataFrame(results.data[0]);
|
||||
expect(results.data.length).toBe(1);
|
||||
expect(results.data[0].target).toBe('test{job="testjob"}');
|
||||
expect(getFieldTitle(frame.fields[1])).toBe('test{job="testjob"}');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -162,6 +162,7 @@ describe('Prometheus Result Transformer', () => {
|
||||
expect(result).toEqual([
|
||||
{
|
||||
target: '1',
|
||||
title: '1',
|
||||
query: undefined,
|
||||
datapoints: [
|
||||
[10, 1445000010000],
|
||||
@@ -172,6 +173,7 @@ describe('Prometheus Result Transformer', () => {
|
||||
},
|
||||
{
|
||||
target: '2',
|
||||
title: '2',
|
||||
query: undefined,
|
||||
datapoints: [
|
||||
[10, 1445000010000],
|
||||
@@ -182,6 +184,7 @@ describe('Prometheus Result Transformer', () => {
|
||||
},
|
||||
{
|
||||
target: '3',
|
||||
title: '3',
|
||||
query: undefined,
|
||||
datapoints: [
|
||||
[10, 1445000010000],
|
||||
@@ -285,6 +288,7 @@ describe('Prometheus Result Transformer', () => {
|
||||
expect(result).toEqual([
|
||||
{
|
||||
target: 'test{job="testjob"}',
|
||||
title: 'test{job="testjob"}',
|
||||
query: undefined,
|
||||
datapoints: [
|
||||
[10, 0],
|
||||
@@ -324,6 +328,7 @@ describe('Prometheus Result Transformer', () => {
|
||||
expect(result).toEqual([
|
||||
{
|
||||
target: 'test{job="testjob"}',
|
||||
title: 'test{job="testjob"}',
|
||||
query: undefined,
|
||||
datapoints: [
|
||||
[null, 0],
|
||||
@@ -335,6 +340,64 @@ describe('Prometheus Result Transformer', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should use __name__ label as series name', () => {
|
||||
const response = {
|
||||
status: 'success',
|
||||
data: {
|
||||
resultType: 'matrix',
|
||||
result: [
|
||||
{
|
||||
metric: { __name__: 'test', job: 'testjob' },
|
||||
values: [
|
||||
[1, '10'],
|
||||
[2, '0'],
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const options = {
|
||||
format: 'timeseries',
|
||||
step: 1,
|
||||
start: 0,
|
||||
end: 2,
|
||||
};
|
||||
|
||||
const result = ctx.resultTransformer.transform({ data: response }, options);
|
||||
expect(result[0].target).toEqual('test{job="testjob"}');
|
||||
});
|
||||
|
||||
it('should set frame name to undefined if no __name__ label but there are other labels', () => {
|
||||
const response = {
|
||||
status: 'success',
|
||||
data: {
|
||||
resultType: 'matrix',
|
||||
result: [
|
||||
{
|
||||
metric: { job: 'testjob' },
|
||||
values: [
|
||||
[1, '10'],
|
||||
[2, '0'],
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const options = {
|
||||
format: 'timeseries',
|
||||
step: 1,
|
||||
query: 'Some query',
|
||||
start: 0,
|
||||
end: 2,
|
||||
};
|
||||
|
||||
const result = ctx.resultTransformer.transform({ data: response }, options);
|
||||
expect(result[0].target).toBe('{job="testjob"}');
|
||||
expect(result[0].tags.job).toEqual('testjob');
|
||||
});
|
||||
|
||||
it('should align null values with step', () => {
|
||||
const response = {
|
||||
status: 'success',
|
||||
@@ -356,13 +419,20 @@ describe('Prometheus Result Transformer', () => {
|
||||
step: 2,
|
||||
start: 0,
|
||||
end: 8,
|
||||
refId: 'A',
|
||||
meta: { custom: { hello: '1' } },
|
||||
};
|
||||
|
||||
const result = ctx.resultTransformer.transform({ data: response }, options);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
target: 'test{job="testjob"}',
|
||||
title: 'test{job="testjob"}',
|
||||
meta: {
|
||||
custom: { hello: '1' },
|
||||
},
|
||||
query: undefined,
|
||||
refId: 'A',
|
||||
datapoints: [
|
||||
[null, 0],
|
||||
[null, 2000],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import _ from 'lodash';
|
||||
import TableModel from 'app/core/table_model';
|
||||
import { TimeSeries, FieldType } from '@grafana/data';
|
||||
import { TimeSeries, FieldType, Labels, formatLabels } from '@grafana/data';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
export class ResultTransformer {
|
||||
@@ -42,9 +42,7 @@ export class ResultTransformer {
|
||||
|
||||
transformMetricData(metricData: any, options: any, start: number, end: number) {
|
||||
const dps = [];
|
||||
let metricLabel = null;
|
||||
|
||||
metricLabel = this.createMetricLabel(metricData.metric, options);
|
||||
const { name, labels, title } = this.createLabelInfo(metricData.metric, options);
|
||||
|
||||
const stepMs = parseFloat(options.step) * 1000;
|
||||
let baseTimestamp = start * 1000;
|
||||
@@ -76,9 +74,10 @@ export class ResultTransformer {
|
||||
datapoints: dps,
|
||||
query: options.query,
|
||||
refId: options.refId,
|
||||
target: name,
|
||||
tags: labels,
|
||||
title,
|
||||
meta: options.meta,
|
||||
target: metricLabel,
|
||||
tags: metricData.metric,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -142,23 +141,39 @@ export class ResultTransformer {
|
||||
|
||||
transformInstantMetricData(md: any, options: any) {
|
||||
const dps = [];
|
||||
let metricLabel = null;
|
||||
metricLabel = this.createMetricLabel(md.metric, options);
|
||||
const { name, labels } = this.createLabelInfo(md.metric, options);
|
||||
dps.push([parseFloat(md.value[1]), md.value[0] * 1000]);
|
||||
return { target: metricLabel, datapoints: dps, tags: md.metric, refId: options.refId, meta: options.meta };
|
||||
return { target: name, datapoints: dps, tags: labels, refId: options.refId, meta: options.meta };
|
||||
}
|
||||
|
||||
createMetricLabel(labelData: { [key: string]: string }, options: any) {
|
||||
let label = '';
|
||||
if (_.isUndefined(options) || _.isEmpty(options.legendFormat)) {
|
||||
label = this.getOriginalMetricName(labelData);
|
||||
} else {
|
||||
label = this.renderTemplate(this.templateSrv.replace(options.legendFormat), labelData);
|
||||
createLabelInfo(labels: { [key: string]: string }, options: any): { name?: string; labels: Labels; title?: string } {
|
||||
if (options?.legendFormat) {
|
||||
const title = this.renderTemplate(this.templateSrv.replace(options.legendFormat), labels);
|
||||
return { name: title, title, labels };
|
||||
}
|
||||
if (!label || label === '{}') {
|
||||
label = options.query;
|
||||
|
||||
let { __name__, ...labelsWithoutName } = labels;
|
||||
|
||||
let title = __name__ || '';
|
||||
|
||||
const labelPart = formatLabels(labelsWithoutName);
|
||||
|
||||
if (!title && !labelPart) {
|
||||
title = options.query;
|
||||
}
|
||||
return label;
|
||||
|
||||
title = `${__name__ ?? ''}${labelPart}`;
|
||||
|
||||
return { name: title, title, labels: labelsWithoutName };
|
||||
}
|
||||
|
||||
getOriginalMetricName(labelData: { [key: string]: string }) {
|
||||
const metricName = labelData.__name__ || '';
|
||||
delete labelData.__name__;
|
||||
const labelPart = Object.entries(labelData)
|
||||
.map(label => `${label[0]}="${label[1]}"`)
|
||||
.join(',');
|
||||
return `${metricName}{${labelPart}}`;
|
||||
}
|
||||
|
||||
renderTemplate(aliasPattern: string, aliasData: { [key: string]: string }) {
|
||||
@@ -171,15 +186,6 @@ export class ResultTransformer {
|
||||
});
|
||||
}
|
||||
|
||||
getOriginalMetricName(labelData: { [key: string]: string }) {
|
||||
const metricName = labelData.__name__ || '';
|
||||
delete labelData.__name__;
|
||||
const labelPart = _.map(_.toPairs(labelData), label => {
|
||||
return label[0] + '="' + label[1] + '"';
|
||||
}).join(',');
|
||||
return metricName + '{' + labelPart + '}';
|
||||
}
|
||||
|
||||
transformToHistogramOverTime(seriesList: TimeSeries[]) {
|
||||
/* t1 = timestamp1, t2 = timestamp2 etc.
|
||||
t1 t2 t3 t1 t2 t3
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
DataFrame,
|
||||
getTimeField,
|
||||
dateTime,
|
||||
getFieldTitle,
|
||||
} from '@grafana/data';
|
||||
import TimeSeries from 'app/core/time_series2';
|
||||
import config from 'app/core/config';
|
||||
@@ -31,27 +32,24 @@ export class DataProcessor {
|
||||
for (let i = 0; i < dataList.length; i++) {
|
||||
const series = dataList[i];
|
||||
const { timeField } = getTimeField(series);
|
||||
|
||||
if (!timeField) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const seriesName = series.name ? series.name : series.refId;
|
||||
for (let j = 0; j < series.fields.length; j++) {
|
||||
const field = series.fields[j];
|
||||
|
||||
if (field.type !== FieldType.number) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = field.config && field.config.title ? field.config.title : field.name;
|
||||
|
||||
if (seriesName && dataList.length > 0 && name !== seriesName) {
|
||||
name = seriesName + ' ' + name;
|
||||
}
|
||||
|
||||
const name = getFieldTitle(field, series, dataList);
|
||||
const datapoints = [];
|
||||
|
||||
for (let r = 0; r < series.length; r++) {
|
||||
datapoints.push([field.values.get(r), dateTime(timeField.values.get(r)).valueOf()]);
|
||||
}
|
||||
|
||||
list.push(this.toTimeSeries(field, name, i, j, datapoints, list.length, range));
|
||||
}
|
||||
}
|
||||
@@ -60,9 +58,11 @@ export class DataProcessor {
|
||||
if (this.panel.xaxis.mode === 'histogram' && !this.panel.stack && list.length > 1) {
|
||||
const first = list[0];
|
||||
first.alias = first.aliasEscaped = 'Count';
|
||||
|
||||
for (let i = 1; i < list.length; i++) {
|
||||
first.datapoints = first.datapoints.concat(list[i].datapoints);
|
||||
}
|
||||
|
||||
return [first];
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
FieldColor,
|
||||
FieldColorMode,
|
||||
FieldConfigSource,
|
||||
getFieldTitle,
|
||||
} from '@grafana/data';
|
||||
|
||||
import { SeriesOptions, GraphOptions, GraphLegendEditorLegendOptions } from './types';
|
||||
@@ -122,7 +123,7 @@ export const getGraphSeriesModel = (
|
||||
});
|
||||
|
||||
graphs.push({
|
||||
label: field.name,
|
||||
label: getFieldTitle(field, series, dataFrames),
|
||||
data: points,
|
||||
color: field.config.color?.fixedColor,
|
||||
info: statsDisplayValues,
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
PanelEvents,
|
||||
formattedValueToString,
|
||||
locationUtil,
|
||||
getFieldTitle,
|
||||
} from '@grafana/data';
|
||||
|
||||
import { convertOldAngularValueMapping } from '@grafana/ui';
|
||||
@@ -156,6 +157,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
|
||||
onFramesReceived(frames: DataFrame[]) {
|
||||
const { panel } = this;
|
||||
this.dataList = frames;
|
||||
|
||||
if (frames && frames.length > 1) {
|
||||
this.data = {
|
||||
@@ -204,7 +206,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
processField(fieldInfo: FieldInfo) {
|
||||
const { panel, dashboard } = this;
|
||||
|
||||
const name = fieldInfo.field.config.title || fieldInfo.field.name;
|
||||
const name = getFieldTitle(fieldInfo.field, fieldInfo.frame.frame, this.dataList as DataFrame[]);
|
||||
let calc = panel.valueName;
|
||||
let calcField = fieldInfo.field;
|
||||
let val: any = undefined;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SingleStatCtrl, ShowData } from '../module';
|
||||
import { dateTime, ReducerID } from '@grafana/data';
|
||||
import { dateTime, ReducerID, getFieldTitle } from '@grafana/data';
|
||||
import { LinkSrv } from 'app/features/panel/panellinks/link_srv';
|
||||
import { LegacyResponseData } from '@grafana/data';
|
||||
import { DashboardModel } from 'app/features/dashboard/state';
|
||||
@@ -90,7 +90,8 @@ describe('SingleStatCtrl', () => {
|
||||
});
|
||||
|
||||
it('Should use series avg as default main value', () => {
|
||||
expect(ctx.data.value).toBe('test.cpu1');
|
||||
const title = getFieldTitle(ctx.data.field);
|
||||
expect(title).toBe('test.cpu1');
|
||||
});
|
||||
|
||||
it('should set formatted value', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { Table, Select } from '@grafana/ui';
|
||||
import { FieldMatcherID, PanelProps, DataFrame, SelectableValue } from '@grafana/data';
|
||||
import { FieldMatcherID, PanelProps, DataFrame, SelectableValue, getFrameDisplayTitle } from '@grafana/data';
|
||||
import { Options } from './types';
|
||||
import { css } from 'emotion';
|
||||
import { config } from 'app/core/config';
|
||||
@@ -101,7 +101,7 @@ export class TablePanel extends Component<Props> {
|
||||
const currentIndex = this.getCurrentFrameIndex();
|
||||
const names = data.series.map((frame, index) => {
|
||||
return {
|
||||
label: `${frame.name ?? 'Series'}`,
|
||||
label: getFrameDisplayTitle(frame),
|
||||
value: index,
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user