diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.ts index 91e31c7614f..be06ac3e498 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.ts @@ -20,7 +20,7 @@ import { Observable, from } from 'rxjs'; import { mergeMap } from 'rxjs/operators'; import { getAuthType, getAzureCloud, getAzurePortalUrl } from '../credentials'; import { isGUIDish } from '../components/ResourcePicker/utils'; -import { routeNames } from '../utils/common'; +import { interpolateVariable, routeNames } from '../utils/common'; interface AdhocQuery { datasourceId: number; @@ -118,7 +118,7 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< workspace = this.firstWorkspace; } - const query = templateSrv.replace(item.query, scopedVars, this.interpolateVariable); + const query = templateSrv.replace(item.query, scopedVars, interpolateVariable); return { refId: target.refId, @@ -272,7 +272,7 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< private buildQuery(query: string, options: any, workspace: string): AdhocQuery[] { const querystringBuilder = new LogAnalyticsQuerystringBuilder( - getTemplateSrv().replace(query, {}, this.interpolateVariable), + getTemplateSrv().replace(query, {}, interpolateVariable), options, 'TimeGenerated' ); @@ -293,29 +293,6 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< return queries; } - interpolateVariable(value: string, variable: { multi: any; includeAll: any }) { - if (typeof value === 'string') { - if (variable.multi || variable.includeAll) { - return "'" + value + "'"; - } else { - return value; - } - } - - if (typeof value === 'number') { - return value; - } - - const quotedValues = map(value, (val) => { - if (typeof value === 'number') { - return value; - } - - return "'" + val + "'"; - }); - return quotedValues.join(','); - } - async getDefaultOrFirstSubscription(): Promise { if (this.defaultSubscriptionId) { return this.defaultSubscriptionId; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_resource_graph/azure_resource_graph_datasource.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_resource_graph/azure_resource_graph_datasource.test.ts index 875f6f63c41..d98ec160af9 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_resource_graph/azure_resource_graph_datasource.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_resource_graph/azure_resource_graph_datasource.test.ts @@ -2,9 +2,6 @@ import { TemplateSrv } from 'app/features/templating/template_srv'; import { backendSrv } from 'app/core/services/backend_srv'; import AzureResourceGraphDatasource from './azure_resource_graph_datasource'; import { CustomVariableModel, initialVariableModelState, VariableHide } from 'app/features/variables/types'; -import { initialCustomVariableModelState } from 'app/features/variables/custom/reducer'; - -const templateSrv = new TemplateSrv(); const single: CustomVariableModel = { ...initialVariableModelState, @@ -38,7 +35,30 @@ const multi: CustomVariableModel = { type: 'custom', }; -templateSrv.init([single, multi]); +const subs: CustomVariableModel = { + ...initialVariableModelState, + id: 'subs', + name: 'subs', + index: 3, + current: { value: ['sub-foo', 'sub-baz'], text: 'sub-foo + sub-baz', selected: true }, + options: [ + { selected: true, value: 'sub-foo', text: 'sub-foo' }, + { selected: false, value: 'sub-bar', text: 'sub-bar' }, + { selected: true, value: 'sub-baz', text: 'sub-baz' }, + ], + multi: true, + includeAll: false, + query: '', + hide: VariableHide.dontHide, + type: 'custom', +}; + +const templateSrv = new TemplateSrv({ + getVariables: () => [subs, single, multi], + getVariableWithName: jest.fn(), + getFilteredVariables: jest.fn(), +}); +templateSrv.init([subs, single, multi]); jest.mock('app/core/services/backend_srv'); jest.mock('@grafana/runtime', () => ({ @@ -77,7 +97,7 @@ describe('AzureResourceGraphDatasource', () => { azureResourceGraph: { query: 'Resources | var1-foo', resultFormat: 'table' }, queryType: 'Azure Resource Graph', refId: undefined, - subscriptions: undefined, + subscriptions: [], }); }); @@ -95,53 +115,27 @@ describe('AzureResourceGraphDatasource', () => { }, queryType: 'Azure Resource Graph', refId: undefined, - subscriptions: undefined, + subscriptions: [], }); }); }); - describe('When interpolating variables', () => { - beforeEach(() => { - ctx.variable = { ...initialCustomVariableModelState }; - }); - - describe('and value is a string', () => { - it('should return an unquoted value', () => { - expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual('abc'); - }); - }); - - describe('and value is a number', () => { - it('should return an unquoted value', () => { - expect(ctx.ds.interpolateVariable(1000, ctx.variable)).toEqual(1000); - }); - }); - - describe('and value is an array of strings', () => { - it('should return comma separated quoted values', () => { - expect(ctx.ds.interpolateVariable(['a', 'b', 'c'], ctx.variable)).toEqual("'a','b','c'"); - }); - }); - - describe('and variable allows multi-value and value is a string', () => { - it('should return a quoted value', () => { - ctx.variable.multi = true; - expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'"); - }); - }); - - describe('and variable contains single quote', () => { - it('should return a quoted value', () => { - ctx.variable.multi = true; - expect(ctx.ds.interpolateVariable("a'bc", ctx.variable)).toEqual("'a'bc'"); - }); - }); - - describe('and variable allows all and value is a string', () => { - it('should return a quoted value', () => { - ctx.variable.includeAll = true; - expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'"); - }); + it('should apply subscription variable', () => { + const target = { + subscriptions: ['$subs'], + azureResourceGraph: { + query: 'resources | where $__contains(name, $var3)', + resultFormat: '', + }, + }; + expect(ctx.ds.applyTemplateVariables(target)).toStrictEqual({ + azureResourceGraph: { + query: `resources | where $__contains(name, 'var3-foo','var3-baz')`, + resultFormat: 'table', + }, + queryType: 'Azure Resource Graph', + refId: undefined, + subscriptions: ['sub-foo', 'sub-baz'], }); }); }); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_resource_graph/azure_resource_graph_datasource.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_resource_graph/azure_resource_graph_datasource.ts index 4ac37c91bd3..e768b9d2d77 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_resource_graph/azure_resource_graph_datasource.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_resource_graph/azure_resource_graph_datasource.ts @@ -3,6 +3,7 @@ import _ from 'lodash'; import { AzureMonitorQuery, AzureDataSourceJsonData, AzureQueryType } from '../types'; import { ScopedVars } from '@grafana/data'; import { getTemplateSrv, DataSourceWithBackend } from '@grafana/runtime'; +import { interpolateVariable } from '../utils/common'; export default class AzureResourceGraphDatasource extends DataSourceWithBackend< AzureMonitorQuery, @@ -19,39 +20,26 @@ export default class AzureResourceGraphDatasource extends DataSourceWithBackend< } const templateSrv = getTemplateSrv(); - const query = templateSrv.replace(item.query, scopedVars, this.interpolateVariable); + const variableNames = templateSrv.getVariables().map((v) => `$${v.name}`); + const subscriptionVar = _.find(target.subscriptions, (sub) => _.includes(variableNames, sub)); + const interpolatedSubscriptions = templateSrv + .replace(subscriptionVar, scopedVars, (v: any) => v) + .split(',') + .filter((v) => v.length > 0); + const subscriptions = [ + ...interpolatedSubscriptions, + ..._.filter(target.subscriptions, (sub) => !_.includes(variableNames, sub)), + ]; + const query = templateSrv.replace(item.query, scopedVars, interpolateVariable); return { refId: target.refId, queryType: AzureQueryType.AzureResourceGraph, - subscriptions: target.subscriptions, + subscriptions, azureResourceGraph: { resultFormat: 'table', query, }, }; } - - interpolateVariable(value: string, variable: { multi: any; includeAll: any }) { - if (typeof value === 'string') { - if (variable.multi || variable.includeAll) { - return "'" + value + "'"; - } else { - return value; - } - } - - if (typeof value === 'number') { - return value; - } - - const quotedValues = _.map(value, (val) => { - if (typeof value === 'number') { - return value; - } - - return "'" + val + "'"; - }); - return quotedValues.join(','); - } } diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/SubscriptionField.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/SubscriptionField.tsx index bb462ca5312..af76717593c 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/SubscriptionField.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/SubscriptionField.tsx @@ -69,7 +69,7 @@ const SubscriptionField: React.FC = ({ { it('can find an option in flat array', () => { @@ -40,3 +41,44 @@ describe('AzureMonitor: hasOption', () => { expect(hasOption(options, 'c-b')).toBeTruthy(); }); }); + +describe('When interpolating variables', () => { + describe('and value is a string', () => { + it('should return an unquoted value', () => { + expect(interpolateVariable('abc', initialCustomVariableModelState)).toEqual('abc'); + }); + }); + + describe('and value is a number', () => { + it('should return an unquoted value', () => { + expect(interpolateVariable(1000, initialCustomVariableModelState)).toEqual(1000); + }); + }); + + describe('and value is an array of strings', () => { + it('should return comma separated quoted values', () => { + expect(interpolateVariable(['a', 'b', 'c'], initialCustomVariableModelState)).toEqual("'a','b','c'"); + }); + }); + + describe('and variable allows multi-value and value is a string', () => { + it('should return a quoted value', () => { + const variable = { ...initialCustomVariableModelState, multi: true }; + expect(interpolateVariable('abc', variable)).toEqual("'abc'"); + }); + }); + + describe('and variable contains single quote', () => { + it('should return a quoted value', () => { + const variable = { ...initialCustomVariableModelState, multi: true }; + expect(interpolateVariable("a'bc", variable)).toEqual("'a'bc'"); + }); + }); + + describe('and variable allows all and value is a string', () => { + it('should return a quoted value', () => { + const variable = { ...initialCustomVariableModelState, includeAll: true }; + expect(interpolateVariable('abc', variable)).toEqual("'abc'"); + }); + }); +}); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/common.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/common.ts index 1a131605813..9bcfae999d6 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/common.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/common.ts @@ -1,3 +1,4 @@ +import { map } from 'lodash'; import { rangeUtil } from '@grafana/data'; import TimegrainConverter from '../time_grain_converter'; import { AzureMonitorOption } from '../types'; @@ -36,3 +37,26 @@ export const routeNames = { appInsights: 'appinsights', resourceGraph: 'resourcegraph', }; + +export function interpolateVariable(value: any, variable: { multi: any; includeAll: any }) { + if (typeof value === 'string') { + if (variable.multi || variable.includeAll) { + return "'" + value + "'"; + } else { + return value; + } + } + + if (typeof value === 'number') { + return value; + } + + const quotedValues = map(value, (val) => { + if (typeof value === 'number') { + return value; + } + + return "'" + val + "'"; + }); + return quotedValues.join(','); +}