AzureMonitor: Remove unused code (#50399)

This commit is contained in:
Andres Martinez Gotor 2022-06-09 17:27:01 +02:00 committed by GitHub
parent 6da4feb2a9
commit 9703c9211e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 471 additions and 1858 deletions

View File

@ -1,10 +1,20 @@
import { render, screen, waitFor } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { selectOptionInTest } from 'test/helpers/selectOptionInTest';
import { config } from '@grafana/runtime';
import createMockDatasource from '../../__mocks__/datasource';
import { createMockInstanceSetttings } from '../../__mocks__/instanceSettings';
import createMockPanelData from '../../__mocks__/panelData';
import createMockQuery from '../../__mocks__/query';
import {
createMockResourceGroupsBySubscription,
createMockSubscriptions,
mockResourcesByResourceGroup,
} from '../../__mocks__/resourcePickerRows';
import ResourcePickerData from '../../resourcePicker/resourcePickerData';
import MetricsQueryEditor from './MetricsQueryEditor';
@ -13,259 +23,238 @@ const variableOptionGroup = {
options: [],
};
describe('Azure Monitor QueryEditor', () => {
const mockPanelData = createMockPanelData();
it('should render', async () => {
const mockDatasource = createMockDatasource();
render(
<MetricsQueryEditor
data={mockPanelData}
subscriptionId="123"
query={createMockQuery()}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={() => {}}
setError={() => {}}
/>
);
await waitFor(() => expect(screen.getByTestId('azure-monitor-metrics-query-editor')).toBeInTheDocument());
});
const tests = [
{
id: 'azure-monitor-metrics-query-editor-with-resource-picker',
},
{
id: 'azure-monitor-metrics-query-editor-with-experimental-ui',
},
];
it('should change the subscription ID when selected', async () => {
const mockDatasource = createMockDatasource();
const onChange = jest.fn();
const mockQuery = createMockQuery();
(mockQuery.azureMonitor ?? {}).metricName = undefined;
mockDatasource.azureMonitorDatasource.getSubscriptions = jest.fn().mockResolvedValueOnce([
{
value: 'abc-123',
text: 'Primary Subscription',
},
{
value: 'abc-456',
text: 'Another Subscription',
},
]);
export function createMockResourcePickerData() {
const mockDatasource = new ResourcePickerData(createMockInstanceSetttings());
render(
<MetricsQueryEditor
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
mockDatasource.getSubscriptions = jest.fn().mockResolvedValue(createMockSubscriptions());
mockDatasource.getResourceGroupsBySubscriptionId = jest
.fn()
.mockResolvedValue(createMockResourceGroupsBySubscription());
mockDatasource.getResourcesForResourceGroup = jest.fn().mockResolvedValue(mockResourcesByResourceGroup());
mockDatasource.getResourceURIFromWorkspace = jest.fn().mockReturnValue('');
mockDatasource.getResourceURIDisplayProperties = jest.fn().mockResolvedValue({});
const subscriptions = await screen.findByLabelText('Subscription');
await selectOptionInTest(subscriptions, 'Another Subscription');
return mockDatasource;
}
expect(onChange).toHaveBeenCalledWith({
...mockQuery,
subscription: 'abc-456',
azureMonitor: {
...mockQuery.azureMonitor,
resourceGroup: undefined,
metricDefinition: undefined,
metricNamespace: undefined,
resourceName: undefined,
metricName: undefined,
aggregation: undefined,
timeGrain: '',
dimensionFilters: [],
},
for (const t of tests) {
describe(`MetricsQueryEditor: ${t.id}`, () => {
const originalScrollIntoView = window.HTMLElement.prototype.scrollIntoView;
const mockPanelData = createMockPanelData();
beforeEach(() => {
window.HTMLElement.prototype.scrollIntoView = function () {};
config.featureToggles.azureMonitorExperimentalUI =
t.id === 'azure-monitor-metrics-query-editor-with-experimental-ui';
});
afterEach(() => {
window.HTMLElement.prototype.scrollIntoView = originalScrollIntoView;
config.featureToggles.azureMonitorExperimentalUI = false;
});
it('should render', async () => {
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
render(
<MetricsQueryEditor
data={mockPanelData}
query={createMockQuery()}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={() => {}}
setError={() => {}}
/>
);
expect(await screen.findByTestId(t.id)).toBeInTheDocument();
});
it('should change resource when a resource is selected in the ResourcePicker', async () => {
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
const query = createMockQuery();
delete query?.azureMonitor?.resourceUri;
const onChange = jest.fn();
render(
<MetricsQueryEditor
data={mockPanelData}
query={query}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
const resourcePickerButton = await screen.findByRole('button', { name: 'Select a resource' });
expect(resourcePickerButton).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Expand Primary Subscription' })).not.toBeInTheDocument();
resourcePickerButton.click();
const subscriptionButton = await screen.findByRole('button', { name: 'Expand Primary Subscription' });
expect(subscriptionButton).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Expand A Great Resource Group' })).not.toBeInTheDocument();
subscriptionButton.click();
const resourceGroupButton = await screen.findByRole('button', { name: 'Expand A Great Resource Group' });
expect(resourceGroupButton).toBeInTheDocument();
expect(screen.queryByLabelText('web-server')).not.toBeInTheDocument();
resourceGroupButton.click();
const checkbox = await screen.findByLabelText('web-server');
expect(checkbox).toBeInTheDocument();
expect(checkbox).not.toBeChecked();
await userEvent.click(checkbox);
expect(checkbox).toBeChecked();
await userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
expect(onChange).toBeCalledTimes(1);
expect(onChange).toBeCalledWith(
expect.objectContaining({
azureMonitor: expect.objectContaining({
resourceUri:
'/subscriptions/def-456/resourceGroups/dev-3/providers/Microsoft.Compute/virtualMachines/web-server',
}),
})
);
});
it('should reset metric namespace, metric name, and aggregation fields after selecting a new resource when a valid query has already been set', async () => {
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
const query = createMockQuery();
const onChange = jest.fn();
render(
<MetricsQueryEditor
data={mockPanelData}
query={query}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
const resourcePickerButton = await screen.findByRole('button', { name: /grafana/ });
expect(screen.getByText('Microsoft.Compute/virtualMachines')).toBeInTheDocument();
expect(screen.getByText('Metric A')).toBeInTheDocument();
expect(screen.getByText('Average')).toBeInTheDocument();
expect(resourcePickerButton).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Expand Primary Subscription' })).not.toBeInTheDocument();
resourcePickerButton.click();
const subscriptionButton = await screen.findByRole('button', { name: 'Expand Dev Subscription' });
expect(subscriptionButton).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Expand Development 3' })).not.toBeInTheDocument();
subscriptionButton.click();
const resourceGroupButton = await screen.findByRole('button', { name: 'Expand Development 3' });
expect(resourceGroupButton).toBeInTheDocument();
expect(screen.queryByLabelText('db-server')).not.toBeInTheDocument();
resourceGroupButton.click();
const checkbox = await screen.findByLabelText('db-server');
expect(checkbox).toBeInTheDocument();
expect(checkbox).not.toBeChecked();
await userEvent.click(checkbox);
expect(checkbox).toBeChecked();
await userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
expect(onChange).toBeCalledTimes(1);
expect(onChange).toBeCalledWith(
expect.objectContaining({
azureMonitor: expect.objectContaining({
resourceUri:
'/subscriptions/def-456/resourceGroups/dev-3/providers/Microsoft.Compute/virtualMachines/db-server',
metricNamespace: undefined,
metricName: undefined,
aggregation: undefined,
timeGrain: '',
dimensionFilters: [],
}),
})
);
});
it('should change the metric name when selected', async () => {
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
const onChange = jest.fn();
const mockQuery = createMockQuery();
mockDatasource.azureMonitorDatasource.getMetricNames = jest.fn().mockResolvedValue([
{
value: 'metric-a',
text: 'Metric A',
},
{
value: 'metric-b',
text: 'Metric B',
},
]);
render(
<MetricsQueryEditor
data={mockPanelData}
query={createMockQuery()}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
const metrics = await screen.findByLabelText('Metric');
expect(metrics).toBeInTheDocument();
await selectOptionInTest(metrics, 'Metric B');
expect(onChange).toHaveBeenLastCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
metricName: 'metric-b',
aggregation: undefined,
timeGrain: '',
},
});
});
it('should change the aggregation type when selected', async () => {
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
const onChange = jest.fn();
const mockQuery = createMockQuery();
render(
<MetricsQueryEditor
data={mockPanelData}
query={createMockQuery()}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
const aggregation = await screen.findByLabelText('Aggregation');
expect(aggregation).toBeInTheDocument();
await selectOptionInTest(aggregation, 'Maximum');
expect(onChange).toHaveBeenLastCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
aggregation: 'Maximum',
},
});
});
});
it('should change the resource group when selected', async () => {
const mockDatasource = createMockDatasource();
const onChange = jest.fn();
const mockQuery = createMockQuery();
mockDatasource.getResourceGroups = jest.fn().mockResolvedValue([
{ text: 'grafanastaging', value: 'grafanastaging' },
{ text: 'Grafana Prod', value: 'grafanaprod' },
]);
render(
<MetricsQueryEditor
data={mockPanelData}
subscriptionId="123"
query={createMockQuery()}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
await waitFor(() => expect(screen.getByTestId('azure-monitor-metrics-query-editor')).toBeInTheDocument());
const resourceGroup = await screen.findByLabelText('Resource group');
await selectOptionInTest(resourceGroup, 'Grafana Prod');
expect(onChange).toHaveBeenLastCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
resourceUri: '',
resourceGroup: 'grafanaprod',
metricDefinition: undefined,
metricNamespace: undefined,
resourceName: undefined,
metricName: undefined,
aggregation: undefined,
timeGrain: '',
dimensionFilters: [],
},
});
});
it('should change the resource type when selected', async () => {
const mockDatasource = createMockDatasource();
const onChange = jest.fn();
const mockQuery = createMockQuery();
mockDatasource.getMetricDefinitions = jest.fn().mockResolvedValue([
{ text: 'Virtual Machine', value: 'azure/vm' },
{ text: 'Database', value: 'azure/db' },
]);
render(
<MetricsQueryEditor
data={mockPanelData}
subscriptionId="123"
query={createMockQuery()}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
await waitFor(() => expect(screen.getByTestId('azure-monitor-metrics-query-editor')).toBeInTheDocument());
const resourceGroup = await screen.findByLabelText('Resource type');
await selectOptionInTest(resourceGroup, 'Virtual Machine');
expect(onChange).toHaveBeenLastCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
resourceUri: '',
metricDefinition: 'azure/vm',
resourceName: undefined,
metricNamespace: undefined,
metricName: undefined,
aggregation: undefined,
timeGrain: '',
dimensionFilters: [],
},
});
});
it('should change the resource name when selected', async () => {
const mockDatasource = createMockDatasource();
const onChange = jest.fn();
const mockQuery = createMockQuery();
mockDatasource.getResourceNames = jest.fn().mockResolvedValue([
{ text: 'ResourceName1', value: 'resource-name-1' },
{ text: 'ResourceName2', value: 'resource-name-2' },
]);
render(
<MetricsQueryEditor
data={mockPanelData}
subscriptionId="123"
query={createMockQuery()}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
await waitFor(() => expect(screen.getByTestId('azure-monitor-metrics-query-editor')).toBeInTheDocument());
const resourceGroup = await screen.findByLabelText('Resource name');
await selectOptionInTest(resourceGroup, 'ResourceName1');
expect(onChange).toHaveBeenLastCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
resourceUri: '',
resourceName: 'resource-name-1',
metricNamespace: undefined,
metricName: undefined,
aggregation: undefined,
timeGrain: '',
dimensionFilters: [],
},
});
});
it('should change the metric name when selected', async () => {
const mockDatasource = createMockDatasource();
const onChange = jest.fn();
const mockQuery = createMockQuery();
mockDatasource.azureMonitorDatasource.getMetricNames = jest.fn().mockResolvedValue([
{
value: 'metric-a',
text: 'Metric A',
},
{
value: 'metric-b',
text: 'Metric B',
},
]);
render(
<MetricsQueryEditor
data={mockPanelData}
subscriptionId="123"
query={createMockQuery()}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
await waitFor(() => expect(screen.getByTestId('azure-monitor-metrics-query-editor')).toBeInTheDocument());
const metrics = await screen.findByLabelText('Metric');
await selectOptionInTest(metrics, 'Metric B');
expect(onChange).toHaveBeenLastCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
metricName: 'metric-b',
aggregation: undefined,
timeGrain: '',
},
});
});
it('should change the aggregation type when selected', async () => {
const mockDatasource = createMockDatasource();
const onChange = jest.fn();
const mockQuery = createMockQuery();
render(
<MetricsQueryEditor
data={mockPanelData}
subscriptionId="123"
query={createMockQuery()}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
await waitFor(() => expect(screen.getByTestId('azure-monitor-metrics-query-editor')).toBeInTheDocument());
const aggregation = await screen.findByLabelText('Aggregation');
await selectOptionInTest(aggregation, 'Maximum');
expect(onChange).toHaveBeenLastCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
aggregation: 'Maximum',
},
});
});
});
}

