diff --git a/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts b/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts index 8070eeb8688..5479778b689 100644 --- a/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts +++ b/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts @@ -1,11 +1,12 @@ import { dateTime } from '@grafana/data'; import { setBackendSrv } from '@grafana/runtime'; -import { TemplateSrvMock } from '../../../../features/templating/template_srv.mock'; +import { TemplateSrv } from 'app/features/templating/template_srv'; import { initialCustomVariableModelState } from 'app/features/variables/custom/reducer'; import { CustomVariableModel } from 'app/features/variables/types'; import { of } from 'rxjs'; + +import { TemplateSrvMock } from '../../../../features/templating/template_srv.mock'; import { CloudWatchDatasource } from '../datasource'; -import { TemplateSrv } from 'app/features/templating/template_srv'; export function setupMockedDataSource({ data = [], variables }: { data?: any; variables?: any } = {}) { let templateService = new TemplateSrvMock({ @@ -16,6 +17,8 @@ export function setupMockedDataSource({ data = [], variables }: { data?: any; va if (variables) { templateService = new TemplateSrv(); templateService.init(variables); + templateService.getVariables = jest.fn().mockReturnValue(variables); + templateService.getVariableName = (name: string) => name; } const datasource = new CloudWatchDatasource( diff --git a/public/app/plugins/datasource/cloudwatch/components/QueryHeader.tsx b/public/app/plugins/datasource/cloudwatch/components/QueryHeader.tsx index def9d666832..268abb355e1 100644 --- a/public/app/plugins/datasource/cloudwatch/components/QueryHeader.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/QueryHeader.tsx @@ -1,12 +1,11 @@ -import React from 'react'; -import { pick } from 'lodash'; - import { ExploreMode, SelectableValue } from '@grafana/data'; import { EditorHeader, InlineSelect } from '@grafana/experimental'; +import { pick } from 'lodash'; +import React from 'react'; import { CloudWatchDatasource } from '../datasource'; -import { CloudWatchQuery, CloudWatchQueryMode } from '../types'; import { useRegions } from '../hooks'; +import { CloudWatchQuery, CloudWatchQueryMode } from '../types'; import MetricsQueryHeader from './MetricsQueryHeader'; interface QueryHeaderProps { @@ -59,7 +58,7 @@ const QueryHeader: React.FC = ({ v.value === region)} + value={region} placeholder="Select region" allowCustomValue onChange={({ value: region }) => region && onRegion({ value: region })} diff --git a/public/app/plugins/datasource/cloudwatch/datasource.test.ts b/public/app/plugins/datasource/cloudwatch/datasource.test.ts index 591e95dcee6..840e90ffbad 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.test.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.test.ts @@ -1,17 +1,17 @@ -import { lastValueFrom, of } from 'rxjs'; -import { setDataSourceSrv } from '@grafana/runtime'; import { ArrayVector, DataFrame, dataFrameToJSON, dateTime, Field, MutableDataFrame } from '@grafana/data'; - +import { setDataSourceSrv } from '@grafana/runtime'; +import { lastValueFrom, of } from 'rxjs'; import { toArray } from 'rxjs/operators'; -import { CloudWatchMetricsQuery, MetricEditorMode, MetricQueryType, CloudWatchLogsQueryStatus } from './types'; + import { - setupMockedDataSource, - namespaceVariable, - metricVariable, labelsVariable, limitVariable, + metricVariable, + namespaceVariable, + setupMockedDataSource, } from './__mocks__/CloudWatchDataSource'; import { CloudWatchDatasource } from './datasource'; +import { CloudWatchLogsQueryStatus, CloudWatchMetricsQuery, MetricEditorMode, MetricQueryType } from './types'; describe('datasource', () => { describe('query', () => { @@ -91,6 +91,57 @@ describe('datasource', () => { }, ]); }); + + describe('debouncedCustomAlert', () => { + const debouncedAlert = jest.fn(); + beforeEach(() => { + const { datasource } = setupMockedDataSource({ + variables: [ + { ...namespaceVariable, multi: true }, + { ...metricVariable, multi: true }, + ], + }); + datasource.debouncedCustomAlert = debouncedAlert; + datasource.performTimeSeriesQuery = jest.fn().mockResolvedValue([]); + datasource.query({ + targets: [ + { + queryMode: 'Metrics', + id: '', + region: 'us-east-2', + namespace: namespaceVariable.id, + metricName: metricVariable.id, + period: '', + alias: '', + dimensions: {}, + matchExact: true, + statistic: '', + refId: '', + expression: 'x * 2', + metricQueryType: MetricQueryType.Search, + metricEditorMode: MetricEditorMode.Code, + }, + ], + } as any); + }); + it('should show debounced alert for namespace and metric name', async () => { + expect(debouncedAlert).toHaveBeenCalledWith( + 'CloudWatch templating error', + 'Multi template variables are not supported for namespace' + ); + expect(debouncedAlert).toHaveBeenCalledWith( + 'CloudWatch templating error', + 'Multi template variables are not supported for metric name' + ); + }); + + it('should not show debounced alert for region', async () => { + expect(debouncedAlert).not.toHaveBeenCalledWith( + 'CloudWatch templating error', + 'Multi template variables are not supported for region' + ); + }); + }); }); describe('filterMetricQuery', () => { diff --git a/public/app/plugins/datasource/cloudwatch/datasource.ts b/public/app/plugins/datasource/cloudwatch/datasource.ts index 91881025ddc..32f1f374671 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.ts @@ -1,9 +1,3 @@ -import React from 'react'; -import { cloneDeep, find, findLast, isEmpty, isString, set } from 'lodash'; -import { from, lastValueFrom, merge, Observable, of, throwError, zip } from 'rxjs'; -import { catchError, concatMap, finalize, map, mergeMap, repeat, scan, share, takeWhile, tap } from 'rxjs/operators'; -import { DataSourceWithBackend, FetchError, getBackendSrv, toDataQueryResponse } from '@grafana/runtime'; -import { RowContextOptions } from '@grafana/ui/src/components/Logs/LogRowContextProvider'; import { DataFrame, DataQueryError, @@ -22,44 +16,50 @@ import { TimeRange, toLegacyResponseData, } from '@grafana/data'; - +import { DataSourceWithBackend, FetchError, getBackendSrv, toDataQueryResponse } from '@grafana/runtime'; +import { toTestingStatus } from '@grafana/runtime/src/utils/queryResponse'; +import { RowContextOptions } from '@grafana/ui/src/components/Logs/LogRowContextProvider'; import { notifyApp } from 'app/core/actions'; import { createErrorNotification } from 'app/core/copy/appNotification'; -import { AppNotificationTimeout } from 'app/types'; -import { store } from 'app/store/store'; -import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; +import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; +import { VariableWithMultiSupport } from 'app/features/variables/types'; +import { store } from 'app/store/store'; +import { AppNotificationTimeout } from 'app/types'; +import { cloneDeep, find, findLast, isEmpty, isString, set } from 'lodash'; +import React from 'react'; +import { from, lastValueFrom, merge, Observable, of, throwError, zip } from 'rxjs'; +import { catchError, concatMap, finalize, map, mergeMap, repeat, scan, share, takeWhile, tap } from 'rxjs/operators'; + +import { SQLCompletionItemProvider } from './cloudwatch-sql/completion/CompletionItemProvider'; import { ThrottlingErrorMessage } from './components/ThrottlingErrorMessage'; +import { CloudWatchLanguageProvider } from './language_provider'; import memoizedDebounce from './memoizedDebounce'; +import { MetricMathCompletionItemProvider } from './metric-math/completion/CompletionItemProvider'; import { - MetricEditorMode, CloudWatchJsonData, CloudWatchLogsQuery, CloudWatchLogsQueryStatus, + CloudWatchLogsRequest, CloudWatchMetricsQuery, CloudWatchQuery, DescribeLogGroupsRequest, + Dimensions, GetLogEventsRequest, GetLogGroupFieldsRequest, GetLogGroupFieldsResponse, isCloudWatchLogsQuery, LogAction, - MetricQueryType, + MetricEditorMode, MetricQuery, + MetricQueryType, MetricRequest, StartQueryRequest, TSDBResponse, - Dimensions, - CloudWatchLogsRequest, } from './types'; -import { CloudWatchLanguageProvider } from './language_provider'; -import { VariableWithMultiSupport } from 'app/features/variables/types'; -import { increasingInterval } from './utils/rxjs/increasingInterval'; -import { toTestingStatus } from '@grafana/runtime/src/utils/queryResponse'; import { addDataLinksToLogsResponse } from './utils/datalinks'; import { runWithRetry } from './utils/logsRetry'; -import { SQLCompletionItemProvider } from './cloudwatch-sql/completion/CompletionItemProvider'; -import { MetricMathCompletionItemProvider } from './metric-math/completion/CompletionItemProvider'; +import { increasingInterval } from './utils/rxjs/increasingInterval'; const DS_QUERY_ENDPOINT = '/api/ds/query'; @@ -268,7 +268,7 @@ export class CloudWatchDatasource const validMetricsQueries = metricQueries .filter(this.filterMetricQuery) .map((item: CloudWatchMetricsQuery): MetricQuery => { - item.region = this.replace(this.getActualRegion(item.region), options.scopedVars, true, 'region'); + item.region = this.templateSrv.replace(this.getActualRegion(item.region), options.scopedVars); item.namespace = this.replace(item.namespace, options.scopedVars, true, 'namespace'); item.metricName = this.replace(item.metricName, options.scopedVars, true, 'metric name'); item.dimensions = this.convertDimensionFormat(item.dimensions ?? {}, options.scopedVars);