From c8094b33cd1ee556c96cbac1e8d1800d4cb8855a Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Thu, 26 May 2022 14:53:12 +0100 Subject: [PATCH] AzureMonitor: Ensure original query properties are included unchanged (#49642) * Ensure original query properties are included unchanged * Remove refId from pseudo datasources * Include testing for interpolateVariablesInQueries - Add util function to create template variables - Update mock query with missing props - Additional tests on each ds for template variables * Correct typo and add explicit check for datasource --- .../__mocks__/query.ts | 6 +++ .../__mocks__/utils.ts | 40 ++++++++++++++++ .../azure_log_analytics_datasource.test.ts | 37 ++++++++++++++ .../azure_log_analytics_datasource.ts | 2 +- .../azure_monitor_datasource.test.ts | 47 +++++++++++++++++- .../azure_monitor/azure_monitor_datasource.ts | 2 +- .../azure_resource_graph_datasource.test.ts | 48 +++++++++++++++++-- .../azure_resource_graph_datasource.ts | 2 +- 8 files changed, 176 insertions(+), 8 deletions(-) create mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/utils.ts diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/query.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/query.ts index cac782301ec..2b499896b5e 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/query.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/query.ts @@ -7,10 +7,12 @@ export default function createMockQuery(): AzureMonitorQuery { '//change this example to create your own time series query\n //the table to query (e.g. Usage, Heartbeat, Perf)\n| where $__timeFilter(TimeGenerated) //this is a macro used to show the full chart’s time range, choose the datetime column here\n| summarize count() by , bin(TimeGenerated, $__interval) //change “group by column” to a column in your table, such as “Computer”. The $__interval macro is used to auto-select the time grain. Can also use 1h, 5m etc.\n| order by TimeGenerated asc', resultFormat: 'time_series', workspace: 'e3fe4fde-ad5e-4d60-9974-e2f3562ffdf2', + resource: 'test-resource', }, azureResourceGraph: { query: 'Resources | summarize count()', + resultFormat: 'table', }, azureMonitor: { @@ -36,5 +38,9 @@ export default function createMockQuery(): AzureMonitorQuery { refId: 'A', subscription: '99999999-cccc-bbbb-aaaa-9106972f9572', subscriptions: ['99999999-cccc-bbbb-aaaa-9106972f9572'], + datasource: { + type: 'grafana-azure-monitor-datasource', + uid: 'AAAAA11111BBBBB22222CCCC', + }, }; } diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/utils.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/utils.ts new file mode 100644 index 00000000000..e4bc47e7c2f --- /dev/null +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/utils.ts @@ -0,0 +1,40 @@ +import { VariableType } from '@grafana/data'; +import { LoadingState } from '@grafana/data/src/types/data'; +import { VariableWithOptions } from 'app/features/variables/types'; + +interface TemplateableValue { + variableName: string; + templateVariable: VariableWithOptions; +} + +export function createTemplateVariables(templateableProps: string[]): Map { + const templateVariables = new Map(); + templateableProps.map((prop) => { + const variableName = prop.replace(/[\[\].]/g, ''); + const templateVariable = { + current: { + selected: false, + text: `${variableName}-template-variable`, + value: `${variableName}-template-variable`, + }, + id: variableName, + name: variableName, + type: 'textbox' as VariableType, + options: [], + query: '', + rootStateKey: null, + global: false, + hide: 0, + skipUrlSync: false, + index: 0, + state: 'Done' as LoadingState, + error: null, + description: null, + }; + templateVariables.set(prop, { + variableName, + templateVariable, + }); + }); + return templateVariables; +} diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.test.ts index 6acfffb8630..56144881df2 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.test.ts @@ -1,7 +1,10 @@ +import { get, set } from 'lodash'; + import { toUtc } from '@grafana/data'; import { TemplateSrv } from 'app/features/templating/template_srv'; import createMockQuery from '../__mocks__/query'; +import { createTemplateVariables } from '../__mocks__/utils'; import { singleVariable } from '../__mocks__/variables'; import AzureMonitorDatasource from '../datasource'; import { AzureMonitorQuery, AzureQueryType, DatasourceValidationResult } from '../types'; @@ -369,4 +372,38 @@ describe('AzureLogAnalyticsDatasource', () => { expect(laDatasource.filterQuery(query)).toBeFalsy(); }); }); + + describe('When performing interpolateVariablesInQueries for azure_log_analytics', () => { + beforeEach(() => { + templateSrv.init([]); + }); + + it('should return a query unchanged if no template variables are provided', () => { + const query = createMockQuery(); + query.queryType = AzureQueryType.LogAnalytics; + const templatedQuery = ctx.ds.interpolateVariablesInQueries([query], {}); + expect(templatedQuery[0]).toEqual(query); + }); + + it('should return a query with any template variables replaced', () => { + const templateableProps = ['resource', 'workspace', 'query']; + const templateVariables = createTemplateVariables(templateableProps); + templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable)); + const query = createMockQuery(); + const azureLogAnalytics: { [index: string]: any } = {}; + for (const [path, templateVariable] of templateVariables.entries()) { + set(azureLogAnalytics, path, `$${templateVariable.variableName}`); + } + query.queryType = AzureQueryType.LogAnalytics; + query.azureLogAnalytics = { + ...query.azureLogAnalytics, + ...azureLogAnalytics, + }; + const templatedQuery = ctx.ds.interpolateVariablesInQueries([query], {}); + expect(templatedQuery[0]).toHaveProperty('datasource'); + for (const [path, templateVariable] of templateVariables.entries()) { + expect(get(templatedQuery[0].azureLogAnalytics, path)).toEqual(templateVariable.templateVariable.current.value); + } + }); + }); }); 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 192f84692bc..4b5bb2b7095 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 @@ -134,7 +134,7 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< const query = templateSrv.replace(item.query, scopedVars, interpolateVariable); return { - refId: target.refId, + ...target, queryType: AzureQueryType.LogAnalytics, azureLogAnalytics: { diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.test.ts index da69bd5aa11..8b1adc3478b 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.test.ts @@ -1,9 +1,10 @@ -import { startsWith } from 'lodash'; +import { startsWith, get, set } from 'lodash'; import { DataSourceInstanceSettings } from '@grafana/data'; import { TemplateSrv } from 'app/features/templating/template_srv'; import createMockQuery from '../__mocks__/query'; +import { createTemplateVariables } from '../__mocks__/utils'; import { singleVariable, subscriptionsVariable } from '../__mocks__/variables'; import AzureMonitorDatasource from '../datasource'; import { AzureDataSourceJsonData, AzureQueryType, DatasourceValidationResult } from '../types'; @@ -259,6 +260,50 @@ describe('AzureMonitorDatasource', () => { }); }); + describe('When performing interpolateVariablesInQueries for azure_monitor_metrics', () => { + beforeEach(() => { + templateSrv.init([]); + }); + + it('should return a query unchanged if no template variables are provided', () => { + const query = createMockQuery(); + const templatedQuery = ctx.ds.interpolateVariablesInQueries([query], {}); + expect(templatedQuery[0]).toEqual(query); + }); + + it('should return a query with any template variables replaced', () => { + const templateableProps = [ + 'resourceUri', + 'resourceGroup', + 'resourceName', + 'metricNamespace', + 'metricDefinition', + 'timeGrain', + 'aggregation', + 'top', + 'dimensionFilters[0].dimension', + 'dimensionFilters[0].filters[0]', + ]; + const templateVariables = createTemplateVariables(templateableProps); + templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable)); + const query = createMockQuery(); + const azureMonitorQuery: { [index: string]: any } = {}; + for (const [path, templateVariable] of templateVariables.entries()) { + set(azureMonitorQuery, path, `$${templateVariable.variableName}`); + } + + query.azureMonitor = { + ...query.azureMonitor, + ...azureMonitorQuery, + }; + const templatedQuery = ctx.ds.interpolateVariablesInQueries([query], {}); + expect(templatedQuery[0]).toHaveProperty('datasource'); + for (const [path, templateVariable] of templateVariables.entries()) { + expect(get(templatedQuery[0].azureMonitor, path)).toEqual(templateVariable.templateVariable.current.value); + } + }); + }); + describe('Legacy Azure Monitor Query Object data fetchers', () => { describe('When performing getSubscriptions', () => { const response = { diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.ts index 52af47ee5a1..b9cf3a51899 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.ts @@ -114,7 +114,7 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend ({ @@ -41,7 +43,48 @@ describe('AzureResourceGraphDatasource', () => { ctx.ds = new AzureResourceGraphDatasource(ctx.instanceSettings); }); + describe('When performing interpolateVariablesInQueries for azure_resource_graph', () => { + beforeEach(() => { + templateSrv.init([]); + }); + + it('should return a query unchanged if no template variables are provided', () => { + const query = createMockQuery(); + query.queryType = AzureQueryType.AzureResourceGraph; + const templatedQuery = ctx.ds.interpolateVariablesInQueries([query], {}); + expect(templatedQuery[0]).toEqual(query); + }); + + it('should return a query with any template variables replaced', () => { + const templateableProps = ['query']; + const templateVariables = createTemplateVariables(templateableProps); + templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable)); + const query = createMockQuery(); + const azureResourceGraph: { [index: string]: any } = {}; + for (const [path, templateVariable] of templateVariables.entries()) { + set(azureResourceGraph, path, `$${templateVariable.variableName}`); + } + + query.queryType = AzureQueryType.AzureResourceGraph; + query.azureResourceGraph = { + ...query.azureResourceGraph, + ...azureResourceGraph, + }; + const templatedQuery = ctx.ds.interpolateVariablesInQueries([query], {}); + expect(templatedQuery[0]).toHaveProperty('datasource'); + for (const [path, templateVariable] of templateVariables.entries()) { + expect(get(templatedQuery[0].azureResourceGraph, path)).toEqual( + templateVariable.templateVariable.current.value + ); + } + }); + }); + describe('When applying template variables', () => { + beforeEach(() => { + templateSrv.init([subscriptionsVariable, singleVariable, multiVariable]); + }); + it('should expand single value template variable', () => { const target = { azureResourceGraph: { @@ -52,7 +95,6 @@ describe('AzureResourceGraphDatasource', () => { expect(ctx.ds.applyTemplateVariables(target)).toStrictEqual({ azureResourceGraph: { query: 'Resources | var1-foo', resultFormat: 'table' }, queryType: 'Azure Resource Graph', - refId: undefined, subscriptions: [], }); }); @@ -70,7 +112,6 @@ describe('AzureResourceGraphDatasource', () => { resultFormat: 'table', }, queryType: 'Azure Resource Graph', - refId: undefined, subscriptions: [], }); }); @@ -90,7 +131,6 @@ describe('AzureResourceGraphDatasource', () => { 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 5543b2baee7..bed9f6cc52c 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 @@ -35,7 +35,7 @@ export default class AzureResourceGraphDatasource extends DataSourceWithBackend< const query = templateSrv.replace(item.query, scopedVars, interpolateVariable); return { - refId: target.refId, + ...target, queryType: AzureQueryType.AzureResourceGraph, subscriptions, azureResourceGraph: {