View File

@ -1,37 +1,31 @@
import { css } from '@emotion/css';
import React from 'react';
import { PanelData } from '@grafana/data/src/types';
import { InlineFieldRow } from '@grafana/ui';
import { EditorRows, EditorRow, EditorFieldGroup } from '@grafana/experimental';
import { config } from '@grafana/runtime';
import { InlineFieldRow, useStyles2 } from '@grafana/ui';
import Datasource from '../../datasource';
import { AzureMonitorQuery, AzureMonitorOption, AzureMonitorErrorish } from '../../types';
import SubscriptionField from '../SubscriptionField';
import type Datasource from '../../datasource';
import type { AzureMonitorQuery, AzureMonitorOption, AzureMonitorErrorish } from '../../types';
import ResourceField from '../ResourceField';
import { ResourceRowType } from '../ResourcePicker/types';
import AggregationField from './AggregationField';
import DimensionFields from './DimensionFields';
import LegendFormatField from './LegendFormatField';
import MetricNameField from './MetricNameField';
import MetricNamespaceField from './MetricNamespaceField';
import ResourceGroupsField from './ResourceGroupsField';
import ResourceNameField from './ResourceNameField';
import ResourceTypeField from './ResourceTypeField';
import NewDimensionFields from './NewDimensionFields';
import TimeGrainField from './TimeGrainField';
import TopField from './TopField';
import {
useMetricNames,
useMetricNamespaces,
useResourceGroups,
useResourceNames,
useResourceTypes,
useSubscriptions,
useMetricMetadata,
} from './dataHooks';
import { useMetricNames, useMetricNamespaces, useMetricMetadata } from './dataHooks';
import { setResource } from './setQueryValue';
interface MetricsQueryEditorProps {
data: PanelData | undefined;
query: AzureMonitorQuery;
datasource: Datasource;
subscriptionId?: string;
onChange: (newQuery: AzureMonitorQuery) => void;
variableOptionGroup: { label: string; options: AzureMonitorOption[] };
setError: (source: string, error: AzureMonitorErrorish | undefined) => void;
@ -41,133 +35,194 @@ const MetricsQueryEditor: React.FC<MetricsQueryEditorProps> = ({
data,
query,
datasource,
subscriptionId,
variableOptionGroup,
onChange,
setError,
}) => {
const styles = useStyles2(getStyles);
const metricsMetadata = useMetricMetadata(query, datasource, onChange);
const subscriptions = useSubscriptions(query, datasource, onChange, setError);
const resourceGroups = useResourceGroups(query, datasource, onChange, setError);
const resourceTypes = useResourceTypes(query, datasource, onChange, setError);
const resourceNames = useResourceNames(query, datasource, onChange, setError);
const metricNames = useMetricNames(query, datasource, onChange, setError);
const metricNamespaces = useMetricNamespaces(query, datasource, onChange, setError);
const metricNames = useMetricNames(query, datasource, onChange, setError);
if (config.featureToggles.azureMonitorExperimentalUI) {
return (
<span data-testid="azure-monitor-metrics-query-editor-with-experimental-ui">
<EditorRows>
<EditorRow>
<EditorFieldGroup>
<ResourceField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
selectableEntryTypes={[ResourceRowType.Resource]}
setResource={setResource}
resourceUri={query.azureMonitor?.resourceUri}
/>
</EditorFieldGroup>
</EditorRow>
return (
<div data-testid="azure-monitor-metrics-query-editor">
<InlineFieldRow>
<SubscriptionField
subscriptions={subscriptions}
query={query}
datasource={datasource}
subscriptionId={subscriptionId}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
<EditorRow>
<EditorFieldGroup>
<MetricNamespaceField
metricNamespaces={metricNamespaces}
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
<MetricNameField
metricNames={metricNames}
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
</EditorFieldGroup>
</EditorRow>
<EditorRow>
<EditorFieldGroup>
<AggregationField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
aggregationOptions={metricsMetadata?.aggOptions ?? []}
isLoading={metricsMetadata.isLoading}
/>
<TimeGrainField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
timeGrainOptions={metricsMetadata?.timeGrains ?? []}
/>
</EditorFieldGroup>
</EditorRow>
<EditorRow>
<EditorFieldGroup>
<NewDimensionFields
data={data}
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
dimensionOptions={metricsMetadata?.dimensions ?? []}
/>
</EditorFieldGroup>
</EditorRow>
<EditorRow>
<EditorFieldGroup>
<TopField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
<LegendFormatField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
</EditorFieldGroup>
</EditorRow>
</EditorRows>
</span>
);
} else {
return (
<div data-testid="azure-monitor-metrics-query-editor-with-resource-picker">
<InlineFieldRow className={styles.row}>
<ResourceField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
selectableEntryTypes={[ResourceRowType.Resource]}
setResource={setResource}
resourceUri={query.azureMonitor?.resourceUri}
/>
</InlineFieldRow>
<ResourceGroupsField
resourceGroups={resourceGroups}
<InlineFieldRow className={styles.row}>
<MetricNamespaceField
metricNamespaces={metricNamespaces}
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
<MetricNameField
metricNames={metricNames}
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
</InlineFieldRow>
<InlineFieldRow className={styles.row}>
<AggregationField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
aggregationOptions={metricsMetadata?.aggOptions ?? []}
isLoading={metricsMetadata.isLoading}
/>
<TimeGrainField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
timeGrainOptions={metricsMetadata?.timeGrains ?? []}
/>
</InlineFieldRow>
<DimensionFields
data={data}
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
dimensionOptions={metricsMetadata?.dimensions ?? []}
/>
<TopField
query={query}
datasource={datasource}
subscriptionId={subscriptionId}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
</InlineFieldRow>
<InlineFieldRow>
<ResourceTypeField
resourceTypes={resourceTypes}
<LegendFormatField
query={query}
datasource={datasource}
subscriptionId={subscriptionId}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
<ResourceNameField
resourceNames={resourceNames}
query={query}
datasource={datasource}
subscriptionId={subscriptionId}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
</InlineFieldRow>
<InlineFieldRow>
<MetricNamespaceField
metricNamespaces={metricNamespaces}
query={query}
datasource={datasource}
subscriptionId={subscriptionId}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
<MetricNameField
metricNames={metricNames}
query={query}
datasource={datasource}
subscriptionId={subscriptionId}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
</InlineFieldRow>
<InlineFieldRow>
<AggregationField
query={query}
datasource={datasource}
subscriptionId={subscriptionId}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
aggregationOptions={metricsMetadata?.aggOptions ?? []}
isLoading={metricsMetadata.isLoading}
/>
<TimeGrainField
query={query}
datasource={datasource}
subscriptionId={subscriptionId}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
timeGrainOptions={metricsMetadata?.timeGrains ?? []}
/>
</InlineFieldRow>
<DimensionFields
data={data}
query={query}
datasource={datasource}
subscriptionId={subscriptionId}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
dimensionOptions={metricsMetadata?.dimensions ?? []}
/>
<TopField
query={query}
datasource={datasource}
subscriptionId={subscriptionId}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
<LegendFormatField
query={query}
datasource={datasource}
subscriptionId={subscriptionId}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
</div>
);
</div>
);
}
};
const getStyles = () => ({
row: css({
rowGap: 0,
}),
});
export default MetricsQueryEditor;

View File

@ -1,46 +0,0 @@
import React, { useCallback, useMemo } from 'react';
import { SelectableValue } from '@grafana/data';
import { Select } from '@grafana/ui';
import { AzureQueryEditorFieldProps, AzureMonitorOption } from '../../types';
import { Field } from '../Field';
import { setResourceGroup } from './setQueryValue';
interface ResourceGroupsFieldProps extends AzureQueryEditorFieldProps {
resourceGroups: AzureMonitorOption[];
}
const ResourceGroupsField: React.FC<ResourceGroupsFieldProps> = ({
query,
resourceGroups,
variableOptionGroup,
onQueryChange,
setError,
}) => {
const handleChange = useCallback(
(change: SelectableValue<string>) => {
const newQuery = setResourceGroup(query, change.value);
onQueryChange(newQuery);
},
[onQueryChange, query]
);
const options = useMemo(() => [...resourceGroups, variableOptionGroup], [resourceGroups, variableOptionGroup]);
return (
<Field label="Resource group">
<Select
inputId="azure-monitor-metrics-resource-group-field"
value={query.azureMonitor?.resourceGroup}
onChange={handleChange}
options={options}
width={38}
allowCustomValue
/>
</Field>
);
};
export default ResourceGroupsField;

View File

@ -1,46 +0,0 @@
import React, { useCallback, useMemo } from 'react';
import { SelectableValue } from '@grafana/data';
import { Select } from '@grafana/ui';
import { AzureQueryEditorFieldProps, AzureMonitorOption } from '../../types';
import { Field } from '../Field';
import { setResourceName } from './setQueryValue';
interface ResourceNameFieldProps extends AzureQueryEditorFieldProps {
resourceNames: AzureMonitorOption[];
}
const ResourceNameField: React.FC<ResourceNameFieldProps> = ({
resourceNames,
query,
variableOptionGroup,
onQueryChange,
}) => {
const handleChange = useCallback(
(change: SelectableValue<string>) => {
const newQuery = setResourceName(query, change.value);
onQueryChange(newQuery);
},
[onQueryChange, query]
);
const options = useMemo(() => [...resourceNames, variableOptionGroup], [resourceNames, variableOptionGroup]);
const value = query.azureMonitor?.resourceName ?? null;
return (
<Field label="Resource name">
<Select
inputId="azure-monitor-metrics-resource-name-field"
value={value}
onChange={handleChange}
options={options}
width={38}
allowCustomValue
/>
</Field>
);
};
export default ResourceNameField;

View File

@ -1,50 +0,0 @@
import React, { useCallback, useMemo } from 'react';
import { SelectableValue } from '@grafana/data';
import { Select } from '@grafana/ui';
import { AzureQueryEditorFieldProps, AzureMonitorOption } from '../../types';
import { Field } from '../Field';
import { setResourceType } from './setQueryValue';
interface NamespaceFieldProps extends AzureQueryEditorFieldProps {
resourceTypes: AzureMonitorOption[];
}
const NamespaceField: React.FC<NamespaceFieldProps> = ({
resourceTypes,
query,
variableOptionGroup,
onQueryChange,
}) => {
const handleChange = useCallback(
(change: SelectableValue<string>) => {
if (!change.value) {
return;
}
const newQuery = setResourceType(query, change.value);
onQueryChange(newQuery);
},
[onQueryChange, query]
);
const options = useMemo(() => [...resourceTypes, variableOptionGroup], [resourceTypes, variableOptionGroup]);
return (
<Field label="Resource type">
{/* It's expected that the label reads Resource type but the property is metricDefinition */}
<Select
inputId="azure-monitor-metrics-resource-type-field"
value={query.azureMonitor?.metricDefinition}
onChange={handleChange}
options={options}
width={38}
allowCustomValue
/>
</Field>
);
};
export default NamespaceField;

View File

@ -5,17 +5,12 @@ import Datasource from '../../datasource';
import { AzureMetricQuery, AzureMonitorOption, AzureMonitorQuery, AzureQueryType } from '../../types';
import {
useMetricNames,
useMetricNamespaces,
useMetricMetadata,
DataHook,
MetricMetadata,
MetricsMetadataHook,
updateSubscriptions,
useMetricMetadata,
useMetricNames,
useMetricNamespaces,
useResourceGroups,
useResourceNames,
useResourceTypes,
useSubscriptions,
} from './dataHooks';
const WAIT_OPTIONS = {
@ -45,124 +40,17 @@ describe('AzureMonitor: metrics dataHooks', () => {
};
const testTable: TestScenario[] = [
{
name: 'useSubscriptions',
hook: useSubscriptions,
emptyQueryPartial: {},
topLevelCustomProperties: {
subscription: 'subscription-$ENVIRONMENT',
},
customProperties: {},
expectedOptions: [
{
label: 'sub-abc-123',
value: 'sub-abc-123',
},
],
expectedCustomPropertyResults: [
{
label: 'sub-abc-123',
value: 'sub-abc-123',
},
{
label: 'subscription-$ENVIRONMENT',
value: 'subscription-$ENVIRONMENT',
},
],
},
{
name: 'useResourceGroups',
hook: useResourceGroups,
emptyQueryPartial: {},
customProperties: {
resourceGroup: 'resource-group-$ENVIRONMENT',
},
expectedOptions: [
{
label: 'Web App - Production',
value: 'web-app-production',
},
{
label: 'Web App - Development',
value: 'web-app-development',
},
],
expectedCustomPropertyResults: [
{ label: 'Web App - Production', value: 'web-app-production' },
{ label: 'Web App - Development', value: 'web-app-development' },
{ label: 'resource-group-$ENVIRONMENT', value: 'resource-group-$ENVIRONMENT' },
],
},
{
name: 'useResourceTypes',
hook: useResourceTypes,
emptyQueryPartial: {
resourceGroup: 'web-app-development',
},
customProperties: {
resourceGroup: 'web-app-development',
metricDefinition: 'azure/resource-type-$ENVIRONMENT',
},
expectedOptions: [
{
label: 'Virtual Machine',
value: 'azure/vm',
},
{
label: 'Database',
value: 'azure/db',
},
],
expectedCustomPropertyResults: [
{ label: 'Virtual Machine', value: 'azure/vm' },
{ label: 'Database', value: 'azure/db' },
{ label: 'azure/resource-type-$ENVIRONMENT', value: 'azure/resource-type-$ENVIRONMENT' },
],
},
{
name: 'useResourceNames',
hook: useResourceNames,
emptyQueryPartial: {
resourceGroup: 'web-app-development',
metricDefinition: 'azure/vm',
},
customProperties: {
resourceGroup: 'web-app-development',
metricDefinition: 'azure/vm',
resourceName: 'resource-name-$ENVIRONMENT',
},
expectedOptions: [
{
label: 'Web server',
value: 'web-server',
},
{
label: 'Job server',
value: 'job-server',
},
],
expectedCustomPropertyResults: [
{ label: 'Web server', value: 'web-server' },
{ label: 'Job server', value: 'job-server' },
{ label: 'resource-name-$ENVIRONMENT', value: 'resource-name-$ENVIRONMENT' },
],
},
{
name: 'useMetricNames',
hook: useMetricNames,
emptyQueryPartial: {
resourceGroup: 'web-app-development',
metricDefinition: 'azure/vm',
resourceName: 'web-server',
resourceUri:
'/subscriptions/99999999-cccc-bbbb-aaaa-9106972f9572/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana',
metricNamespace: 'azure/vm',
},
customProperties: {
resourceGroup: 'web-app-development',
metricDefinition: 'azure/vm',
resourceName: 'web-server',
resourceUri:
'/subscriptions/99999999-cccc-bbbb-aaaa-9106972f9572/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana',
metricNamespace: 'azure/vm',
metricName: 'metric-$ENVIRONMENT',
},
@ -182,20 +70,17 @@ describe('AzureMonitor: metrics dataHooks', () => {
{ label: 'metric-$ENVIRONMENT', value: 'metric-$ENVIRONMENT' },
],
},
{
name: 'useMetricNamespaces',
hook: useMetricNamespaces,
emptyQueryPartial: {
resourceGroup: 'web-app-development',
metricDefinition: 'azure/vm',
resourceName: 'web-server',
resourceUri:
'/subscriptions/99999999-cccc-bbbb-aaaa-9106972f9572/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana',
metricNamespace: 'azure/vm',
},
customProperties: {
resourceGroup: 'web-app-development',
metricDefinition: 'azure/vm',
resourceName: 'web-server',
resourceUri:
'/subscriptions/99999999-cccc-bbbb-aaaa-9106972f9572/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana',
metricNamespace: 'azure/vm-$ENVIRONMENT',
metricName: 'metric-name',
},
@ -307,11 +192,9 @@ describe('AzureMonitor: metrics dataHooks', () => {
name: 'useMetricMetadata',
hook: useMetricMetadata,
emptyQueryPartial: {
resourceGroup: 'web-app-development',
metricDefinition: 'azure/vm',
resourceName: 'web-server',
resourceUri:
'/subscriptions/99999999-cccc-bbbb-aaaa-9106972f9572/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana',
metricNamespace: 'azure/vm',
subscription: 'test-sub',
metricName: 'Average CPU',
},
customProperties: {},
@ -356,81 +239,3 @@ describe('AzureMonitor: metrics dataHooks', () => {
});
});
});
describe('AzureMonitor: updateSubscriptions', () => {
const bareQuery = {
refId: 'A',
queryType: AzureQueryType.AzureMonitor,
};
[
{
description: 'should not update with no subscriptions',
query: bareQuery,
subscriptionOptions: [],
},
{
description: 'should not update with the subscription as an option',
query: { ...bareQuery, subscription: 'foo' },
subscriptionOptions: [{ label: 'foo', value: 'foo' }],
},
{
description: 'should not update with a template variable',
query: { ...bareQuery, subscription: '$foo' },
subscriptionOptions: [],
},
{
description: 'should update with the first subscription',
query: { ...bareQuery },
subscriptionOptions: [{ label: 'foo', value: 'foo' }],
onChangeArgs: {
...bareQuery,
subscription: 'foo',
azureMonitor: {
dimensionFilters: [],
timeGrain: '',
resourceUri: '',
},
},
},
{
description: 'should update with the default subscription if the current subsription does not exists',
query: { ...bareQuery, subscription: 'bar' },
subscriptionOptions: [{ label: 'foo', value: 'foo' }],
onChangeArgs: {
...bareQuery,
subscription: 'foo',
azureMonitor: {
dimensionFilters: [],
timeGrain: '',
resourceUri: '',
},
},
},
{
description: 'should clean up if neither the default sub nor the current sub exists',
query: { ...bareQuery, subscription: 'bar' },
subscriptionOptions: [{ label: 'foo', value: 'foo' }],
defaultSubscription: 'foobar',
onChangeArgs: {
...bareQuery,
subscription: '',
azureMonitor: {
dimensionFilters: [],
timeGrain: '',
resourceUri: '',
},
},
},
].forEach((test) => {
it(test.description, () => {
const onChange = jest.fn();
updateSubscriptions(test.query, test.subscriptionOptions, onChange, test.defaultSubscription);
if (test.onChangeArgs) {
expect(onChange).toHaveBeenCalledWith(test.onChangeArgs);
} else {
expect(onChange).not.toHaveBeenCalled();
}
});
});
});

View File

@ -5,24 +5,12 @@ 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 { toOption } from '../../utils/common';
import { useAsyncState } from '../../utils/useAsyncState';
import { setMetricNamespace, setSubscriptionID } from './setQueryValue';
export interface MetricMetadata {
aggOptions: AzureMonitorOption[];
timeGrains: AzureMonitorOption[];
dimensions: AzureMonitorOption[];
isLoading: boolean;
// These two properties are only used within the hook, and not elsewhere
supportedAggTypes: string[];
primaryAggType: string | undefined;
}
import { setMetricNamespace } from './setQueryValue';
type SetErrorFn = (source: string, error: AzureMonitorErrorish | undefined) => void;
type OnChangeFn = (newQuery: AzureMonitorQuery) => void;
export type DataHook = (
query: AzureMonitorQuery,
@ -37,138 +25,29 @@ export type MetricsMetadataHook = (
onChange: OnChangeFn
) => MetricMetadata;
export const updateSubscriptions = (
query: AzureMonitorQuery,
subscriptionOptions: AzureMonitorOption[],
onChange: OnChangeFn,
defaultSubscription?: string
) => {
const { subscription } = query;
export interface MetricMetadata {
aggOptions: AzureMonitorOption[];
timeGrains: AzureMonitorOption[];
dimensions: AzureMonitorOption[];
isLoading: boolean;
// Return early if subscriptions havent loaded, or if the query already has a subscription
if (
!subscriptionOptions.length ||
(subscription && (hasOption(subscriptionOptions, subscription) || subscription.includes('$')))
) {
return;
}
// These two properties are only used within the hook, and not elsewhere
supportedAggTypes: string[];
primaryAggType: string | undefined;
}
const defaultSub = defaultSubscription || subscriptionOptions[0].value;
if (!subscription && defaultSub && hasOption(subscriptionOptions, defaultSub)) {
onChange(setSubscriptionID(query, defaultSub));
}
// Check if the current subscription is in the list of subscriptions
if (subscription && !hasOption(subscriptionOptions, subscription)) {
if (hasOption(subscriptionOptions, defaultSub)) {
// Use the default sub if is on theh list
onChange(setSubscriptionID(query, defaultSub));
} else {
// Neither the current subscription nor the defaultSub is on the list, remove it
onChange(setSubscriptionID(query, ''));
}
}
};
export const useSubscriptions: DataHook = (query, datasource, onChange, setError) => {
const defaultSubscription = datasource.azureMonitorDatasource.defaultSubscriptionId;
const { subscription } = query;
const subscriptionOptions = useAsyncState(
async () => {
const results = await datasource.azureMonitorDatasource.getSubscriptions();
const options = formatOptions(results, subscription);
return options;
},
setError,
[]
);
useEffect(() => {
updateSubscriptions(query, subscriptionOptions, onChange, defaultSubscription);
}, [subscriptionOptions, query, defaultSubscription, onChange]);
return subscriptionOptions;
};
export const useResourceGroups: DataHook = (query, datasource, onChange, setError) => {
const { subscription } = query;
const { resourceGroup } = query.azureMonitor ?? {};
return useAsyncState(
async () => {
if (!subscription) {
return;
}
const results = await datasource.getResourceGroups(subscription);
const options = formatOptions(results, resourceGroup);
return options;
},
setError,
[subscription]
);
};
export const useResourceTypes: DataHook = (query, datasource, onChange, setError) => {
const { subscription } = query;
const { resourceGroup, metricDefinition } = query.azureMonitor ?? {};
return useAsyncState(
async () => {
if (!(subscription && resourceGroup)) {
return;
}
const results = await datasource.getMetricDefinitions(subscription, resourceGroup);
const options = formatOptions(results, metricDefinition);
return options;
},
setError,
[subscription, resourceGroup]
);
};
export const useResourceNames: DataHook = (query, datasource, onChange, setError) => {
const { subscription } = query;
const { resourceGroup, metricDefinition, resourceName } = query.azureMonitor ?? {};
return useAsyncState(
async () => {
if (!(subscription && resourceGroup && metricDefinition)) {
return;
}
const results = await datasource.getResourceNames(subscription, resourceGroup, metricDefinition);
const options = formatOptions(results, resourceName);
return options;
},
setError,
[subscription, resourceGroup, metricDefinition]
);
};
type OnChangeFn = (newQuery: AzureMonitorQuery) => void;
export const useMetricNamespaces: DataHook = (query, datasource, onChange, setError) => {
const { subscription } = query;
const { resourceGroup, metricDefinition, resourceName, metricNamespace } = query.azureMonitor ?? {};
const { metricNamespace, resourceUri } = query.azureMonitor ?? {};
const metricNamespaces = useAsyncState(
async () => {
if (!(subscription && resourceGroup && metricDefinition && resourceName)) {
if (!resourceUri) {
return;
}
const results = await datasource.azureMonitorDatasource.getMetricNamespaces({
subscription,
resourceGroup,
metricDefinition,
resourceName,
});
const results = await datasource.azureMonitorDatasource.getMetricNamespaces({ resourceUri });
const options = formatOptions(results, metricNamespace);
// Do some cleanup of the query state if need be
@ -179,67 +58,61 @@ export const useMetricNamespaces: DataHook = (query, datasource, onChange, setEr
return options;
},
setError,
[subscription, resourceGroup, metricDefinition, resourceName]
[resourceUri]
);
return metricNamespaces;
};
export const useMetricNames: DataHook = (query, datasource, onChange, setError) => {
const { subscription } = query;
const { resourceGroup, metricDefinition, resourceName, metricNamespace, metricName } = query.azureMonitor ?? {};
const { metricNamespace, metricName, resourceUri } = query.azureMonitor ?? {};
return useAsyncState(
async () => {
if (!(subscription && resourceGroup && metricDefinition && resourceName && metricNamespace)) {
if (!(metricNamespace && resourceUri)) {
return;
}
const results = await datasource.azureMonitorDatasource.getMetricNames({
subscription,
resourceGroup,
metricDefinition,
resourceName,
metricNamespace,
});
const results = await datasource.azureMonitorDatasource.getMetricNames({ resourceUri, metricNamespace });
const options = formatOptions(results, metricName);
return options;
},
setError,
[subscription, resourceGroup, metricDefinition, resourceName, metricNamespace]
[resourceUri, metricNamespace]
);
};
export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasource, onChange: OnChangeFn) => {
const [metricMetadata, setMetricMetadata] = useState<MetricMetadata>({
aggOptions: [],
timeGrains: [],
dimensions: [],
isLoading: false,
supportedAggTypes: [],
primaryAggType: undefined,
});
const defaultMetricMetadata: MetricMetadata = {
aggOptions: [],
timeGrains: [],
dimensions: [],
isLoading: false,
supportedAggTypes: [],
primaryAggType: undefined,
};
const { subscription } = query;
const { resourceGroup, metricDefinition, resourceName, metricNamespace, metricName, aggregation, timeGrain } =
query.azureMonitor ?? {};
export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasource, onChange: OnChangeFn) => {
const [metricMetadata, setMetricMetadata] = useState<MetricMetadata>(defaultMetricMetadata);
const { resourceUri, metricNamespace, metricName, aggregation, timeGrain } = query.azureMonitor ?? {};
// Fetch new metric metadata when the fields change
useEffect(() => {
if (!(subscription && resourceGroup && metricDefinition && resourceName && metricNamespace && metricName)) {
if (!(resourceUri && metricNamespace && metricName)) {
setMetricMetadata(defaultMetricMetadata);
return;
}
datasource.azureMonitorDatasource
.getMetricMetadata({ subscription, resourceGroup, metricDefinition, resourceName, metricNamespace, metricName })
.getMetricMetadata({ resourceUri, metricNamespace, metricName })
.then((metadata) => {
// TODO: Move the aggregationTypes and timeGrain defaults into `getMetricMetadata`
const aggregations = (metadata.supportedAggTypes || [metadata.primaryAggType]).map((v) => ({
label: v,
value: v,
}));
setMetricMetadata({
aggOptions: aggregations,
timeGrains: metadata.supportedTimeGrains,
@ -249,7 +122,7 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour
primaryAggType: metadata.primaryAggType,
});
});
}, [datasource, subscription, resourceGroup, metricDefinition, resourceName, metricNamespace, metricName]);
}, [datasource, resourceUri, metricNamespace, metricName]);
// Update the query state in response to the meta data changing
useEffect(() => {
@ -286,7 +159,7 @@ function formatOptions(
const options = rawResults.map(toOption);
// account for custom values that might have been set in json file like ones crafted with a template variable (ex: "cloud-datasource-resource-$Environment")
if (selectedValue && !options.find((option) => option.value === selectedValue)) {
if (selectedValue && !options.find((option) => option.value === selectedValue.toLowerCase())) {
options.push({ label: selectedValue, value: selectedValue });
}

View File

@ -1 +0,0 @@
export { default } from './MetricsQueryEditor';

View File

@ -16,95 +16,6 @@ export function setResource(query: AzureMonitorQuery, resourceURI: string | unde
};
}
export function setSubscriptionID(query: AzureMonitorQuery, subscriptionID: string): AzureMonitorQuery {
if (query.subscription === subscriptionID) {
return query;
}
return {
...query,
subscription: subscriptionID,
azureMonitor: {
...query.azureMonitor,
resourceUri: '',
resourceGroup: undefined,
metricDefinition: undefined,
metricNamespace: undefined,
resourceName: undefined,
metricName: undefined,
aggregation: undefined,
timeGrain: '',
dimensionFilters: [],
},
};
}
export function setResourceGroup(query: AzureMonitorQuery, resourceGroup: string | undefined): AzureMonitorQuery {
if (query.azureMonitor?.resourceGroup === resourceGroup) {
return query;
}
return {
...query,
azureMonitor: {
...query.azureMonitor,
resourceUri: '',
resourceGroup: resourceGroup,
metricDefinition: undefined,
metricNamespace: undefined,
resourceName: undefined,
metricName: undefined,
aggregation: undefined,
timeGrain: '',
dimensionFilters: [],
},
};
}
// In the query as "metricDefinition" for some reason
export function setResourceType(query: AzureMonitorQuery, resourceType: string | undefined): AzureMonitorQuery {
if (query.azureMonitor?.metricDefinition === resourceType) {
return query;
}
const newQuery = {
...query,
azureMonitor: {
...query.azureMonitor,
resourceUri: '',
metricDefinition: resourceType,
resourceName: undefined,
metricNamespace: undefined,
metricName: undefined,
aggregation: undefined,
timeGrain: '',
dimensionFilters: [],
},
};
return newQuery;
}
export function setResourceName(query: AzureMonitorQuery, resourceName: string | undefined): AzureMonitorQuery {
if (query.azureMonitor?.resourceName === resourceName) {
return query;
}
return {
...query,
azureMonitor: {
...query.azureMonitor,
resourceUri: '',
resourceName: resourceName,
metricNamespace: undefined,
metricName: undefined,
aggregation: undefined,
timeGrain: '',
dimensionFilters: [],
},
};
}
export function setMetricNamespace(query: AzureMonitorQuery, metricNamespace: string | undefined): AzureMonitorQuery {
if (query.azureMonitor?.metricNamespace === metricNamespace) {
return query;

View File

@ -1,260 +0,0 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { selectOptionInTest } from 'test/helpers/selectOptionInTest';
import { config } from '@grafana/runtime';
import createMockDatasource from '../../__mocks__/datasource';
import { createMockInstanceSetttings } from '../../__mocks__/instanceSettings';
import createMockPanelData from '../../__mocks__/panelData';
import createMockQuery from '../../__mocks__/query';
import {
createMockResourceGroupsBySubscription,
createMockSubscriptions,
mockResourcesByResourceGroup,
} from '../../__mocks__/resourcePickerRows';
import ResourcePickerData from '../../resourcePicker/resourcePickerData';
import MetricsQueryEditor from './MetricsQueryEditor';
const variableOptionGroup = {
label: 'Template variables',
options: [],
};
const tests = [
{
id: 'azure-monitor-metrics-query-editor-with-resource-picker',
},
{
id: 'azure-monitor-metrics-query-editor-with-experimental-ui',
},
];
export function createMockResourcePickerData() {
const mockDatasource = new ResourcePickerData(createMockInstanceSetttings());
mockDatasource.getSubscriptions = jest.fn().mockResolvedValue(createMockSubscriptions());
mockDatasource.getResourceGroupsBySubscriptionId = jest
.fn()
.mockResolvedValue(createMockResourceGroupsBySubscription());
mockDatasource.getResourcesForResourceGroup = jest.fn().mockResolvedValue(mockResourcesByResourceGroup());
mockDatasource.getResourceURIFromWorkspace = jest.fn().mockReturnValue('');
mockDatasource.getResourceURIDisplayProperties = jest.fn().mockResolvedValue({});
return mockDatasource;
}
for (const t of tests) {
describe(`MetricsQueryEditor: ${t.id}`, () => {
const originalScrollIntoView = window.HTMLElement.prototype.scrollIntoView;
const mockPanelData = createMockPanelData();
beforeEach(() => {
window.HTMLElement.prototype.scrollIntoView = function () {};
config.featureToggles.azureMonitorExperimentalUI =
t.id === 'azure-monitor-metrics-query-editor-with-experimental-ui';
});
afterEach(() => {
window.HTMLElement.prototype.scrollIntoView = originalScrollIntoView;
config.featureToggles.azureMonitorExperimentalUI = false;
});
it('should render', async () => {
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
render(
<MetricsQueryEditor
data={mockPanelData}
query={createMockQuery()}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={() => {}}
setError={() => {}}
/>
);
expect(await screen.findByTestId(t.id)).toBeInTheDocument();
});
it('should change resource when a resource is selected in the ResourcePicker', async () => {
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
const query = createMockQuery();
delete query?.azureMonitor?.resourceUri;
const onChange = jest.fn();
render(
<MetricsQueryEditor
data={mockPanelData}
query={query}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
const resourcePickerButton = await screen.findByRole('button', { name: 'Select a resource' });
expect(resourcePickerButton).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Expand Primary Subscription' })).not.toBeInTheDocument();
resourcePickerButton.click();
const subscriptionButton = await screen.findByRole('button', { name: 'Expand Primary Subscription' });
expect(subscriptionButton).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Expand A Great Resource Group' })).not.toBeInTheDocument();
subscriptionButton.click();
const resourceGroupButton = await screen.findByRole('button', { name: 'Expand A Great Resource Group' });
expect(resourceGroupButton).toBeInTheDocument();
expect(screen.queryByLabelText('web-server')).not.toBeInTheDocument();
resourceGroupButton.click();
const checkbox = await screen.findByLabelText('web-server');
expect(checkbox).toBeInTheDocument();
expect(checkbox).not.toBeChecked();
await userEvent.click(checkbox);
expect(checkbox).toBeChecked();
await userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
expect(onChange).toBeCalledTimes(1);
expect(onChange).toBeCalledWith(
expect.objectContaining({
azureMonitor: expect.objectContaining({
resourceUri:
'/subscriptions/def-456/resourceGroups/dev-3/providers/Microsoft.Compute/virtualMachines/web-server',
}),
})
);
});
it('should reset metric namespace, metric name, and aggregation fields after selecting a new resource when a valid query has already been set', async () => {
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
const query = createMockQuery();
const onChange = jest.fn();
render(
<MetricsQueryEditor
data={mockPanelData}
query={query}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
const resourcePickerButton = await screen.findByRole('button', { name: /grafana/ });
expect(screen.getByText('Microsoft.Compute/virtualMachines')).toBeInTheDocument();
expect(screen.getByText('Metric A')).toBeInTheDocument();
expect(screen.getByText('Average')).toBeInTheDocument();
expect(resourcePickerButton).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Expand Primary Subscription' })).not.toBeInTheDocument();
resourcePickerButton.click();
const subscriptionButton = await screen.findByRole('button', { name: 'Expand Dev Subscription' });
expect(subscriptionButton).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Expand Development 3' })).not.toBeInTheDocument();
subscriptionButton.click();
const resourceGroupButton = await screen.findByRole('button', { name: 'Expand Development 3' });
expect(resourceGroupButton).toBeInTheDocument();
expect(screen.queryByLabelText('db-server')).not.toBeInTheDocument();
resourceGroupButton.click();
const checkbox = await screen.findByLabelText('db-server');
expect(checkbox).toBeInTheDocument();
expect(checkbox).not.toBeChecked();
await userEvent.click(checkbox);
expect(checkbox).toBeChecked();
await userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
expect(onChange).toBeCalledTimes(1);
expect(onChange).toBeCalledWith(
expect.objectContaining({
azureMonitor: expect.objectContaining({
resourceUri:
'/subscriptions/def-456/resourceGroups/dev-3/providers/Microsoft.Compute/virtualMachines/db-server',
metricNamespace: undefined,
metricName: undefined,
aggregation: undefined,
timeGrain: '',
dimensionFilters: [],
}),
})
);
});
it('should change the metric name when selected', async () => {
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
const onChange = jest.fn();
const mockQuery = createMockQuery();
mockDatasource.azureMonitorDatasource.getMetricNames = jest.fn().mockResolvedValue([
{
value: 'metric-a',
text: 'Metric A',
},
{
value: 'metric-b',
text: 'Metric B',
},
]);
render(
<MetricsQueryEditor
data={mockPanelData}
query={createMockQuery()}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
const metrics = await screen.findByLabelText('Metric');
expect(metrics).toBeInTheDocument();
await selectOptionInTest(metrics, 'Metric B');
expect(onChange).toHaveBeenLastCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
metricName: 'metric-b',
aggregation: undefined,
timeGrain: '',
},
});
});
it('should change the aggregation type when selected', async () => {
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
const onChange = jest.fn();
const mockQuery = createMockQuery();
render(
<MetricsQueryEditor
data={mockPanelData}
query={createMockQuery()}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
setError={() => {}}
/>
);
const aggregation = await screen.findByLabelText('Aggregation');
expect(aggregation).toBeInTheDocument();
await selectOptionInTest(aggregation, 'Maximum');
expect(onChange).toHaveBeenLastCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
aggregation: 'Maximum',
},
});
});
});
}

View File

@ -1,228 +0,0 @@
import { css } from '@emotion/css';
import React from 'react';
import { PanelData } from '@grafana/data/src/types';
import { EditorRows, EditorRow, EditorFieldGroup } from '@grafana/experimental';
import { config } from '@grafana/runtime';
import { InlineFieldRow, useStyles2 } from '@grafana/ui';
import type Datasource from '../../datasource';
import type { AzureMonitorQuery, AzureMonitorOption, AzureMonitorErrorish } from '../../types';
import AggregationField from '../MetricsQueryEditor/AggregationField';
import DimensionFields from '../MetricsQueryEditor/DimensionFields';
import LegendFormatField from '../MetricsQueryEditor/LegendFormatField';
import MetricNameField from '../MetricsQueryEditor/MetricNameField';
import MetricNamespaceField from '../MetricsQueryEditor/MetricNamespaceField';
import NewDimensionFields from '../MetricsQueryEditor/NewDimensionFields';
import TimeGrainField from '../MetricsQueryEditor/TimeGrainField';
import TopField from '../MetricsQueryEditor/TopField';
import { setResource } from '../MetricsQueryEditor/setQueryValue';
import ResourceField from '../ResourceField';
import { ResourceRowType } from '../ResourcePicker/types';
import { useMetricNames, useMetricNamespaces, useMetricMetadata } from './dataHooks';
interface MetricsQueryEditorProps {
data: PanelData | undefined;
query: AzureMonitorQuery;
datasource: Datasource;
onChange: (newQuery: AzureMonitorQuery) => void;
variableOptionGroup: { label: string; options: AzureMonitorOption[] };
setError: (source: string, error: AzureMonitorErrorish | undefined) => void;
}
const MetricsQueryEditor: React.FC<MetricsQueryEditorProps> = ({
data,
query,
datasource,
variableOptionGroup,
onChange,
setError,
}) => {
const styles = useStyles2(getStyles);
const metricsMetadata = useMetricMetadata(query, datasource, onChange);
const metricNamespaces = useMetricNamespaces(query, datasource, onChange, setError);
const metricNames = useMetricNames(query, datasource, onChange, setError);
if (config.featureToggles.azureMonitorExperimentalUI) {
return (
<span data-testid="azure-monitor-metrics-query-editor-with-experimental-ui">
<EditorRows>
<EditorRow>
<EditorFieldGroup>
<ResourceField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
selectableEntryTypes={[ResourceRowType.Resource]}
setResource={setResource}
resourceUri={query.azureMonitor?.resourceUri}
/>
</EditorFieldGroup>
</EditorRow>
<EditorRow>
<EditorFieldGroup>
<MetricNamespaceField
metricNamespaces={metricNamespaces}
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
<MetricNameField
metricNames={metricNames}
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
</EditorFieldGroup>
</EditorRow>
<EditorRow>
<EditorFieldGroup>
<AggregationField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
aggregationOptions={metricsMetadata?.aggOptions ?? []}
isLoading={metricsMetadata.isLoading}
/>
<TimeGrainField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
timeGrainOptions={metricsMetadata?.timeGrains ?? []}
/>
</EditorFieldGroup>
</EditorRow>
<EditorRow>
<EditorFieldGroup>
<NewDimensionFields
data={data}
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
dimensionOptions={metricsMetadata?.dimensions ?? []}
/>
</EditorFieldGroup>
</EditorRow>
<EditorRow>
<EditorFieldGroup>
<TopField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
<LegendFormatField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
</EditorFieldGroup>
</EditorRow>
</EditorRows>
</span>
);
} else {
return (
<div data-testid="azure-monitor-metrics-query-editor-with-resource-picker">
<InlineFieldRow className={styles.row}>
<ResourceField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
selectableEntryTypes={[ResourceRowType.Resource]}
setResource={setResource}
resourceUri={query.azureMonitor?.resourceUri}
/>
</InlineFieldRow>
<InlineFieldRow className={styles.row}>
<MetricNamespaceField
metricNamespaces={metricNamespaces}
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
<MetricNameField
metricNames={metricNames}
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
</InlineFieldRow>
<InlineFieldRow className={styles.row}>
<AggregationField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
aggregationOptions={metricsMetadata?.aggOptions ?? []}
isLoading={metricsMetadata.isLoading}
/>
<TimeGrainField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
timeGrainOptions={metricsMetadata?.timeGrains ?? []}
/>
</InlineFieldRow>
<DimensionFields
data={data}
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
dimensionOptions={metricsMetadata?.dimensions ?? []}
/>
<TopField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
<LegendFormatField
query={query}
datasource={datasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={setError}
/>
</div>
);
}
};
const getStyles = () => ({
row: css({
rowGap: 0,
}),
});
export default MetricsQueryEditor;

View File

@ -1,235 +0,0 @@
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, MetricMetadata, MetricsMetadataHook } from '../MetricsQueryEditor/dataHooks';
import { useMetricNames, useMetricNamespaces, useMetricMetadata } from './dataHooks';
const WAIT_OPTIONS = {
timeout: 1000,
};
const opt = (text: string, value: string) => ({ text, value });
interface TestScenario {
name: string;
hook: DataHook | MetricsMetadataHook;
// For convenience, only need to define the azureMonitor part of the query for some tests
emptyQueryPartial: AzureMetricQuery;
customProperties: AzureMetricQuery;
topLevelCustomProperties?: Partial<AzureMonitorQuery>;
expectedCustomPropertyResults?: Array<AzureMonitorOption<string>>;
expectedOptions: AzureMonitorOption[] | MetricMetadata;
}
describe('AzureMonitor: metrics dataHooks', () => {
const bareQuery = {
refId: 'A',
queryType: AzureQueryType.AzureMonitor,
subscription: 'sub-abc-123',
};
const testTable: TestScenario[] = [
{
name: 'useMetricNames',
hook: useMetricNames,
emptyQueryPartial: {
resourceUri:
'/subscriptions/99999999-cccc-bbbb-aaaa-9106972f9572/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana',
metricNamespace: 'azure/vm',
},
customProperties: {
resourceUri:
'/subscriptions/99999999-cccc-bbbb-aaaa-9106972f9572/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana',
metricNamespace: 'azure/vm',
metricName: 'metric-$ENVIRONMENT',
},
expectedOptions: [
{
label: 'Percentage CPU',
value: 'percentage-cpu',
},
{
label: 'Free memory',
value: 'free-memory',
},
],
expectedCustomPropertyResults: [
{ label: 'Percentage CPU', value: 'percentage-cpu' },
{ label: 'Free memory', value: 'free-memory' },
{ label: 'metric-$ENVIRONMENT', value: 'metric-$ENVIRONMENT' },
],
},
{
name: 'useMetricNamespaces',
hook: useMetricNamespaces,
emptyQueryPartial: {
resourceUri:
'/subscriptions/99999999-cccc-bbbb-aaaa-9106972f9572/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana',
metricNamespace: 'azure/vm',
},
customProperties: {
resourceUri:
'/subscriptions/99999999-cccc-bbbb-aaaa-9106972f9572/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana',
metricNamespace: 'azure/vm-$ENVIRONMENT',
metricName: 'metric-name',
},
expectedOptions: [
{
label: 'Compute Virtual Machine',
value: 'azure/vmc',
},
{
label: 'Database NS',
value: 'azure/dbns',
},
{
label: 'azure/vm',
value: 'azure/vm',
},
],
expectedCustomPropertyResults: [
{ label: 'Compute Virtual Machine', value: 'azure/vmc' },
{ label: 'Database NS', value: 'azure/dbns' },
{ label: 'azure/vm-$ENVIRONMENT', value: 'azure/vm-$ENVIRONMENT' },
],
},
];
let datasource: Datasource;
let onChange: jest.Mock<any, any>;
let setError: jest.Mock<any, any>;
beforeEach(() => {
onChange = jest.fn();
setError = jest.fn();
datasource = createMockDatasource();
datasource.getVariables = jest.fn().mockReturnValue(['$sub', '$rg', '$rt', '$variable']);
datasource.azureMonitorDatasource.getSubscriptions = jest
.fn()
.mockResolvedValue([opt('sub-abc-123', 'sub-abc-123')]);
datasource.getResourceGroups = jest
.fn()
.mockResolvedValue([
opt('Web App - Production', 'web-app-production'),
opt('Web App - Development', 'web-app-development'),
]);
datasource.getMetricDefinitions = jest
.fn()
.mockResolvedValue([opt('Virtual Machine', 'azure/vm'), opt('Database', 'azure/db')]);
datasource.getResourceNames = jest
.fn()
.mockResolvedValue([opt('Web server', 'web-server'), opt('Job server', 'job-server')]);
datasource.azureMonitorDatasource.getMetricNames = jest
.fn()
.mockResolvedValue([opt('Percentage CPU', 'percentage-cpu'), opt('Free memory', 'free-memory')]);
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 = {
...bareQuery,
azureMonitor: scenario.emptyQueryPartial,
};
const { result, waitForNextUpdate } = renderHook(() => scenario.hook(query, datasource, onChange, setError));
await waitForNextUpdate(WAIT_OPTIONS);
expect(result.current).toEqual(scenario.expectedOptions);
});
it('adds custom properties as a valid option', async () => {
const query = {
...bareQuery,
azureMonitor: scenario.customProperties,
...scenario.topLevelCustomProperties,
};
const { result, waitForNextUpdate } = renderHook(() => scenario.hook(query, datasource, onChange, setError));
await waitForNextUpdate(WAIT_OPTIONS);
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,152 +0,0 @@
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';
import { DataHook } from '../MetricsQueryEditor/dataHooks';
import { setMetricNamespace } from '../MetricsQueryEditor/setQueryValue';
export interface MetricMetadata {
aggOptions: AzureMonitorOption[];
timeGrains: AzureMonitorOption[];
dimensions: AzureMonitorOption[];
isLoading: boolean;
// These two properties are only used within the hook, and not elsewhere
supportedAggTypes: string[];
primaryAggType: string | undefined;
}
type OnChangeFn = (newQuery: AzureMonitorQuery) => void;
export const useMetricNamespaces: DataHook = (query, datasource, onChange, setError) => {
const { metricNamespace, resourceUri } = query.azureMonitor ?? {};
const metricNamespaces = useAsyncState(
async () => {
if (!resourceUri) {
return;
}
const results = await datasource.azureMonitorDatasource.getMetricNamespaces({ resourceUri });
const options = formatOptions(results, metricNamespace);
// Do some cleanup of the query state if need be
if (!metricNamespace && options.length) {
onChange(setMetricNamespace(query, options[0].value));
}
return options;
},
setError,
[resourceUri]
);
return metricNamespaces;
};
export const useMetricNames: DataHook = (query, datasource, onChange, setError) => {
const { metricNamespace, metricName, resourceUri } = query.azureMonitor ?? {};
return useAsyncState(
async () => {
if (!(metricNamespace && resourceUri)) {
return;
}
const results = await datasource.azureMonitorDatasource.getMetricNames({ resourceUri, metricNamespace });
const options = formatOptions(results, metricName);
return options;
},
setError,
[resourceUri, metricNamespace]
);
};
const defaultMetricMetadata: MetricMetadata = {
aggOptions: [],
timeGrains: [],
dimensions: [],
isLoading: false,
supportedAggTypes: [],
primaryAggType: undefined,
};
export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasource, onChange: OnChangeFn) => {
const [metricMetadata, setMetricMetadata] = useState<MetricMetadata>(defaultMetricMetadata);
const { resourceUri, metricNamespace, metricName, aggregation, timeGrain } = query.azureMonitor ?? {};
// Fetch new metric metadata when the fields change
useEffect(() => {
if (!(resourceUri && metricNamespace && metricName)) {
setMetricMetadata(defaultMetricMetadata);
return;
}
datasource.azureMonitorDatasource
.getMetricMetadata({ resourceUri, metricNamespace, metricName })
.then((metadata) => {
// TODO: Move the aggregationTypes and timeGrain defaults into `getMetricMetadata`
const aggregations = (metadata.supportedAggTypes || [metadata.primaryAggType]).map((v) => ({
label: v,
value: v,
}));
setMetricMetadata({
aggOptions: aggregations,
timeGrains: metadata.supportedTimeGrains,
dimensions: metadata.dimensions,
isLoading: false,
supportedAggTypes: metadata.supportedAggTypes ?? [],
primaryAggType: metadata.primaryAggType,
});
});
}, [datasource, resourceUri, metricNamespace, metricName]);
// Update the query state in response to the meta data changing
useEffect(() => {
const newAggregation = aggregation || metricMetadata.primaryAggType;
const newTimeGrain = timeGrain || 'auto';
if (newAggregation !== aggregation || newTimeGrain !== timeGrain) {
onChange({
...query,
azureMonitor: {
...query.azureMonitor,
aggregation: newAggregation,
timeGrain: newTimeGrain,
allowedTimeGrainsMs: metricMetadata.timeGrains
.filter((timeGrain) => timeGrain.value !== 'auto')
.map((timeGrain) =>
rangeUtil.intervalToMs(TimegrainConverter.createKbnUnitFromISO8601Duration(timeGrain.value))
),
},
});
}
}, [onChange, metricMetadata, aggregation, timeGrain, query]);
return metricMetadata;
};
function formatOptions(
rawResults: Array<{
text: string;
value: string;
}>,
selectedValue?: string
) {
const options = rawResults.map(toOption);
// account for custom values that might have been set in json file like ones crafted with a template variable (ex: "cloud-datasource-resource-$Environment")
if (selectedValue && !options.find((option) => option.value === selectedValue.toLowerCase())) {
options.push({ label: selectedValue, value: selectedValue });
}
return options;
}

View File

@ -16,7 +16,7 @@ import {
import useLastError from '../../utils/useLastError';
import ArgQueryEditor from '../ArgQueryEditor';
import LogsQueryEditor from '../LogsQueryEditor';
import NewMetricsQueryEditor from '../NewMetricsQueryEditor/MetricsQueryEditor';
import NewMetricsQueryEditor from '../MetricsQueryEditor/MetricsQueryEditor';
import { QueryHeader } from '../QueryHeader';
import { Space } from '../Space';
@ -157,8 +157,6 @@ const EditorForQueryType: React.FC<EditorForQueryTypeProps> = ({
</Alert>
);
}
return null;
};
export default QueryEditor;