AzureMonitor: Fix auto-selection of time-grain for metrics. (#49278)

* Update query editor to fix auto time-grain selection

* Update new query editor to fix auto time-grain selection

* Remove log and fix lint issues

* Add test for useMetricMetadata

- Add necessary types

* More test updates

- Update old dataHooks test
- Ensure query changes

Co-authored-by: Kevin Yu <kevinwcyu@users.noreply.github.com>
Co-authored-by: Andres Martinez Gotor <andres.mgotor@gmail.com>
Co-authored-by: Erik Sundell <erik.sundell87@gmail.com>
This commit is contained in:
Andreas Christou 2022-05-20 15:45:54 +01:00 committed by GitHub
parent bd320ee0b3
commit 2780651ea8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 175 additions and 11 deletions

View File

@ -106,6 +106,7 @@ func (e *AzureMonitorDatasource) buildQueries(queries []backend.DataQuery, dsInf
timeGrain := azJSONModel.TimeGrain
timeGrains := azJSONModel.AllowedTimeGrainsMs
if timeGrain == "auto" {
timeGrain, err = azTime.SetAutoTimeGrain(query.Interval.Milliseconds(), timeGrains)
if err != nil {

View File

@ -6,7 +6,10 @@ import { AzureMetricQuery, AzureMonitorOption, AzureMonitorQuery, AzureQueryType
import {
DataHook,
MetricMetadata,
MetricsMetadataHook,
updateSubscriptions,
useMetricMetadata,
useMetricNames,
useMetricNamespaces,
useResourceGroups,
@ -23,7 +26,7 @@ const opt = (text: string, value: string) => ({ text, value });
interface TestScenario {
name: string;
hook: DataHook;
hook: DataHook | MetricsMetadataHook;
// For convenience, only need to define the azureMonitor part of the query for some tests
emptyQueryPartial: AzureMetricQuery;
@ -31,7 +34,7 @@ interface TestScenario {
topLevelCustomProperties?: Partial<AzureMonitorQuery>;
expectedCustomPropertyResults?: Array<AzureMonitorOption<string>>;
expectedOptions: AzureMonitorOption[];
expectedOptions: AzureMonitorOption[] | MetricMetadata;
}
describe('AzureMonitor: metrics dataHooks', () => {
@ -255,7 +258,25 @@ describe('AzureMonitor: metrics dataHooks', () => {
datasource.azureMonitorDatasource.getMetricNamespaces = jest
.fn()
.mockResolvedValue([opt('Compute Virtual Machine', 'azure/vmc'), opt('Database NS', 'azure/dbns')]);
datasource.azureMonitorDatasource.getMetricMetadata = jest.fn().mockResolvedValue({
primaryAggType: 'Average',
supportedAggTypes: ['Average'],
supportedTimeGrains: [
{ label: 'Auto', value: 'auto' },
{ label: '1 minute', value: 'PT1M' },
{ label: '5 minutes', value: 'PT5M' },
{ label: '15 minutes', value: 'PT15M' },
{ label: '30 minutes', value: 'PT30M' },
{ label: '1 hour', value: 'PT1H' },
{ label: '6 hours', value: 'PT6H' },
{ label: '12 hours', value: 'PT12H' },
{ label: '1 day', value: 'P1D' },
],
dimensions: [],
});
});
describe.each(testTable)('scenario %#: $name', (scenario) => {
it('returns values', async () => {
const query = {
@ -280,6 +301,60 @@ describe('AzureMonitor: metrics dataHooks', () => {
expect(result.current).toEqual(scenario.expectedCustomPropertyResults);
});
});
describe('useMetricsMetadataHook', () => {
const metricsMetadataConfig = {
name: 'useMetricMetadata',
hook: useMetricMetadata,
emptyQueryPartial: {
resourceGroup: 'web-app-development',
metricDefinition: 'azure/vm',
resourceName: 'web-server',
metricNamespace: 'azure/vm',
subscription: 'test-sub',
metricName: 'Average CPU',
},
customProperties: {},
expectedOptions: {
aggOptions: [{ label: 'Average', value: 'Average' }],
timeGrains: [
{ label: 'Auto', value: 'auto' },
{ label: '1 minute', value: 'PT1M' },
{ label: '5 minutes', value: 'PT5M' },
{ label: '15 minutes', value: 'PT15M' },
{ label: '30 minutes', value: 'PT30M' },
{ label: '1 hour', value: 'PT1H' },
{ label: '6 hours', value: 'PT6H' },
{ label: '12 hours', value: 'PT12H' },
{ label: '1 day', value: 'P1D' },
],
dimensions: [],
isLoading: false,
supportedAggTypes: ['Average'],
primaryAggType: 'Average',
},
};
it('returns values', async () => {
const query = {
...bareQuery,
azureMonitor: metricsMetadataConfig.emptyQueryPartial,
};
const { result, waitForNextUpdate } = renderHook(() => metricsMetadataConfig.hook(query, datasource, onChange));
await waitForNextUpdate(WAIT_OPTIONS);
expect(result.current).toEqual(metricsMetadataConfig.expectedOptions);
expect(onChange).toHaveBeenCalledWith({
...query,
azureMonitor: {
...query.azureMonitor,
aggregation: result.current.primaryAggType,
timeGrain: 'auto',
allowedTimeGrainsMs: [60_000, 300_000, 900_000, 1_800_000, 3_600_000, 21_600_000, 43_200_000, 86_400_000],
},
});
});
});
});
describe('AzureMonitor: updateSubscriptions', () => {

View File

@ -1,6 +1,9 @@
import { useEffect, useState } from 'react';
import { rangeUtil } from '@grafana/data';
import Datasource from '../../datasource';
import TimegrainConverter from '../../time_grain_converter';
import { AzureMonitorErrorish, AzureMonitorOption, AzureMonitorQuery } from '../../types';
import { hasOption, toOption } from '../../utils/common';
import { useAsyncState } from '../../utils/useAsyncState';
@ -28,6 +31,12 @@ export type DataHook = (
setError: SetErrorFn
) => AzureMonitorOption[];
export type MetricsMetadataHook = (
query: AzureMonitorQuery,
datasource: Datasource,
onChange: OnChangeFn
) => MetricMetadata;
export const updateSubscriptions = (
query: AzureMonitorQuery,
subscriptionOptions: AzureMonitorOption[],
@ -231,7 +240,6 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour
label: v,
value: v,
}));
setMetricMetadata({
aggOptions: aggregations,
timeGrains: metadata.supportedTimeGrains,
@ -255,6 +263,11 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour
...query.azureMonitor,
aggregation: newAggregation,
timeGrain: newTimeGrain,
allowedTimeGrainsMs: metricMetadata.timeGrains
.filter((timeGrain) => timeGrain.value !== 'auto')
.map((timeGrain) =>
rangeUtil.intervalToMs(TimegrainConverter.createKbnUnitFromISO8601Duration(timeGrain.value))
),
},
});
}

View File

@ -3,9 +3,9 @@ import { renderHook } from '@testing-library/react-hooks';
import createMockDatasource from '../../__mocks__/datasource';
import Datasource from '../../datasource';
import { AzureMetricQuery, AzureMonitorOption, AzureMonitorQuery, AzureQueryType } from '../../types';
import { DataHook } from '../MetricsQueryEditor/dataHooks';
import { DataHook, MetricMetadata, MetricsMetadataHook } from '../MetricsQueryEditor/dataHooks';
import { useMetricNames, useMetricNamespaces } from './dataHooks';
import { useMetricNames, useMetricNamespaces, useMetricMetadata } from './dataHooks';
const WAIT_OPTIONS = {
timeout: 1000,
@ -15,7 +15,7 @@ const opt = (text: string, value: string) => ({ text, value });
interface TestScenario {
name: string;
hook: DataHook;
hook: DataHook | MetricsMetadataHook;
// For convenience, only need to define the azureMonitor part of the query for some tests
emptyQueryPartial: AzureMetricQuery;
@ -23,7 +23,7 @@ interface TestScenario {
topLevelCustomProperties?: Partial<AzureMonitorQuery>;
expectedCustomPropertyResults?: Array<AzureMonitorOption<string>>;
expectedOptions: AzureMonitorOption[];
expectedOptions: AzureMonitorOption[] | MetricMetadata;
}
describe('AzureMonitor: metrics dataHooks', () => {
@ -64,7 +64,6 @@ describe('AzureMonitor: metrics dataHooks', () => {
{ label: 'metric-$ENVIRONMENT', value: 'metric-$ENVIRONMENT' },
],
},
{
name: 'useMetricNamespaces',
hook: useMetricNamespaces,
@ -138,7 +137,25 @@ describe('AzureMonitor: metrics dataHooks', () => {
datasource.azureMonitorDatasource.getMetricNamespaces = jest
.fn()
.mockResolvedValue([opt('Compute Virtual Machine', 'azure/vmc'), opt('Database NS', 'azure/dbns')]);
datasource.azureMonitorDatasource.getMetricMetadata = jest.fn().mockResolvedValue({
primaryAggType: 'Average',
supportedAggTypes: ['Average'],
supportedTimeGrains: [
{ label: 'Auto', value: 'auto' },
{ label: '1 minute', value: 'PT1M' },
{ label: '5 minutes', value: 'PT5M' },
{ label: '15 minutes', value: 'PT15M' },
{ label: '30 minutes', value: 'PT30M' },
{ label: '1 hour', value: 'PT1H' },
{ label: '6 hours', value: 'PT6H' },
{ label: '12 hours', value: 'PT12H' },
{ label: '1 day', value: 'P1D' },
],
dimensions: [],
});
});
describe.each(testTable)('scenario %#: $name', (scenario) => {
it('returns values', async () => {
const query = {
@ -163,4 +180,56 @@ describe('AzureMonitor: metrics dataHooks', () => {
expect(result.current).toEqual(scenario.expectedCustomPropertyResults);
});
});
describe('useMetricsMetadataHook', () => {
const metricsMetadataConfig = {
name: 'useMetricMetadata',
hook: useMetricMetadata,
emptyQueryPartial: {
resourceUri:
'/subscriptions/99999999-cccc-bbbb-aaaa-9106972f9572/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana',
metricNamespace: 'azure/vm',
metricName: 'Average CPU',
},
customProperties: {},
expectedOptions: {
aggOptions: [{ label: 'Average', value: 'Average' }],
timeGrains: [
{ label: 'Auto', value: 'auto' },
{ label: '1 minute', value: 'PT1M' },
{ label: '5 minutes', value: 'PT5M' },
{ label: '15 minutes', value: 'PT15M' },
{ label: '30 minutes', value: 'PT30M' },
{ label: '1 hour', value: 'PT1H' },
{ label: '6 hours', value: 'PT6H' },
{ label: '12 hours', value: 'PT12H' },
{ label: '1 day', value: 'P1D' },
],
dimensions: [],
isLoading: false,
supportedAggTypes: ['Average'],
primaryAggType: 'Average',
},
};
it('returns values', async () => {
const query = {
...bareQuery,
azureMonitor: metricsMetadataConfig.emptyQueryPartial,
};
const { result, waitForNextUpdate } = renderHook(() => metricsMetadataConfig.hook(query, datasource, onChange));
await waitForNextUpdate(WAIT_OPTIONS);
expect(result.current).toEqual(metricsMetadataConfig.expectedOptions);
expect(onChange).toHaveBeenCalledWith({
...query,
azureMonitor: {
...query.azureMonitor,
aggregation: result.current.primaryAggType,
timeGrain: 'auto',
allowedTimeGrainsMs: [60_000, 300_000, 900_000, 1_800_000, 3_600_000, 21_600_000, 43_200_000, 86_400_000],
},
});
});
});
});

View File

@ -1,6 +1,9 @@
import { useEffect, useState } from 'react';
import { rangeUtil } from '@grafana/data';
import Datasource from '../../datasource';
import TimegrainConverter from '../../time_grain_converter';
import { AzureMonitorOption, AzureMonitorQuery } from '../../types';
import { toOption } from '../../utils/common';
import { useAsyncState } from '../../utils/useAsyncState';
@ -118,6 +121,11 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour
...query.azureMonitor,
aggregation: newAggregation,
timeGrain: newTimeGrain,
allowedTimeGrainsMs: metricMetadata.timeGrains
.filter((timeGrain) => timeGrain.value !== 'auto')
.map((timeGrain) =>
rangeUtil.intervalToMs(TimegrainConverter.createKbnUnitFromISO8601Duration(timeGrain.value))
),
},
});
}

View File

@ -45,13 +45,11 @@ export interface AzureMetricQuery {
dimensionFilters?: AzureMetricDimension[];
alias?: string;
top?: string;
allowedTimeGrainsMs?: number[];
/** @deprecated */
timeGrainUnit?: string;
/** @deprecated Remove this once angular is removed */
allowedTimeGrainsMs?: number[];
/** @deprecated This property was migrated to dimensionFilters and should only be accessed in the migration */
dimension?: string;