mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CloudWatch: Fetch Dimension keys correctly from Dimension Picker (#78556)
This commit is contained in:
parent
faa29db241
commit
931c8e99b9
@ -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,6 +158,7 @@ describe('VariableEditor', () => {
|
|||||||
metricName: 'i3',
|
metricName: 'i3',
|
||||||
dimensionFilters: undefined,
|
dimensionFilters: undefined,
|
||||||
});
|
});
|
||||||
|
await waitFor(() => {
|
||||||
expect(onChange).toHaveBeenCalledWith({
|
expect(onChange).toHaveBeenCalledWith({
|
||||||
...defaultQuery,
|
...defaultQuery,
|
||||||
queryType: VariableQueryType.DimensionValues,
|
queryType: VariableQueryType.DimensionValues,
|
||||||
@ -167,6 +168,7 @@ describe('VariableEditor', () => {
|
|||||||
dimensionKey: 's4',
|
dimensionKey: 's4',
|
||||||
dimensionFilters: { v4: undefined },
|
dimensionFilters: { v4: undefined },
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// set filter value
|
// set filter value
|
||||||
const valueSelect = screen.getByRole('combobox', { name: 'Dimensions filter value' });
|
const valueSelect = screen.getByRole('combobox', { name: 'Dimensions 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,
|
|
||||||
dimensions: {
|
|
||||||
'my-key': 'my-value',
|
'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…
Reference in New Issue
Block a user