QueryEditorRow: Render frame warnings in QueryEditorRow (#50116)

This commit is contained in:
Gilles De Mey 2022-06-28 13:38:31 +02:00 committed by GitHub
parent 14edc6f1db
commit 3ab410de0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 123 additions and 4 deletions

View File

@ -1,6 +1,8 @@
import { render, screen } from '@testing-library/react';
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 {
return {
@ -108,3 +110,77 @@ describe('filterPanelDataToQuery', () => {
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);
});
});

View File

@ -1,6 +1,7 @@
// Libraries
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';
// Utils & Services
@ -15,12 +16,13 @@ import {
LoadingState,
PanelData,
PanelEvents,
QueryResultMetaNotice,
TimeRange,
toLegacyResponseData,
} from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
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 { QueryOperationAction } from 'app/core/components/QueryOperationRow/QueryOperationAction';
import {
@ -307,9 +309,46 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
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 = () => {
const { query, queries, data, onAddQuery, dataSource } = this.props;
return RowActionComponents.getAllExtraRenderAction()
const extraActions = RowActionComponents.getAllExtraRenderAction()
.map((action, index) =>
action({
query,
@ -321,6 +360,10 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
})
)
.filter(Boolean);
extraActions.push(this.renderWarnings());
return extraActions;
};
renderActions = (props: QueryOperationRowRenderProps) => {