AzureMonitor: Update ARG Subscription selection (#66890)

* Update ARG Subscription selection

- Filter out queries that do not contain at least one subscription
- Refactor SubscriptionField component
- Display error if no subscriptions are selected
- Update tests

* Fix test
This commit is contained in:
Andreas Christou 2023-04-21 10:59:40 +01:00 committed by GitHub
parent 3c0e896282
commit f3dbb7b34a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 134 additions and 99 deletions

View File

@ -12,7 +12,7 @@ export default class AzureResourceGraphDatasource extends DataSourceWithBackend<
AzureDataSourceJsonData
> {
filterQuery(item: AzureMonitorQuery): boolean {
return !!item.azureResourceGraph?.query;
return !!item.azureResourceGraph?.query && !!item.subscriptions && item.subscriptions.length > 0;
}
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): AzureMonitorQuery {

View File

@ -1,4 +1,5 @@
import { render, screen } from '@testing-library/react';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import createMockDatasource from '../../__mocks__/datasource';
@ -118,4 +119,67 @@ describe('ArgQueryEditor', () => {
).toHaveTextContent('$test');
expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ subscriptions: ['$test'] }));
});
it('should display an error if no subscription is selected', async () => {
const onChange = jest.fn();
const datasource = createMockDatasource({
getSubscriptions: jest.fn().mockResolvedValue([]),
});
const query = createMockQuery({
subscriptions: [],
});
render(
<ArgQueryEditor
{...defaultProps}
datasource={datasource}
onChange={onChange}
query={query}
variableOptionGroup={{ label: 'Template Variables', options: [] }}
/>
);
expect(await waitFor(() => screen.getByText('At least one subscription must be chosen.'))).toBeInTheDocument();
});
it('should display an error if subscriptions are cleared', async () => {
const onChange = jest.fn();
const datasource = createMockDatasource({
getSubscriptions: jest.fn().mockResolvedValue([{ text: 'foo', value: 'test-subscription-value' }]),
});
const query = createMockQuery({
subscription: undefined,
subscriptions: ['test-subscription-value'],
});
const { rerender } = render(
<ArgQueryEditor
{...defaultProps}
query={query}
datasource={datasource}
onChange={onChange}
variableOptionGroup={{ label: 'Template Variables', options: [] }}
/>
);
expect(datasource.getSubscriptions).toHaveBeenCalled();
expect(await waitFor(() => onChange)).toHaveBeenCalledWith(
expect.objectContaining({ subscriptions: ['test-subscription-value'] })
);
expect(await waitFor(() => screen.findByText('foo'))).toBeInTheDocument();
const clear = screen.getByLabelText('select-clear-value');
await userEvent.click(clear);
expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ subscriptions: [] }));
rerender(
<ArgQueryEditor
{...defaultProps}
datasource={datasource}
onChange={onChange}
query={{ ...query, subscriptions: [] }}
variableOptionGroup={{ label: 'Template Variables', options: [] }}
/>
);
expect(await waitFor(() => screen.getByText('At least one subscription must be chosen.'))).toBeInTheDocument();
});
});

View File

