CloudMonitoring: Fixes broken variable queries that use group bys (#43914)

This commit is contained in:
Erik Sundell 2022-01-17 15:51:24 +01:00 committed by GitHub
parent 9fb8339f87
commit 715166baf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 10 deletions

View File

@ -100,7 +100,10 @@ export default class CloudMonitoringMetricFindQuery {
return []; return [];
} }
const refId = 'handleLabelValuesQuery'; const refId = 'handleLabelValuesQuery';
const labels = await this.datasource.getLabels(selectedMetricType, refId, projectName, [labelKey]); const labels = await this.datasource.getLabels(selectedMetricType, refId, projectName, {
groupBys: [labelKey],
crossSeriesReducer: 'REDUCE_MEAN',
});
const interpolatedKey = this.datasource.templateSrv.replace(labelKey); const interpolatedKey = this.datasource.templateSrv.replace(labelKey);
const values = labels.hasOwnProperty(interpolatedKey) ? labels[interpolatedKey] : []; const values = labels.hasOwnProperty(interpolatedKey) ? labels[interpolatedKey] : [];
return values.map(this.toFindQueryResult); return values.map(this.toFindQueryResult);

View File

@ -61,15 +61,15 @@ function Editor({
variableOptionGroup, variableOptionGroup,
}: React.PropsWithChildren<Props>) { }: React.PropsWithChildren<Props>) {
const [state, setState] = useState<State>(defaultState); const [state, setState] = useState<State>(defaultState);
const { projectName, metricType, groupBys, editorMode } = query; const { projectName, metricType, groupBys, editorMode, crossSeriesReducer } = query;
useEffect(() => { useEffect(() => {
if (projectName && metricType) { if (projectName && metricType) {
datasource datasource
.getLabels(metricType, refId, projectName, groupBys) .getLabels(metricType, refId, projectName, { groupBys, crossSeriesReducer })
.then((labels) => setState((prevState) => ({ ...prevState, labels }))); .then((labels) => setState((prevState) => ({ ...prevState, labels })));
} }
}, [datasource, groupBys, metricType, projectName, refId]); }, [datasource, groupBys, metricType, projectName, refId, crossSeriesReducer]);
const onChange = useCallback( const onChange = useCallback(
(metricQuery: MetricQuery | SLOQuery) => { (metricQuery: MetricQuery | SLOQuery) => {

View File

@ -20,6 +20,7 @@ import {
MetricDescriptor, MetricDescriptor,
QueryType, QueryType,
PostResponse, PostResponse,
Aggregation,
} from './types'; } from './types';
import { CloudMonitoringVariableSupport } from './variables'; import { CloudMonitoringVariableSupport } from './variables';
@ -136,7 +137,7 @@ export default class CloudMonitoringDatasource extends DataSourceWithBackend<
}; };
} }
async getLabels(metricType: string, refId: string, projectName: string, groupBys?: string[]) { async getLabels(metricType: string, refId: string, projectName: string, aggregation?: Aggregation) {
const options = { const options = {
targets: [ targets: [
{ {
@ -146,8 +147,8 @@ export default class CloudMonitoringDatasource extends DataSourceWithBackend<
metricQuery: { metricQuery: {
projectName: this.templateSrv.replace(projectName), projectName: this.templateSrv.replace(projectName),
metricType: this.templateSrv.replace(metricType), metricType: this.templateSrv.replace(metricType),
groupBys: this.interpolateGroupBys(groupBys || [], {}), groupBys: this.interpolateGroupBys(aggregation?.groupBys || [], {}),
crossSeriesReducer: 'REDUCE_NONE', crossSeriesReducer: aggregation?.crossSeriesReducer ?? 'REDUCE_NONE',
view: 'HEADERS', view: 'HEADERS',
}, },
}, },

View File

@ -17,6 +17,8 @@ jest.mock('@grafana/runtime', () => ({
type Args = { response?: any; throws?: boolean; templateSrv?: TemplateSrv }; type Args = { response?: any; throws?: boolean; templateSrv?: TemplateSrv };
const fetchMock = jest.spyOn(backendSrv, 'fetch');
function getTestcontext({ response = {}, throws = false, templateSrv = new TemplateSrv() }: Args = {}) { function getTestcontext({ response = {}, throws = false, templateSrv = new TemplateSrv() }: Args = {}) {
jest.clearAllMocks(); jest.clearAllMocks();
@ -26,9 +28,12 @@ function getTestcontext({ response = {}, throws = false, templateSrv = new Templ
}, },
} as unknown) as DataSourceInstanceSettings<CloudMonitoringOptions>; } as unknown) as DataSourceInstanceSettings<CloudMonitoringOptions>;
const timeSrv = {} as TimeSrv; const timeSrv = {
timeRange: () => ({
const fetchMock = jest.spyOn(backendSrv, 'fetch'); from: toUtc('2017-08-22T20:00:00Z'),
to: toUtc('2017-08-22T23:59:00Z'),
}),
} as TimeSrv;
throws throws
? fetchMock.mockImplementation(() => throwError(response)) ? fetchMock.mockImplementation(() => throwError(response))
@ -82,6 +87,41 @@ describe('CloudMonitoringDataSource', () => {
}); });
}); });
describe('When loading labels', () => {
describe('and no aggregation was specified', () => {
it('should use default values', async () => {
const { ds } = getTestcontext();
await ds.getLabels('cpu', 'a', 'default-proj');
await expect(fetchMock.mock.calls[0][0].data.queries[0].metricQuery).toMatchObject({
crossSeriesReducer: 'REDUCE_NONE',
groupBys: [],
metricType: 'cpu',
projectName: 'default-proj',
view: 'HEADERS',
});
});
});
describe('and an aggregation was specified', () => {
it('should use the provided aggregation', async () => {
const { ds } = getTestcontext();
await ds.getLabels('sql', 'b', 'default-proj', {
crossSeriesReducer: 'REDUCE_MEAN',
groupBys: ['metadata.system_label.name'],
});
await expect(fetchMock.mock.calls[0][0].data.queries[0].metricQuery).toMatchObject({
crossSeriesReducer: 'REDUCE_MEAN',
groupBys: ['metadata.system_label.name'],
metricType: 'sql',
projectName: 'default-proj',
view: 'HEADERS',
});
});
});
});
describe('when interpolating a template variable for the filter', () => { describe('when interpolating a template variable for the filter', () => {
describe('and is single value variable', () => { describe('and is single value variable', () => {
it('should replace the variable with the value', () => { it('should replace the variable with the value', () => {

View File

@ -53,6 +53,11 @@ export interface VariableQueryData {
loading: boolean; loading: boolean;
} }
export interface Aggregation {
crossSeriesReducer?: string;
groupBys?: string[];
}
export enum QueryType { export enum QueryType {
METRICS = 'metrics', METRICS = 'metrics',
SLO = 'slo', SLO = 'slo',