mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
QueryEditorRow: Render frame warnings in QueryEditorRow (#50116)
This commit is contained in:
parent
14edc6f1db
commit
3ab410de0b
@ -1,6 +1,8 @@
|
|||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
|
||||||
import { DataQueryRequest, dateTime, LoadingState, PanelData, toDataFrame } from '@grafana/data';
|
import { DataQueryRequest, dateTime, LoadingState, PanelData, toDataFrame } from '@grafana/data';
|
||||||
|
|
||||||
import { filterPanelDataToQuery } from './QueryEditorRow';
|
import { filterPanelDataToQuery, QueryEditorRow } from './QueryEditorRow';
|
||||||
|
|
||||||
function makePretendRequest(requestId: string, subRequests?: DataQueryRequest[]): DataQueryRequest {
|
function makePretendRequest(requestId: string, subRequests?: DataQueryRequest[]): DataQueryRequest {
|
||||||
return {
|
return {
|
||||||
@ -108,3 +110,77 @@ describe('filterPanelDataToQuery', () => {
|
|||||||
expect(panelDataA?.state).toBe(LoadingState.Loading);
|
expect(panelDataA?.state).toBe(LoadingState.Loading);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('frame results with warnings', () => {
|
||||||
|
const meta = {
|
||||||
|
notices: [
|
||||||
|
{
|
||||||
|
severity: 'warning',
|
||||||
|
text: 'Reduce operation is not needed. Input query or expression A is already reduced data.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataWithWarnings: PanelData = {
|
||||||
|
state: LoadingState.Done,
|
||||||
|
series: [
|
||||||
|
toDataFrame({
|
||||||
|
refId: 'B',
|
||||||
|
fields: [{ name: 'B1' }],
|
||||||
|
meta,
|
||||||
|
}),
|
||||||
|
toDataFrame({
|
||||||
|
refId: 'B',
|
||||||
|
fields: [{ name: 'B2' }],
|
||||||
|
meta,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
timeRange: { from: dateTime(), to: dateTime(), raw: { from: 'now-1d', to: 'now' } },
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataWithoutWarnings: PanelData = {
|
||||||
|
state: LoadingState.Done,
|
||||||
|
series: [
|
||||||
|
toDataFrame({
|
||||||
|
refId: 'B',
|
||||||
|
fields: [{ name: 'B1' }],
|
||||||
|
meta: {},
|
||||||
|
}),
|
||||||
|
toDataFrame({
|
||||||
|
refId: 'B',
|
||||||
|
fields: [{ name: 'B2' }],
|
||||||
|
meta: {},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
timeRange: { from: dateTime(), to: dateTime(), raw: { from: 'now-1d', to: 'now' } },
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should show a warning badge and de-duplicate warning messages', () => {
|
||||||
|
// @ts-ignore: there are _way_ too many props to inject here :(
|
||||||
|
const editorRow = new QueryEditorRow({
|
||||||
|
data: dataWithWarnings,
|
||||||
|
query: {
|
||||||
|
refId: 'B',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const warningsComponent = editorRow.renderWarnings();
|
||||||
|
expect(warningsComponent).not.toBe(null);
|
||||||
|
|
||||||
|
render(warningsComponent!);
|
||||||
|
expect(screen.getByText('1 warning')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not show a warning badge when there are no warnings', () => {
|
||||||
|
// @ts-ignore: there are _way_ too many props to inject here :(
|
||||||
|
const editorRow = new QueryEditorRow({
|
||||||
|
data: dataWithoutWarnings,
|
||||||
|
query: {
|
||||||
|
refId: 'B',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const warningsComponent = editorRow.renderWarnings();
|
||||||
|
expect(warningsComponent).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Libraries
|
// Libraries
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { cloneDeep, has } from 'lodash';
|
import { cloneDeep, filter, has, uniqBy } from 'lodash';
|
||||||
|
import pluralize from 'pluralize';
|
||||||
import React, { PureComponent, ReactNode } from 'react';
|
import React, { PureComponent, ReactNode } from 'react';
|
||||||
|
|
||||||
// Utils & Services
|
// Utils & Services
|
||||||
@ -15,12 +16,13 @@ import {
|
|||||||
LoadingState,
|
LoadingState,
|
||||||
PanelData,
|
PanelData,
|
||||||
PanelEvents,
|
PanelEvents,
|
||||||
|
QueryResultMetaNotice,
|
||||||
TimeRange,
|
TimeRange,
|
||||||
toLegacyResponseData,
|
toLegacyResponseData,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { AngularComponent, getAngularLoader } from '@grafana/runtime';
|
import { AngularComponent, getAngularLoader } from '@grafana/runtime';
|
||||||
import { ErrorBoundaryAlert, HorizontalGroup } from '@grafana/ui';
|
import { Badge, ErrorBoundaryAlert, HorizontalGroup } from '@grafana/ui';
|
||||||
import { OperationRowHelp } from 'app/core/components/QueryOperationRow/OperationRowHelp';
|
import { OperationRowHelp } from 'app/core/components/QueryOperationRow/OperationRowHelp';
|
||||||
import { QueryOperationAction } from 'app/core/components/QueryOperationRow/QueryOperationAction';
|
import { QueryOperationAction } from 'app/core/components/QueryOperationRow/QueryOperationAction';
|
||||||
import {
|
import {
|
||||||
@ -307,9 +309,46 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderWarnings = (): JSX.Element | null => {
|
||||||
|
const { data, query } = this.props;
|
||||||
|
const dataFilteredByRefId = filterPanelDataToQuery(data, query.refId)?.series ?? [];
|
||||||
|
|
||||||
|
const allWarnings = dataFilteredByRefId.reduce((acc: QueryResultMetaNotice[], serie) => {
|
||||||
|
if (!serie.meta?.notices) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const warnings = filter(serie.meta.notices, { severity: 'warning' }) ?? [];
|
||||||
|
return acc.concat(warnings);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const uniqueWarnings = uniqBy(allWarnings, 'text');
|
||||||
|
|
||||||
|
const hasWarnings = uniqueWarnings.length > 0;
|
||||||
|
if (!hasWarnings) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const serializedWarnings = uniqueWarnings.map((warning) => warning.text).join('\n');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Badge
|
||||||
|
color="orange"
|
||||||
|
icon="exclamation-triangle"
|
||||||
|
text={
|
||||||
|
<>
|
||||||
|
{uniqueWarnings.length} {pluralize('warning', uniqueWarnings.length)}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
tooltip={serializedWarnings}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
renderExtraActions = () => {
|
renderExtraActions = () => {
|
||||||
const { query, queries, data, onAddQuery, dataSource } = this.props;
|
const { query, queries, data, onAddQuery, dataSource } = this.props;
|
||||||
return RowActionComponents.getAllExtraRenderAction()
|
|
||||||
|
const extraActions = RowActionComponents.getAllExtraRenderAction()
|
||||||
.map((action, index) =>
|
.map((action, index) =>
|
||||||
action({
|
action({
|
||||||
query,
|
query,
|
||||||
@ -321,6 +360,10 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
|
extraActions.push(this.renderWarnings());
|
||||||
|
|
||||||
|
return extraActions;
|
||||||
};
|
};
|
||||||
|
|
||||||
renderActions = (props: QueryOperationRowRenderProps) => {
|
renderActions = (props: QueryOperationRowRenderProps) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user