CloudWatch: Fetch Dimension keys correctly from Dimension Picker (#78556)

pull/78150/head^2
Isabella Siu 2 years ago committed by GitHub
parent faa29db241
commit 931c8e99b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.test.tsx
  2. 5
      public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.tsx
  3. 25
      public/app/plugins/datasource/cloudwatch/components/shared/Dimensions/Dimensions.test.tsx
  4. 14
      public/app/plugins/datasource/cloudwatch/components/shared/Dimensions/Dimensions.tsx
  5. 53
      public/app/plugins/datasource/cloudwatch/components/shared/Dimensions/FilterItem.test.tsx
  6. 17
      public/app/plugins/datasource/cloudwatch/components/shared/Dimensions/FilterItem.tsx
  7. 4
      public/app/plugins/datasource/cloudwatch/components/shared/MetricStatEditor/MetricStatEditor.tsx

@ -149,7 +149,7 @@ describe('VariableEditor', () => {
// change filter key // change filter key
const keySelect = screen.getByRole('combobox', { name: 'Dimensions filter key' }); const keySelect = screen.getByRole('combobox', { name: 'Dimensions filter key' });
// confirms getDimensionKeys was called with filter and that the element uses keysForDimensionFilter // confirms getDimensionKeys was called with filter and that the element uses keysForDimensionFilter
await select(keySelect, 'v4', { select(keySelect, 'v4', {
container: document.body, container: document.body,
}); });
expect(ds.datasource.resources.getDimensionKeys).toHaveBeenCalledWith({ expect(ds.datasource.resources.getDimensionKeys).toHaveBeenCalledWith({
@ -158,14 +158,16 @@ describe('VariableEditor', () => {
metricName: 'i3', metricName: 'i3',
dimensionFilters: undefined, dimensionFilters: undefined,
}); });
expect(onChange).toHaveBeenCalledWith({ await waitFor(() => {
...defaultQuery, expect(onChange).toHaveBeenCalledWith({
queryType: VariableQueryType.DimensionValues, ...defaultQuery,
namespace: 'z2', queryType: VariableQueryType.DimensionValues,
region: 'a1', namespace: 'z2',
metricName: 'i3', region: 'a1',
dimensionKey: 's4', metricName: 'i3',
dimensionFilters: { v4: undefined }, dimensionKey: 's4',
dimensionFilters: { v4: undefined },
});
}); });
// set filter value // set filter value

@ -37,12 +37,11 @@ const queryTypes: Array<{ value: string; label: string }> = [
export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => { export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => {
const parsedQuery = migrateVariableQuery(query); const parsedQuery = migrateVariableQuery(query);
const { region, namespace, metricName, dimensionKey, dimensionFilters } = parsedQuery; const { region, namespace, metricName, dimensionKey } = parsedQuery;
const [regions, regionIsLoading] = useRegions(datasource); const [regions, regionIsLoading] = useRegions(datasource);
const namespaces = useNamespaces(datasource); const namespaces = useNamespaces(datasource);
const metrics = useMetrics(datasource, { region, namespace }); const metrics = useMetrics(datasource, { region, namespace });
const dimensionKeys = useDimensionKeys(datasource, { region, namespace, metricName }); const dimensionKeys = useDimensionKeys(datasource, { region, namespace, metricName });
const keysForDimensionFilter = useDimensionKeys(datasource, { region, namespace, metricName, dimensionFilters });
const accountState = useAccountOptions(datasource.resources, query.region); const accountState = useAccountOptions(datasource.resources, query.region);
const newFormStylingEnabled = config.featureToggles.awsDatasourcesNewFormStyling; const newFormStylingEnabled = config.featureToggles.awsDatasourcesNewFormStyling;
@ -188,7 +187,6 @@ export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => {
onChange={(dimensions) => { onChange={(dimensions) => {
onChange({ ...parsedQuery, dimensionFilters: dimensions }); onChange({ ...parsedQuery, dimensionFilters: dimensions });
}} }}
dimensionKeys={keysForDimensionFilter}
disableExpressions={true} disableExpressions={true}
datasource={datasource} datasource={datasource}
/> />
@ -205,7 +203,6 @@ export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => {
onChange={(dimensions) => { onChange={(dimensions) => {
onChange({ ...parsedQuery, dimensionFilters: dimensions }); onChange({ ...parsedQuery, dimensionFilters: dimensions });
}} }}
dimensionKeys={keysForDimensionFilter}
disableExpressions={true} disableExpressions={true}
datasource={datasource} datasource={datasource}
/> />

@ -43,8 +43,8 @@ describe('Dimensions', () => {
InstanceId: '*', InstanceId: '*',
InstanceGroup: 'Group1', InstanceGroup: 'Group1',
}; };
render(<Dimensions {...props} metricStat={props.query} dimensionKeys={[]} />); render(<Dimensions {...props} metricStat={props.query} />);
const filterItems = screen.getAllByTestId('cloudwatch-dimensions-filter-item'); const filterItems = await screen.findAllByTestId('cloudwatch-dimensions-filter-item');
expect(filterItems.length).toBe(2); expect(filterItems.length).toBe(2);
expect(within(filterItems[0]).getByText('InstanceId')).toBeInTheDocument(); expect(within(filterItems[0]).getByText('InstanceId')).toBeInTheDocument();
@ -61,8 +61,8 @@ describe('Dimensions', () => {
InstanceId: ['*'], InstanceId: ['*'],
InstanceGroup: ['Group1'], InstanceGroup: ['Group1'],
}; };
render(<Dimensions {...props} metricStat={props.query} dimensionKeys={[]} />); render(<Dimensions {...props} metricStat={props.query} />);
const filterItems = screen.getAllByTestId('cloudwatch-dimensions-filter-item'); const filterItems = await screen.findAllByTestId('cloudwatch-dimensions-filter-item');
expect(filterItems.length).toBe(2); expect(filterItems.length).toBe(2);
expect(within(filterItems[0]).getByText('InstanceId')).toBeInTheDocument(); expect(within(filterItems[0]).getByText('InstanceId')).toBeInTheDocument();
@ -77,7 +77,7 @@ describe('Dimensions', () => {
it('it should add the new item but not call onChange', async () => { it('it should add the new item but not call onChange', async () => {
props.query.dimensions = {}; props.query.dimensions = {};
const onChange = jest.fn(); const onChange = jest.fn();
render(<Dimensions {...props} metricStat={props.query} onChange={onChange} dimensionKeys={[]} />); render(<Dimensions {...props} metricStat={props.query} onChange={onChange} />);
await userEvent.click(screen.getByLabelText('Add')); await userEvent.click(screen.getByLabelText('Add'));
expect(screen.getByTestId('cloudwatch-dimensions-filter-item')).toBeInTheDocument(); expect(screen.getByTestId('cloudwatch-dimensions-filter-item')).toBeInTheDocument();
@ -89,9 +89,7 @@ describe('Dimensions', () => {
it('it should add the new item but not call onChange', async () => { it('it should add the new item but not call onChange', async () => {
props.query.dimensions = {}; props.query.dimensions = {};
const onChange = jest.fn(); const onChange = jest.fn();
const { container } = render( const { container } = render(<Dimensions {...props} metricStat={props.query} onChange={onChange} />);
<Dimensions {...props} metricStat={props.query} onChange={onChange} dimensionKeys={[]} />
);
await userEvent.click(screen.getByLabelText('Add')); await userEvent.click(screen.getByLabelText('Add'));
const filterItemElement = screen.getByTestId('cloudwatch-dimensions-filter-item'); const filterItemElement = screen.getByTestId('cloudwatch-dimensions-filter-item');
@ -109,9 +107,7 @@ describe('Dimensions', () => {
it('it should add the new item and trigger onChange', async () => { it('it should add the new item and trigger onChange', async () => {
props.query.dimensions = {}; props.query.dimensions = {};
const onChange = jest.fn(); const onChange = jest.fn();
const { container } = render( const { container } = render(<Dimensions {...props} metricStat={props.query} onChange={onChange} />);
<Dimensions {...props} metricStat={props.query} onChange={onChange} dimensionKeys={[]} />
);
const label = await screen.findByLabelText('Add'); const label = await screen.findByLabelText('Add');
await userEvent.click(label); await userEvent.click(label);
@ -128,11 +124,8 @@ describe('Dimensions', () => {
expect(valueElement).toBeInTheDocument(); expect(valueElement).toBeInTheDocument();
await userEvent.type(valueElement!, 'my-value'); await userEvent.type(valueElement!, 'my-value');
fireEvent.keyDown(valueElement!, { keyCode: 13 }); fireEvent.keyDown(valueElement!, { keyCode: 13 });
expect(onChange).not.toHaveBeenCalledWith({ expect(onChange).toHaveBeenCalledWith({
...props.query, 'my-key': 'my-value',
dimensions: {
'my-key': 'my-value',
},
}); });
}); });
}); });

@ -1,7 +1,6 @@
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import React, { useMemo, useState } from 'react'; import React, { useMemo, useState } from 'react';
import { SelectableValue } from '@grafana/data';
import { EditorList } from '@grafana/experimental'; import { EditorList } from '@grafana/experimental';
import { CloudWatchDatasource } from '../../../datasource'; import { CloudWatchDatasource } from '../../../datasource';
@ -13,7 +12,6 @@ export interface Props {
metricStat: MetricStat; metricStat: MetricStat;
onChange: (dimensions: DimensionsType) => void; onChange: (dimensions: DimensionsType) => void;
datasource: CloudWatchDatasource; datasource: CloudWatchDatasource;
dimensionKeys: Array<SelectableValue<string>>;
disableExpressions: boolean; disableExpressions: boolean;
} }
@ -62,7 +60,7 @@ const filterConditionsToDimensions = (filters: DimensionFilterCondition[]) => {
}, {}); }, {});
}; };
export const Dimensions = ({ metricStat, datasource, dimensionKeys, disableExpressions, onChange }: Props) => { export const Dimensions = ({ metricStat, datasource, disableExpressions, onChange }: Props) => {
const dimensionFilters = useMemo(() => dimensionsToFilterConditions(metricStat.dimensions), [metricStat.dimensions]); const dimensionFilters = useMemo(() => dimensionsToFilterConditions(metricStat.dimensions), [metricStat.dimensions]);
const [items, setItems] = useState<DimensionFilterCondition[]>(dimensionFilters); const [items, setItems] = useState<DimensionFilterCondition[]>(dimensionFilters);
const onDimensionsChange = (newItems: Array<Partial<DimensionFilterCondition>>) => { const onDimensionsChange = (newItems: Array<Partial<DimensionFilterCondition>>) => {
@ -80,17 +78,12 @@ export const Dimensions = ({ metricStat, datasource, dimensionKeys, disableExpre
<EditorList <EditorList
items={items} items={items}
onChange={onDimensionsChange} onChange={onDimensionsChange}
renderItem={makeRenderFilter(datasource, metricStat, dimensionKeys, disableExpressions)} renderItem={makeRenderFilter(datasource, metricStat, disableExpressions)}
/> />
); );
}; };
function makeRenderFilter( function makeRenderFilter(datasource: CloudWatchDatasource, metricStat: MetricStat, disableExpressions: boolean) {
datasource: CloudWatchDatasource,
metricStat: MetricStat,
dimensionKeys: Array<SelectableValue<string>>,
disableExpressions: boolean
) {
function renderFilter( function renderFilter(
item: DimensionFilterCondition, item: DimensionFilterCondition,
onChange: (item: DimensionFilterCondition) => void, onChange: (item: DimensionFilterCondition) => void,
@ -103,7 +96,6 @@ function makeRenderFilter(
datasource={datasource} datasource={datasource}
metricStat={metricStat} metricStat={metricStat}
disableExpressions={disableExpressions} disableExpressions={disableExpressions}
dimensionKeys={dimensionKeys}
onDelete={onDelete} onDelete={onDelete}
/> />
); );

@ -0,0 +1,53 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { setupMockedDataSource } from '../../../__mocks__/CloudWatchDataSource';
import { CloudWatchMetricsQuery } from '../../../types';
import { FilterItem } from './FilterItem';
const ds = setupMockedDataSource({
variables: [],
});
const q: CloudWatchMetricsQuery = {
id: '',
region: 'us-east-2',
namespace: '',
period: '',
alias: '',
metricName: '',
dimensions: { foo: 'bar', abc: 'xyz' },
matchExact: true,
statistic: '',
expression: '',
refId: '',
};
describe('Dimensions', () => {
it('should call getDimensionKeys without the current key', async () => {
const getDimensionKeys = jest.fn().mockResolvedValue([]);
ds.datasource.resources.getDimensionKeys = getDimensionKeys;
const currFilter = { key: 'foo', value: 'bar' };
render(
<FilterItem
metricStat={q}
datasource={ds.datasource}
filter={currFilter}
disableExpressions={true}
onChange={jest.fn()}
onDelete={jest.fn()}
/>
);
await userEvent.click(screen.getByLabelText('Dimensions filter key'));
expect(getDimensionKeys).toHaveBeenCalledWith({
namespace: q.namespace,
region: q.region,
metricName: q.metricName,
accountId: q.accountId,
dimensionFilters: { abc: ['xyz'] },
});
});
});

@ -7,6 +7,7 @@ import { AccessoryButton, InputGroup } from '@grafana/experimental';
import { Select, useStyles2 } from '@grafana/ui'; import { Select, useStyles2 } from '@grafana/ui';
import { CloudWatchDatasource } from '../../../datasource'; import { CloudWatchDatasource } from '../../../datasource';
import { useDimensionKeys } from '../../../hooks';
import { Dimensions, MetricStat } from '../../../types'; import { Dimensions, MetricStat } from '../../../types';
import { appendTemplateVariables } from '../../../utils/utils'; import { appendTemplateVariables } from '../../../utils/utils';
@ -16,7 +17,6 @@ export interface Props {
metricStat: MetricStat; metricStat: MetricStat;
datasource: CloudWatchDatasource; datasource: CloudWatchDatasource;
filter: DimensionFilterCondition; filter: DimensionFilterCondition;
dimensionKeys: Array<SelectableValue<string>>;
disableExpressions: boolean; disableExpressions: boolean;
onChange: (value: DimensionFilterCondition) => void; onChange: (value: DimensionFilterCondition) => void;
onDelete: () => void; onDelete: () => void;
@ -32,19 +32,16 @@ const excludeCurrentKey = (dimensions: Dimensions, currentKey: string | undefine
return acc; return acc;
}, {}); }, {});
export const FilterItem = ({ export const FilterItem = ({ filter, metricStat, datasource, disableExpressions, onChange, onDelete }: Props) => {
filter, const { region, namespace, metricName, dimensions, accountId } = metricStat;
metricStat: { region, namespace, metricName, dimensions, accountId },
datasource,
dimensionKeys,
disableExpressions,
onChange,
onDelete,
}: Props) => {
const dimensionsExcludingCurrentKey = useMemo( const dimensionsExcludingCurrentKey = useMemo(
() => excludeCurrentKey(dimensions ?? {}, filter.key), () => excludeCurrentKey(dimensions ?? {}, filter.key),
[dimensions, filter] [dimensions, filter]
); );
const dimensionKeys = useDimensionKeys(datasource, {
...metricStat,
dimensionFilters: dimensionsExcludingCurrentKey,
});
const loadDimensionValues = async () => { const loadDimensionValues = async () => {
if (!filter.key) { if (!filter.key) {

@ -6,7 +6,7 @@ import { config } from '@grafana/runtime';
import { Select } from '@grafana/ui'; import { Select } from '@grafana/ui';
import { CloudWatchDatasource } from '../../../datasource'; import { CloudWatchDatasource } from '../../../datasource';
import { useAccountOptions, useDimensionKeys, useMetrics, useNamespaces } from '../../../hooks'; import { useAccountOptions, useMetrics, useNamespaces } from '../../../hooks';
import { standardStatistics } from '../../../standardStatistics'; import { standardStatistics } from '../../../standardStatistics';
import { MetricStat } from '../../../types'; import { MetricStat } from '../../../types';
import { appendTemplateVariables, toOption } from '../../../utils/utils'; import { appendTemplateVariables, toOption } from '../../../utils/utils';
@ -35,7 +35,6 @@ export const MetricStatEditor = ({
}: React.PropsWithChildren<Props>) => { }: React.PropsWithChildren<Props>) => {
const namespaces = useNamespaces(datasource); const namespaces = useNamespaces(datasource);
const metrics = useMetrics(datasource, metricStat); const metrics = useMetrics(datasource, metricStat);
const dimensionKeys = useDimensionKeys(datasource, { ...metricStat, dimensionFilters: metricStat.dimensions });
const accountState = useAccountOptions(datasource.resources, metricStat.region); const accountState = useAccountOptions(datasource.resources, metricStat.region);
useEffect(() => { useEffect(() => {
@ -139,7 +138,6 @@ export const MetricStatEditor = ({
<Dimensions <Dimensions
metricStat={metricStat} metricStat={metricStat}
onChange={(dimensions) => onChange({ ...metricStat, dimensions })} onChange={(dimensions) => onChange({ ...metricStat, dimensions })}
dimensionKeys={dimensionKeys}
disableExpressions={disableExpressions} disableExpressions={disableExpressions}
datasource={datasource} datasource={datasource}
/> />

Loading…
Cancel
Save