@ -6,9 +6,9 @@ import { EditorFieldGroup, EditorRow, EditorRows } from '@grafana/experimental';
import Datasource from '../../datasource';
import { selectors } from '../../e2e/selectors';
import { AzureMonitorErrorish, AzureMonitorOption, AzureMonitorQuery } from '../../types';
import SubscriptionField from '../SubscriptionField';
import QueryField from './QueryField';
import SubscriptionField from './SubscriptionField';
interface ArgQueryEditorProps {
query: AzureMonitorQuery;
@ -81,7 +81,6 @@ const ArgQueryEditor = ({
<EditorRow>
<EditorFieldGroup>
<SubscriptionField
multiSelect
subscriptions={subscriptions}
query={query}
datasource={datasource}

View File

@ -0,0 +1,66 @@
import React, { useEffect, useMemo, useState } from 'react';
import { SelectableValue } from '@grafana/data';
import { FieldValidationMessage, MultiSelect } from '@grafana/ui';
import { selectors } from '../../e2e/selectors';
import { AzureMonitorQuery, AzureQueryEditorFieldProps, AzureMonitorOption } from '../../types';
import { findOptions } from '../../utils/common';
import { Field } from '../Field';
interface SubscriptionFieldProps extends AzureQueryEditorFieldProps {
onQueryChange: (newQuery: AzureMonitorQuery) => void;
subscriptions: AzureMonitorOption[];
}
const SubscriptionField = ({ query, subscriptions, variableOptionGroup, onQueryChange }: SubscriptionFieldProps) => {
const [error, setError] = useState<boolean>(false);
const [values, setValues] = useState<Array<SelectableValue<string>>>([]);
const options = useMemo(() => [...subscriptions, variableOptionGroup], [subscriptions, variableOptionGroup]);
useEffect(() => {
if (query.subscriptions && query.subscriptions.length > 0) {
setValues(findOptions([...subscriptions, ...variableOptionGroup.options], query.subscriptions));
setError(false);
} else {
setError(true);
}
}, [query.subscriptions, subscriptions, variableOptionGroup.options]);
const onChange = (change: Array<SelectableValue<string>>) => {
if (!change || change.length === 0) {
setValues([]);
onQueryChange({
...query,
subscriptions: [],
});
setError(true);
} else {
const newSubs = change.map((c) => c.value ?? '');
onQueryChange({
...query,
subscriptions: newSubs,
});
setValues(findOptions([...subscriptions, ...variableOptionGroup.options], newSubs));
setError(false);
}
};
return (
<Field label="Subscriptions" data-testid={selectors.components.queryEditor.argsQueryEditor.subscriptions.input}>
<>
<MultiSelect
isClearable
value={values}
inputId="azure-monitor-subscriptions-field"
onChange={onChange}
options={options}
width={38}
/>
{error ? <FieldValidationMessage>At least one subscription must be chosen.</FieldValidationMessage> : null}
</>
</Field>
);
};
export default SubscriptionField;

View File

@ -1,94 +0,0 @@
import React, { useCallback, useMemo } from 'react';
import { SelectableValue } from '@grafana/data';
import { Select, MultiSelect } from '@grafana/ui';
import { selectors } from '../e2e/selectors';
import { AzureMonitorQuery, AzureQueryEditorFieldProps, AzureMonitorOption, AzureQueryType } from '../types';
import { findOptions } from '../utils/common';
import { Field } from './Field';
interface SubscriptionFieldProps extends AzureQueryEditorFieldProps {
onQueryChange: (newQuery: AzureMonitorQuery) => void;
subscriptions: AzureMonitorOption[];
multiSelect?: boolean;
}
const SubscriptionField = ({
query,
subscriptions,
variableOptionGroup,
onQueryChange,
multiSelect = false,
}: SubscriptionFieldProps) => {
const handleChange = useCallback(
(change: SelectableValue<string>) => {
if (!change.value) {
return;
}
let newQuery: AzureMonitorQuery = {
...query,
subscription: change.value,
};
if (query.queryType === AzureQueryType.AzureMonitor) {
newQuery.azureMonitor = {
...newQuery.azureMonitor,
resources: undefined,
metricNamespace: undefined,
metricName: undefined,
aggregation: undefined,
timeGrain: '',
dimensionFilters: [],
};
}
onQueryChange(newQuery);
},
[query, onQueryChange]
);
const onSubscriptionsChange = useCallback(
(change: Array<SelectableValue<string>>) => {
if (!change) {
return;
}
onQueryChange({
...query,
subscriptions: change.map((c) => c.value ?? ''),
});
},
[query, onQueryChange]
);
const options = useMemo(() => [...subscriptions, variableOptionGroup], [subscriptions, variableOptionGroup]);
return multiSelect ? (
<Field label="Subscriptions" data-testid={selectors.components.queryEditor.argsQueryEditor.subscriptions.input}>
<MultiSelect
isClearable
value={findOptions([...subscriptions, ...variableOptionGroup.options], query.subscriptions)}
inputId="azure-monitor-subscriptions-field"
onChange={onSubscriptionsChange}
options={options}
width={38}
/>
</Field>
) : (
<Field label="Subscription" data-testid={selectors.components.queryEditor.argsQueryEditor.subscriptions.input}>
<Select
value={query.subscription}
inputId="azure-monitor-subscriptions-field"
onChange={handleChange}
options={options}
width={38}
allowCustomValue
/>
</Field>
);
};
export default SubscriptionField;

View File

@ -100,7 +100,7 @@ describe('VariableEditor:', () => {
it('should render', async () => {
render(<VariableEditor {...ARGqueryProps} />);
await waitFor(() => screen.queryByTestId('mockeditor'));
expect(screen.queryByLabelText('Subscriptions')).toBeInTheDocument();
await waitFor(() => screen.queryByLabelText('Subscriptions'));
expect(screen.queryByText('Resource Graph')).toBeInTheDocument();
expect(screen.queryByLabelText('Select subscription')).not.toBeInTheDocument();
expect(screen.queryByLabelText('Select query type')).not.toBeInTheDocument();