Cloudwatch: Add account Id in groupBy options for Metric Insights cross-account (#90906)

This commit is contained in:
Ida Štambuk 2024-07-26 14:20:39 +02:00 committed by GitHub
parent e4ed000d05
commit 67f0d04744
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 66 additions and 5 deletions

View File

@ -2,6 +2,8 @@ import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import selectEvent from 'react-select-event';
import { config } from '@grafana/runtime';
import { setupMockedDataSource } from '../../../../__mocks__/CloudWatchDataSource';
import { createArray, createGroupBy } from '../../../../__mocks__/sqlUtils';
import { CloudWatchMetricsQuery, MetricEditorMode, MetricQueryType, SQLExpression } from '../../../../types';
@ -25,6 +27,10 @@ const makeSQLQuery = (sql?: SQLExpression): CloudWatchMetricsQuery => ({
datasource.resources.getDimensionKeys = jest.fn().mockResolvedValue([]);
describe('Cloudwatch SQLGroupBy', () => {
afterEach(() => {
baseProps.datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(false);
});
const baseProps = {
query: makeSQLQuery(),
datasource,
@ -55,6 +61,44 @@ describe('Cloudwatch SQLGroupBy', () => {
});
});
it('should show Account ID in groupBy options if feature flag is enabled', async () => {
config.featureToggles.cloudWatchCrossAccountQuerying = true;
config.featureToggles.cloudwatchMetricInsightsCrossAccount = true;
baseProps.datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(true);
const query = makeSQLQuery();
render(<SQLGroupBy {...baseProps} query={query} />);
const addButton = screen.getByRole('button', { name: 'Add' });
await userEvent.click(addButton);
selectEvent.openMenu(screen.getByLabelText(/Group by/));
expect(screen.getByText('Account ID')).toBeInTheDocument();
});
it('should not show Account ID in groupBy options if not using a monitoring account', async () => {
config.featureToggles.cloudWatchCrossAccountQuerying = true;
config.featureToggles.cloudwatchMetricInsightsCrossAccount = true;
baseProps.datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(false);
const query = makeSQLQuery();
render(<SQLGroupBy {...baseProps} query={query} />);
const addButton = screen.getByRole('button', { name: 'Add' });
await userEvent.click(addButton);
selectEvent.openMenu(screen.getByLabelText(/Group by/));
expect(screen.queryByText('Account ID')).not.toBeInTheDocument();
});
it('should not show Account ID in groupBy options if feature flag is disabled', async () => {
config.featureToggles.cloudwatchMetricInsightsCrossAccount = false;
const query = makeSQLQuery();
render(<SQLGroupBy {...baseProps} query={query} />);
const addButton = screen.getByRole('button', { name: 'Add' });
await userEvent.click(addButton);
selectEvent.openMenu(screen.getByLabelText(/Group by/));
expect(screen.queryByText('Account ID')).not.toBeInTheDocument();
});
it('should allow adding a new dimension filter', async () => {
const query = makeSQLQuery({
groupBy: undefined,

View File

@ -2,6 +2,7 @@ import { useMemo, useState } from 'react';
import { SelectableValue, toOption } from '@grafana/data';
import { AccessoryButton, EditorList, InputGroup } from '@grafana/experimental';
import { config } from '@grafana/runtime';
import { Select } from '@grafana/ui';
import { CloudWatchDatasource } from '../../../../datasource';
@ -10,7 +11,7 @@ import {
QueryEditorGroupByExpression,
QueryEditorPropertyType,
} from '../../../../expressions';
import { useDimensionKeys } from '../../../../hooks';
import { useDimensionKeys, useIsMonitoringAccount } from '../../../../hooks';
import { CloudWatchMetricsQuery } from '../../../../types';
import {
@ -31,6 +32,7 @@ const SQLGroupBy = ({ query, datasource, onQueryChange }: SQLGroupByProps) => {
const sql = query.sql ?? {};
const groupBysFromQuery = useMemo(() => getFlattenedGroupBys(query.sql ?? {}), [query.sql]);
const [items, setItems] = useState<QueryEditorGroupByExpression[]>(groupBysFromQuery);
const isMonitoringAccount = useIsMonitoringAccount(datasource.resources, query.region);
const namespace = getNamespaceFromExpression(sql.from);
const metricName = getMetricNameFromExpression(sql.select);
@ -38,8 +40,20 @@ const SQLGroupBy = ({ query, datasource, onQueryChange }: SQLGroupByProps) => {
const baseOptions = useDimensionKeys(datasource, { region: query.region, namespace, metricName });
const options = useMemo(
// Exclude options we've already selected
() => baseOptions.filter((option) => !groupBysFromQuery.some((v) => v.property.name === option.value)),
[baseOptions, groupBysFromQuery]
() => {
const isCrossAccountEnabled =
config.featureToggles.cloudWatchCrossAccountQuerying &&
config.featureToggles.cloudwatchMetricInsightsCrossAccount;
const baseOptionsWithAccountId =
isCrossAccountEnabled && isMonitoringAccount
? [{ label: 'Account ID', value: 'AWS.AccountId' }, ...baseOptions]
: baseOptions;
return baseOptionsWithAccountId.filter(
(option) => !groupBysFromQuery.some((v) => v.property.name === option.value)
);
},
[baseOptions, groupBysFromQuery, isMonitoringAccount]
);
const onChange = (newItems: Array<Partial<QueryEditorGroupByExpression>>) => {

View File

@ -160,8 +160,11 @@ export default class SQLGenerator {
const startsWithNumber = /^\d/;
const interpolated = this.templateSrv.replace(label, {}, 'raw');
if (specialCharacters.test(interpolated) || startsWithNumber.test(interpolated)) {
return `"${label}"`;
if (interpolated !== 'AWS.AccountId') {
// AWS.AccountId should never be in quotes
if (specialCharacters.test(interpolated) || startsWithNumber.test(interpolated)) {
return `"${label}"`;
}
}
return label;