diff --git a/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts b/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts index f846d4e254c..7cc2ab389e1 100644 --- a/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts +++ b/public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts @@ -47,7 +47,7 @@ export function setupMockedDataSource({ data = [], variables }: { data?: any; va const fetchMock = jest.fn().mockReturnValue(of({ data })); setBackendSrv({ fetch: fetchMock } as any); - return { datasource, fetchMock }; + return { datasource, fetchMock, templateService }; } export const metricVariable: CustomVariableModel = { @@ -125,3 +125,19 @@ export const aggregationvariable: CustomVariableModel = { ], multi: false, }; + +export const dimensionVariable: CustomVariableModel = { + ...initialCustomVariableModelState, + id: 'dimension', + name: 'dimension', + current: { + value: 'env', + text: 'env', + selected: true, + }, + options: [ + { value: 'env', text: 'env', selected: false }, + { value: 'tag', text: 'tag', selected: false }, + ], + multi: false, +}; diff --git a/public/app/plugins/datasource/cloudwatch/datasource.ts b/public/app/plugins/datasource/cloudwatch/datasource.ts index 6d7cdebe903..379ea6e4d45 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.ts @@ -125,7 +125,7 @@ export class CloudWatchDatasource this.logsTimeout = instanceSettings.jsonData.logsTimeout || '15m'; this.sqlCompletionItemProvider = new SQLCompletionItemProvider(this, this.templateSrv); this.metricMathCompletionItemProvider = new MetricMathCompletionItemProvider(this, this.templateSrv); - this.variables = new CloudWatchVariableSupport(this); + this.variables = new CloudWatchVariableSupport(this, this.templateSrv); } query(options: DataQueryRequest): Observable { diff --git a/public/app/plugins/datasource/cloudwatch/variables.test.ts b/public/app/plugins/datasource/cloudwatch/variables.test.ts new file mode 100644 index 00000000000..e4d2bd4dc0d --- /dev/null +++ b/public/app/plugins/datasource/cloudwatch/variables.test.ts @@ -0,0 +1,169 @@ +import { dimensionVariable, labelsVariable, setupMockedDataSource } from './__mocks__/CloudWatchDataSource'; +import { VariableQuery, VariableQueryType } from './types'; +import { CloudWatchVariableSupport } from './variables'; + +const defaultQuery: VariableQuery = { + queryType: VariableQueryType.Regions, + namespace: 'foo', + region: 'bar', + metricName: '', + dimensionKey: '', + dimensionFilters: '', + ec2Filters: '', + instanceID: '', + attributeName: '', + resourceType: '', + tags: '', + refId: '', +}; + +const ds = setupMockedDataSource({ variables: [labelsVariable, dimensionVariable] }); +ds.datasource.getRegions = jest.fn().mockResolvedValue([{ label: 'a', value: 'a' }]); +ds.datasource.getNamespaces = jest.fn().mockResolvedValue([{ label: 'b', value: 'b' }]); +ds.datasource.getMetrics = jest.fn().mockResolvedValue([{ label: 'c', value: 'c' }]); +ds.datasource.getDimensionKeys = jest.fn().mockResolvedValue([{ label: 'd', value: 'd' }]); +const getDimensionValues = jest.fn().mockResolvedValue([{ label: 'e', value: 'e' }]); +const getEbsVolumeIds = jest.fn().mockResolvedValue([{ label: 'f', value: 'f' }]); +const getEc2InstanceAttribute = jest.fn().mockResolvedValue([{ label: 'g', value: 'g' }]); +const getResourceARNs = jest.fn().mockResolvedValue([{ label: 'h', value: 'h' }]); + +const variables = new CloudWatchVariableSupport(ds.datasource, ds.templateService); + +describe('variables', () => { + it('should run regions', async () => { + const result = await variables.execute({ ...defaultQuery }); + expect(result).toEqual([{ text: 'a', value: 'a', expandable: true }]); + }); + + it('should run namespaces', async () => { + const result = await variables.execute({ ...defaultQuery, queryType: VariableQueryType.Namespaces }); + expect(result).toEqual([{ text: 'b', value: 'b', expandable: true }]); + }); + + it('should run metrics', async () => { + const result = await variables.execute({ ...defaultQuery, queryType: VariableQueryType.Metrics }); + expect(result).toEqual([{ text: 'c', value: 'c', expandable: true }]); + }); + + it('should run dimension keys', async () => { + const result = await variables.execute({ ...defaultQuery, queryType: VariableQueryType.DimensionKeys }); + expect(result).toEqual([{ text: 'd', value: 'd', expandable: true }]); + }); + + describe('dimension values', () => { + const query = { + ...defaultQuery, + queryType: VariableQueryType.DimensionValues, + metricName: 'abc', + dimensionKey: 'efg', + dimensionFilters: '{"a":"b"}', + }; + beforeEach(() => { + ds.datasource.getDimensionValues = getDimensionValues; + getDimensionValues.mockClear(); + }); + + it('should not run if dimension key not set', async () => { + const result = await variables.execute({ ...query, dimensionKey: '' }); + expect(getDimensionValues).not.toBeCalled(); + expect(result).toEqual([]); + }); + + it('should not run if metric name not set', async () => { + const result = await variables.execute({ ...query, metricName: '' }); + expect(getDimensionValues).not.toBeCalled(); + expect(result).toEqual([]); + }); + it('should run if values are set', async () => { + const result = await variables.execute(query); + expect(getDimensionValues).toBeCalledWith(query.region, query.namespace, query.metricName, query.dimensionKey, { + a: 'b', + }); + expect(result).toEqual([{ text: 'e', value: 'e', expandable: true }]); + }); + }); + + describe('EBS volume ids', () => { + beforeEach(() => { + ds.datasource.getEbsVolumeIds = getEbsVolumeIds; + getEbsVolumeIds.mockClear(); + }); + + it('should not run if instance id not set', async () => { + const result = await variables.execute({ ...defaultQuery, queryType: VariableQueryType.EBSVolumeIDs }); + expect(getEbsVolumeIds).not.toBeCalled(); + expect(result).toEqual([]); + }); + + it('should run if instance id set', async () => { + const result = await variables.execute({ + ...defaultQuery, + queryType: VariableQueryType.EBSVolumeIDs, + instanceID: 'foo', + }); + expect(getEbsVolumeIds).toBeCalledWith(defaultQuery.region, 'foo'); + expect(result).toEqual([{ text: 'f', value: 'f', expandable: true }]); + }); + }); + + describe('EC2 instance attributes', () => { + const query = { + ...defaultQuery, + queryType: VariableQueryType.EC2InstanceAttributes, + attributeName: 'abc', + ec2Filters: '{"$dimension":["b"]}', + }; + beforeEach(() => { + ds.datasource.getEc2InstanceAttribute = getEc2InstanceAttribute; + getEc2InstanceAttribute.mockClear(); + }); + + it('should not run if instance id not set', async () => { + const result = await variables.execute({ ...query, attributeName: '' }); + expect(getEc2InstanceAttribute).not.toBeCalled(); + expect(result).toEqual([]); + }); + + it('should run if instance id set', async () => { + const result = await variables.execute(query); + expect(getEc2InstanceAttribute).toBeCalledWith(query.region, query.attributeName, { env: ['b'] }); + expect(result).toEqual([{ text: 'g', value: 'g', expandable: true }]); + }); + }); + + describe('resource arns', () => { + const query = { + ...defaultQuery, + queryType: VariableQueryType.ResourceArns, + resourceType: 'abc', + tags: '{"a":${labels:json}}', + }; + beforeEach(() => { + ds.datasource.getResourceARNs = getResourceARNs; + getResourceARNs.mockClear(); + }); + + it('should not run if instance id not set', async () => { + const result = await variables.execute({ ...query, resourceType: '' }); + expect(getResourceARNs).not.toBeCalled(); + expect(result).toEqual([]); + }); + + it('should run if instance id set', async () => { + const result = await variables.execute(query); + expect(getResourceARNs).toBeCalledWith(query.region, query.resourceType, { a: ['InstanceId', 'InstanceType'] }); + expect(result).toEqual([{ text: 'h', value: 'h', expandable: true }]); + }); + }); + + it('should run statistics', async () => { + const result = await variables.execute({ ...defaultQuery, queryType: VariableQueryType.Statistics }); + expect(result).toEqual([ + { text: 'Average', value: 'Average', expandable: true }, + { text: 'Maximum', value: 'Maximum', expandable: true }, + { text: 'Minimum', value: 'Minimum', expandable: true }, + { text: 'Sum', value: 'Sum', expandable: true }, + { text: 'SampleCount', value: 'SampleCount', expandable: true }, + ]); + }); +}); diff --git a/public/app/plugins/datasource/cloudwatch/variables.ts b/public/app/plugins/datasource/cloudwatch/variables.ts index 836c2db3876..f251e15a2fd 100644 --- a/public/app/plugins/datasource/cloudwatch/variables.ts +++ b/public/app/plugins/datasource/cloudwatch/variables.ts @@ -2,6 +2,7 @@ import { from, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { CustomVariableSupport, DataQueryRequest, DataQueryResponse } from '@grafana/data'; +import { getTemplateSrv, TemplateSrv } from '@grafana/runtime'; import { VariableQueryEditor } from './components/VariableQueryEditor/VariableQueryEditor'; import { CloudWatchDatasource } from './datasource'; @@ -10,10 +11,12 @@ import { VariableQuery, VariableQueryType } from './types'; export class CloudWatchVariableSupport extends CustomVariableSupport { private readonly datasource: CloudWatchDatasource; + private readonly templateSrv: TemplateSrv; - constructor(datasource: CloudWatchDatasource) { + constructor(datasource: CloudWatchDatasource, templateSrv: TemplateSrv = getTemplateSrv()) { super(); this.datasource = datasource; + this.templateSrv = templateSrv; this.query = this.query.bind(this); } @@ -122,7 +125,7 @@ export class CloudWatchVariableSupport extends CustomVariableSupport ({ @@ -138,7 +141,7 @@ export class CloudWatchVariableSupport extends CustomVariableSupport ({