From 3ea6d1f7cc6a9324d9a391fe8a36e79b041764a7 Mon Sep 17 00:00:00 2001 From: Andrew Hackmann <5140848+bossinc@users.noreply.github.com> Date: Wed, 4 Oct 2023 07:25:05 -0700 Subject: [PATCH] AzureMonitor: Remove dependencies on Grafana frontend (#75595) * remove local graf templateSrv and use @grafana/runtime * tests broken still 0.0 * wip * moving around tests and fixing * template var test using mocking and stuff * AM ds tests fixed * fix filter tests * all tests work * remove comment * Update azure_log_analytics_datasource.ts * remove deferred util - not used * mock instead of spying * not calling getTemplateSrv each time * pass range down to filter instead of doing werid stuff * use default time range if no time range * prettierer * remove note --- .../azuremonitor/__mocks__/datasource.ts | 10 +- .../azuremonitor/__mocks__/utils.ts | 3 +- .../azuremonitor/__mocks__/variables.ts | 16 +- .../azure_log_analytics_datasource.test.ts | 137 +++++------ .../azure_log_analytics_datasource.ts | 38 ++- .../azure_monitor_datasource.test.ts | 228 +++++++++--------- .../azure_monitor/azure_monitor_datasource.ts | 39 ++- .../azure_resource_graph_datasource.test.ts | 126 ++++------ .../azure_resource_graph_datasource.ts | 9 +- .../components/QueryEditor/QueryEditor.tsx | 4 + .../components/TracesQueryEditor/Filter.tsx | 16 +- .../TracesQueryEditor/Filters.test.tsx | 13 + .../components/TracesQueryEditor/Filters.tsx | 22 +- .../TracesQueryEditor/TracesQueryEditor.tsx | 7 + .../azuremonitor/datasource.test.ts | 63 +++++ .../datasource/azuremonitor/datasource.ts | 3 +- .../datasource/azuremonitor/types/types.ts | 2 + .../datasource/azuremonitor/utils/common.ts | 3 +- .../datasource/azuremonitor/variables.ts | 12 +- 19 files changed, 394 insertions(+), 357 deletions(-) diff --git a/public/app/plugins/datasource/azuremonitor/__mocks__/datasource.ts b/public/app/plugins/datasource/azuremonitor/__mocks__/datasource.ts index dc973f158e4..170ee9e5618 100644 --- a/public/app/plugins/datasource/azuremonitor/__mocks__/datasource.ts +++ b/public/app/plugins/datasource/azuremonitor/__mocks__/datasource.ts @@ -1,7 +1,5 @@ import { DataSourceInstanceSettings } from '@grafana/data'; -import { ContextSrv } from 'app/core/services/context_srv'; -import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; -import { TemplateSrv } from 'app/features/templating/template_srv'; +import { getTemplateSrv, TemplateSrv } from '@grafana/runtime'; import Datasource from '../datasource'; import { AzureDataSourceJsonData } from '../types'; @@ -20,15 +18,12 @@ export function createContext(overrides?: DeepPartial): Context { const instanceSettings = createMockInstanceSetttings(overrides?.instanceSettings); return { instanceSettings, - templateSrv: new TemplateSrv(), + templateSrv: getTemplateSrv(), datasource: new Datasource(instanceSettings), getResource: jest.fn(), }; } -const contextSrv = new ContextSrv(); -const timeSrv = new TimeSrv(contextSrv); - export default function createMockDatasource(overrides?: DeepPartial) { // We make this a partial so we get _some_ kind of type safety when making this, rather than // having it be any or casted immediately to Datasource @@ -71,7 +66,6 @@ export default function createMockDatasource(overrides?: DeepPartial azureLogAnalyticsDatasource: { getKustoSchema: () => Promise.resolve(), getDeprecatedDefaultWorkSpace: () => 'defaultWorkspaceId', - timeSrv, }, resourcePickerData: { getSubscriptions: () => jest.fn().mockResolvedValue([]), diff --git a/public/app/plugins/datasource/azuremonitor/__mocks__/utils.ts b/public/app/plugins/datasource/azuremonitor/__mocks__/utils.ts index 1f29c41c66f..921573b797e 100644 --- a/public/app/plugins/datasource/azuremonitor/__mocks__/utils.ts +++ b/public/app/plugins/datasource/azuremonitor/__mocks__/utils.ts @@ -1,6 +1,5 @@ -import { VariableType } from '@grafana/data'; +import { VariableType, VariableWithOptions } from '@grafana/data'; import { LoadingState } from '@grafana/data/src/types/data'; -import { VariableWithOptions } from 'app/features/variables/types'; interface TemplateableValue { variableName: string; diff --git a/public/app/plugins/datasource/azuremonitor/__mocks__/variables.ts b/public/app/plugins/datasource/azuremonitor/__mocks__/variables.ts index 9c8cf96c44c..54c0c46ef84 100644 --- a/public/app/plugins/datasource/azuremonitor/__mocks__/variables.ts +++ b/public/app/plugins/datasource/azuremonitor/__mocks__/variables.ts @@ -1,4 +1,18 @@ -import { CustomVariableModel, initialVariableModelState, VariableHide } from 'app/features/variables/types'; +import { BaseVariableModel, CustomVariableModel, LoadingState, VariableHide } from '@grafana/data'; + +const initialVariableModelState: BaseVariableModel = { + id: '00000000-0000-0000-0000-000000000000', + rootStateKey: null, + name: '', + type: 'query', + global: false, + index: -1, + hide: VariableHide.dontHide, + skipUrlSync: false, + state: LoadingState.NotStarted, + error: null, + description: null, +}; export const subscriptionsVariable: CustomVariableModel = { ...initialVariableModelState, diff --git a/public/app/plugins/datasource/azuremonitor/azure_log_analytics/azure_log_analytics_datasource.test.ts b/public/app/plugins/datasource/azuremonitor/azure_log_analytics/azure_log_analytics_datasource.test.ts index 13de4a8e91d..e7fbc8c9115 100644 --- a/public/app/plugins/datasource/azuremonitor/azure_log_analytics/azure_log_analytics_datasource.test.ts +++ b/public/app/plugins/datasource/azuremonitor/azure_log_analytics/azure_log_analytics_datasource.test.ts @@ -1,37 +1,43 @@ -import { TemplateSrv } from 'app/features/templating/template_srv'; +import { CustomVariableModel } from '@grafana/data'; import { Context, createContext } from '../__mocks__/datasource'; import createMockQuery from '../__mocks__/query'; -import { createTemplateVariables } from '../__mocks__/utils'; import { singleVariable } from '../__mocks__/variables'; -import AzureMonitorDatasource from '../datasource'; import { AzureLogsQuery, AzureMonitorQuery, AzureQueryType, AzureTracesQuery } from '../types'; import FakeSchemaData from './__mocks__/schema'; import AzureLogAnalyticsDatasource from './azure_log_analytics_datasource'; -const templateSrv = new TemplateSrv(); +let getTempVars = () => [] as CustomVariableModel[]; +let replace = () => ''; -jest.mock('app/core/services/backend_srv'); -jest.mock('@grafana/runtime', () => ({ - ...jest.requireActual('@grafana/runtime'), - getTemplateSrv: () => templateSrv, -})); +jest.mock('@grafana/runtime', () => { + return { + __esModule: true, + ...jest.requireActual('@grafana/runtime'), + getTemplateSrv: () => ({ + replace: replace, + getVariables: getTempVars, + updateTimeRange: jest.fn(), + containsTemplate: jest.fn(), + }), + }; +}); describe('AzureLogAnalyticsDatasource', () => { let ctx: Context; beforeEach(() => { - templateSrv.init([singleVariable]); - templateSrv.getVariables = jest.fn().mockReturnValue([singleVariable]); ctx = createContext({ instanceSettings: { jsonData: { subscriptionId: 'xxx' }, url: 'http://azureloganalyticsapi' }, }); - ctx.templateSrv = templateSrv; }); describe('When performing getSchema', () => { beforeEach(() => { + getTempVars = () => [] as CustomVariableModel[]; + replace = (target?: string) => target || ''; + ctx = createContext(); ctx.getResource = jest.fn().mockImplementation((path: string) => { expect(path).toContain('metadata'); return Promise.resolve(FakeSchemaData.getlogAnalyticsFakeMetadata()); @@ -71,6 +77,13 @@ describe('AzureLogAnalyticsDatasource', () => { }); it('should interpolate variables when making a request for a schema with a uri that contains template variables', async () => { + replace = () => 'myWorkspace/var1-foo'; + ctx = createContext(); + ctx.getResource = jest.fn().mockImplementation((path: string) => { + expect(path).toContain('metadata'); + return Promise.resolve(FakeSchemaData.getlogAnalyticsFakeMetadata()); + }); + ctx.datasource.azureLogAnalyticsDatasource.getResource = ctx.getResource; await ctx.datasource.azureLogAnalyticsDatasource.getKustoSchema('myWorkspace/$var1'); expect(ctx.getResource).lastCalledWith('loganalytics/v1myWorkspace/var1-foo/metadata'); }); @@ -89,7 +102,15 @@ describe('AzureLogAnalyticsDatasource', () => { }); it('should include template variables as global parameters', async () => { + getTempVars = () => [singleVariable]; + ctx = createContext(); + ctx.getResource = jest.fn().mockImplementation((path: string) => { + expect(path).toContain('metadata'); + return Promise.resolve(FakeSchemaData.getlogAnalyticsFakeMetadata()); + }); + ctx.datasource.azureLogAnalyticsDatasource.getResource = ctx.getResource; const result = await ctx.datasource.azureLogAnalyticsDatasource.getKustoSchema('myWorkspace'); + expect(result.globalScalarParameters?.map((f: { name: string }) => f.name)).toEqual([`$${singleVariable.name}`]); }); }); @@ -129,37 +150,6 @@ describe('AzureLogAnalyticsDatasource', () => { }); }); - describe('When performing targetContainsTemplate', () => { - it('should return false when no variable is being used', () => { - const query = createMockQuery(); - const ds = new AzureMonitorDatasource(ctx.instanceSettings); - query.queryType = AzureQueryType.LogAnalytics; - expect(ds.targetContainsTemplate(query)).toEqual(false); - }); - - it('should return true when resource field is using a variable', () => { - const templateSrv = new TemplateSrv(); - const query = createMockQuery(); - templateSrv.init([singleVariable]); - - const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv); - query.queryType = AzureQueryType.LogAnalytics; - query.azureLogAnalytics = { resources: [`$${singleVariable.name}`] }; - expect(ds.targetContainsTemplate(query)).toEqual(true); - }); - - it('should return false when a variable is used in a different part of the query', () => { - const templateSrv = new TemplateSrv(); - const query = createMockQuery(); - templateSrv.init([singleVariable]); - - const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv); - query.queryType = AzureQueryType.LogAnalytics; - query.azureResourceGraph = { query: `$${singleVariable.name}` }; - expect(ds.targetContainsTemplate(query)).toEqual(false); - }); - }); - describe('When performing filterQuery', () => { let laDatasource: AzureLogAnalyticsDatasource; @@ -248,7 +238,9 @@ describe('AzureLogAnalyticsDatasource', () => { describe('When performing interpolateVariablesInQueries for azure_log_analytics', () => { beforeEach(() => { - templateSrv.init([]); + getTempVars = () => [] as CustomVariableModel[]; + replace = (target?: string) => target || ''; + ctx = createContext(); }); it('should return a query unchanged if no template variables are provided', () => { @@ -259,14 +251,18 @@ describe('AzureLogAnalyticsDatasource', () => { }); it('should return a logs 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)); + replace = (target?: string) => { + if (target === '$var') { + return 'template-variable'; + } + return target || ''; + }; + ctx = createContext(); const query = createMockQuery(); const azureLogAnalytics: Partial = {}; - azureLogAnalytics.query = '$query'; - azureLogAnalytics.workspace = '$workspace'; - azureLogAnalytics.resources = ['$resource']; + azureLogAnalytics.query = '$var'; + azureLogAnalytics.workspace = '$var'; + azureLogAnalytics.resources = ['$var']; query.queryType = AzureQueryType.LogAnalytics; query.azureLogAnalytics = { ...query.azureLogAnalytics, @@ -275,15 +271,15 @@ describe('AzureLogAnalyticsDatasource', () => { const templatedQuery = ctx.datasource.interpolateVariablesInQueries([query], {}); expect(templatedQuery[0]).toHaveProperty('datasource'); expect(templatedQuery[0].azureLogAnalytics).toMatchObject({ - query: templateVariables.get('query')?.templateVariable.current.value, - workspace: templateVariables.get('workspace')?.templateVariable.current.value, - resources: [templateVariables.get('resource')?.templateVariable.current.value], + query: 'template-variable', + workspace: 'template-variable', + resources: ['template-variable'], }); }); it('should return a logs query with multiple resources template variables replaced', () => { - const templateVariables = createTemplateVariables(['resource'], 'resource1,resource2'); - templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable)); + replace = () => 'resource1,resource2'; + ctx = createContext(); const query = createMockQuery(); const azureLogAnalytics: Partial = {}; azureLogAnalytics.resources = ['$resource']; @@ -300,16 +296,15 @@ describe('AzureLogAnalyticsDatasource', () => { }); it('should return a traces query with any template variables replaced', () => { - const templateableProps = ['resource', 'query', 'traceTypes', 'property', 'operation', 'filter', 'operationId']; - const templateVariables = createTemplateVariables(templateableProps); - templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable)); + replace = (target?: string) => (target === '$var' ? 'template-variable' : target || ''); + ctx = createContext(); const query = createMockQuery(); const azureTraces: Partial = {}; - azureTraces.resources = ['$resource']; - azureTraces.query = '$query'; - azureTraces.traceTypes = ['$traceTypes']; - azureTraces.filters = [{ filters: ['$filter'], operation: 'eq', property: '$property' }]; - azureTraces.operationId = '$operationId'; + azureTraces.resources = ['$var']; + azureTraces.query = '$var'; + azureTraces.traceTypes = ['$var']; + azureTraces.filters = [{ filters: ['$var'], operation: 'eq', property: '$var' }]; + azureTraces.operationId = '$var'; query.queryType = AzureQueryType.AzureTraces; query.azureTraces = { ...query.azureTraces, @@ -319,23 +314,23 @@ describe('AzureLogAnalyticsDatasource', () => { const templatedQuery = ctx.datasource.interpolateVariablesInQueries([query], {}); expect(templatedQuery[0]).toHaveProperty('datasource'); expect(templatedQuery[0].azureTraces).toMatchObject({ - query: templateVariables.get('query')?.templateVariable.current.value, - resources: [templateVariables.get('resource')?.templateVariable.current.value], - operationId: templateVariables.get('operationId')?.templateVariable.current.value, - traceTypes: [templateVariables.get('traceTypes')?.templateVariable.current.value], + query: 'template-variable', + resources: ['template-variable'], + operationId: 'template-variable', + traceTypes: ['template-variable'], filters: [ { - filters: [templateVariables.get('filter')?.templateVariable.current.value], + filters: ['template-variable'], operation: 'eq', - property: templateVariables.get('property')?.templateVariable.current.value, + property: 'template-variable', }, ], }); }); it('should return a trace query with multiple resources template variables replaced', () => { - const templateVariables = createTemplateVariables(['resource'], 'resource1,resource2'); - templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable)); + replace = () => 'resource1,resource2'; + ctx = createContext(); const query = createMockQuery(); const azureTraces: Partial = {}; azureTraces.resources = ['$resource']; diff --git a/public/app/plugins/datasource/azuremonitor/azure_log_analytics/azure_log_analytics_datasource.ts b/public/app/plugins/datasource/azuremonitor/azure_log_analytics/azure_log_analytics_datasource.ts index 009e8580245..827bd8ce1ba 100644 --- a/public/app/plugins/datasource/azuremonitor/azure_log_analytics/azure_log_analytics_datasource.ts +++ b/public/app/plugins/datasource/azuremonitor/azure_log_analytics/azure_log_analytics_datasource.ts @@ -1,8 +1,7 @@ import { map } from 'lodash'; import { DataSourceInstanceSettings, ScopedVars } from '@grafana/data'; -import { DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime'; -import { TimeSrv, getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; +import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime'; import ResponseParser from '../azure_monitor/response_parser'; import { getAuthType, getAzureCloud, getAzurePortalUrl } from '../credentials'; @@ -33,9 +32,10 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< azureMonitorPath: string; firstWorkspace?: string; - readonly timeSrv: TimeSrv = getTimeSrv(); - - constructor(private instanceSettings: DataSourceInstanceSettings) { + constructor( + private instanceSettings: DataSourceInstanceSettings, + private readonly templateSrv: TemplateSrv = getTemplateSrv() + ) { super(instanceSettings); this.resourcePath = `${routeNames.logAnalytics}`; @@ -85,7 +85,7 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< } private getWorkspaceList(subscription: string): Promise> { - const subscriptionId = getTemplateSrv().replace(subscription || this.defaultSubscriptionId); + const subscriptionId = this.templateSrv.replace(subscription || this.defaultSubscriptionId); const workspaceListUrl = this.azureMonitorPath + @@ -101,25 +101,23 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< } async getKustoSchema(resourceUri: string) { - const templateSrv = getTemplateSrv(); - const interpolatedUri = templateSrv.replace(resourceUri, {}, interpolateVariable); + const interpolatedUri = this.templateSrv.replace(resourceUri, {}, interpolateVariable); const metadata = await this.getMetadata(interpolatedUri); - return transformMetadataToKustoSchema(metadata, interpolatedUri, templateSrv.getVariables()); + return transformMetadataToKustoSchema(metadata, interpolatedUri, this.templateSrv.getVariables()); } applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): AzureMonitorQuery { let item; if (target.queryType === AzureQueryType.LogAnalytics && target.azureLogAnalytics) { item = target.azureLogAnalytics; - const templateSrv = getTemplateSrv(); const resources = this.expandResourcesForMultipleVariables(item.resources, scopedVars); - let workspace = templateSrv.replace(item.workspace, scopedVars); + let workspace = this.templateSrv.replace(item.workspace, scopedVars); if (!workspace && !resources && this.firstWorkspace) { workspace = this.firstWorkspace; } - const query = templateSrv.replace(item.query, scopedVars, interpolateVariable); + const query = this.templateSrv.replace(item.query, scopedVars, interpolateVariable); return { ...target, @@ -132,23 +130,22 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< // Workspace was removed in Grafana 8, but remains for backwards compat workspace, dashboardTime: item.dashboardTime, - timeColumn: templateSrv.replace(item.timeColumn, scopedVars), + timeColumn: this.templateSrv.replace(item.timeColumn, scopedVars), }, }; } if (target.queryType === AzureQueryType.AzureTraces && target.azureTraces) { item = target.azureTraces; - const templateSrv = getTemplateSrv(); const resources = this.expandResourcesForMultipleVariables(item.resources, scopedVars); - const query = templateSrv.replace(item.query, scopedVars, interpolateVariable); - const traceTypes = item.traceTypes?.map((t) => templateSrv.replace(t, scopedVars)); + const query = this.templateSrv.replace(item.query, scopedVars, interpolateVariable); + const traceTypes = item.traceTypes?.map((t) => this.templateSrv.replace(t, scopedVars)); const filters = (item.filters ?? []) .filter((f) => !!f.property) .map((f) => { - const filtersReplaced = f.filters?.map((filter) => templateSrv.replace(filter ?? '', scopedVars)); + const filtersReplaced = f.filters?.map((filter) => this.templateSrv.replace(filter ?? '', scopedVars)); return { - property: templateSrv.replace(f.property, scopedVars), + property: this.templateSrv.replace(f.property, scopedVars), operation: f.operation || 'eq', filters: filtersReplaced || [], }; @@ -162,7 +159,7 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< resultFormat: item.resultFormat, query, resources, - operationId: templateSrv.replace(target.azureTraces?.operationId, scopedVars), + operationId: this.templateSrv.replace(target.azureTraces?.operationId, scopedVars), filters, traceTypes, }, @@ -180,9 +177,8 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< return undefined; } const expandedResources: string[] = []; - const templateSrv = getTemplateSrv(); resources.forEach((r: string) => { - const tempVars = templateSrv.replace(r, scopedVars, 'raw'); + const tempVars = this.templateSrv.replace(r, scopedVars, 'raw'); const values = tempVars.split(','); values.forEach((value) => { expandedResources.push(value); diff --git a/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.test.ts b/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.test.ts index bfbc408e8c5..cac9970ddd5 100644 --- a/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.test.ts +++ b/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.test.ts @@ -1,20 +1,27 @@ import { 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 { multiVariable, singleVariable, subscriptionsVariable } from '../__mocks__/variables'; +import { multiVariable } from '../__mocks__/variables'; import AzureMonitorDatasource from '../datasource'; -import { AzureAPIResponse, AzureDataSourceJsonData, AzureQueryType, Location } from '../types'; +import { AzureAPIResponse, AzureDataSourceJsonData, Location } from '../types'; -const templateSrv = new TemplateSrv(); +let replace = () => ''; -jest.mock('@grafana/runtime', () => ({ - ...jest.requireActual('@grafana/runtime'), - getTemplateSrv: () => templateSrv, -})); +jest.mock('@grafana/runtime', () => { + return { + __esModule: true, + ...jest.requireActual('@grafana/runtime'), + getTemplateSrv: () => ({ + replace: replace, + getVariables: jest.fn(), + updateTimeRange: jest.fn(), + containsTemplate: jest.fn(), + }), + }; +}); interface TestContext { instanceSettings: DataSourceInstanceSettings; @@ -79,6 +86,11 @@ describe('AzureMonitorDatasource', () => { }); describe('applyTemplateVariables', () => { + beforeEach(() => { + replace = (target?: string) => target || ''; + ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings); + }); + it('should migrate metricDefinition to metricNamespace', () => { const query = createMockQuery({ azureMonitor: { @@ -99,15 +111,14 @@ describe('AzureMonitorDatasource', () => { const resourceGroup = 'cloud-datasources'; const metricNamespace = 'microsoft.insights/components'; const resourceName = 'AppInsightsTestData'; - templateSrv.init([ - { - id: 'resourceUri', - name: 'resourceUri', - current: { - value: `/subscriptions/${subscription}/resourceGroups/${resourceGroup}/providers/${metricNamespace}/${resourceName}`, - }, - }, - ]); + replace = (target?: string) => { + if (target?.includes('$resourceUri')) { + return `/subscriptions/${subscription}/resourceGroups/${resourceGroup}/providers/${metricNamespace}/${resourceName}`; + } + return target || ''; + }; + ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings); + const query = createMockQuery({ azureMonitor: { resourceUri: '$resourceUri', @@ -126,22 +137,16 @@ describe('AzureMonitorDatasource', () => { it('expand template variables in resource groups and names', () => { const resourceGroup = '$rg'; const resourceName = '$rn'; - templateSrv.init([ - { - id: 'rg', - name: 'rg', - current: { - value: `rg1,rg2`, - }, - }, - { - id: 'rn', - name: 'rn', - current: { - value: `rn1,rn2`, - }, - }, - ]); + replace = (target?: string) => { + if (target?.includes('$rg')) { + return 'rg1,rg2'; + } + if (target?.includes('$rn')) { + return 'rn1,rn2'; + } + return target || ''; + }; + ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings); const query = createMockQuery({ azureMonitor: { resources: [{ resourceGroup, resourceName }], @@ -162,15 +167,14 @@ describe('AzureMonitorDatasource', () => { it('expand template variables for a region', () => { const region = '$reg'; - templateSrv.init([ - { - id: 'reg', - name: 'reg', - current: { - value: `eastus`, - }, - }, - ]); + replace = (target?: string) => { + if (target?.includes('$reg')) { + return 'eastus'; + } + return target || ''; + }; + ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings); + const query = createMockQuery({ azureMonitor: { region, @@ -185,29 +189,20 @@ describe('AzureMonitorDatasource', () => { }); it('should migrate legacy properties before interpolation', () => { - templateSrv.init([ - { - id: 'resourcegroup', - name: 'resourcegroup', - current: { - value: `test-rg`, - }, - }, - { - id: 'resourcename', - name: 'resourcename', - current: { - value: `test-resource`, - }, - }, - { - id: 'metric', - name: 'metric', - current: { - value: `test-ns`, - }, - }, - ]); + replace = (target?: string) => { + if (target?.includes('$resourcegroup')) { + return 'test-rg'; + } + if (target?.includes('$resourcename')) { + return 'test-resource'; + } + if (target?.includes('$metric')) { + return 'test-ns'; + } + return target || ''; + }; + ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings); + const query = createMockQuery({ azureMonitor: { metricDefinition: '$metric', @@ -434,15 +429,23 @@ describe('AzureMonitorDatasource', () => { }); it('should replace a template variable for the metric name', () => { - templateSrv.init([ - { - id: 'metric', - name: 'metric', - current: { - value: 'UsedCapacity', - }, - }, - ]); + replace = (target?: string) => { + if (target?.includes('$metric')) { + return 'UsedCapacity'; + } + return target || ''; + }; + ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings); + ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => { + const basePath = 'azuremonitor/subscriptions/mock-subscription-id/resourceGroups/nodeapp'; + const expected = + basePath + + '/providers/microsoft.insights/components/resource1' + + '/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01'; + expect(path).toBe(expected); + return Promise.resolve(response); + }); + return ctx.ds.azureMonitorDatasource .getMetricMetadata({ resourceUri: @@ -460,7 +463,8 @@ describe('AzureMonitorDatasource', () => { describe('When performing interpolateVariablesInQueries for azure_monitor_metrics', () => { beforeEach(() => { - templateSrv.init([]); + replace = (target?: string) => target || ''; + ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings); }); it('should return a query unchanged if no template variables are provided', () => { @@ -481,7 +485,35 @@ describe('AzureMonitorDatasource', () => { 'dimensionFilters[0].filters[0]', ]; const templateVariables = createTemplateVariables(templateableProps); - templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable)); + replace = (target?: string) => { + if (target === '$resources0resourceGroup') { + return 'resources0resourceGroup-template-variable'; + } + if (target === '$resources0resourceName') { + return 'resources0resourceName-template-variable'; + } + if (target === '$metricNamespace') { + return 'metricNamespace-template-variable'; + } + if (target === '$timeGrain') { + return 'timeGrain-template-variable'; + } + if (target === '$aggregation') { + return 'aggregation-template-variable'; + } + if (target === '$top') { + return 'top-template-variable'; + } + if (target === '$dimensionFilters0dimension') { + return 'dimensionFilters0dimension-template-variable'; + } + if (target === '$dimensionFilters0filters0') { + return 'dimensionFilters0filters0-template-variable'; + } + return target || ''; + }; + ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings); + const query = createMockQuery(); const azureMonitorQuery = {}; for (const [path, templateVariable] of templateVariables.entries()) { @@ -712,12 +744,14 @@ describe('AzureMonitorDatasource', () => { }); it('should return multiple resources from a template variable', () => { - const tsrv = new TemplateSrv(); - tsrv.replace = jest - .fn() - .mockImplementation((value: string) => (value === `$${multiVariable.id}` ? 'foo,bar' : value ?? '')); - const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv); - ds.azureMonitorDatasource.templateSrv = tsrv; + replace = (target?: string) => { + if (target?.includes('$reg')) { + return 'eastus'; + } + return target === `$${multiVariable.id}` ? 'foo,bar' : target ?? ''; + }; + const ds = new AzureMonitorDatasource(ctx.instanceSettings); + //ds.azureMonitorDatasource.templateSrv = tsrv; ds.azureMonitorDatasource.getResource = jest .fn() .mockImplementationOnce((path: string) => { @@ -1074,36 +1108,6 @@ describe('AzureMonitorDatasource', () => { }); }); - describe('When performing targetContainsTemplate', () => { - it('should return false when no variable is being used', () => { - const query = createMockQuery(); - query.queryType = AzureQueryType.AzureMonitor; - expect(ctx.ds.targetContainsTemplate(query)).toEqual(false); - }); - - it('should return true when subscriptions field is using a variable', () => { - const query = createMockQuery(); - const templateSrv = new TemplateSrv(); - templateSrv.init([subscriptionsVariable]); - - const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv); - query.queryType = AzureQueryType.AzureMonitor; - query.subscription = `$${subscriptionsVariable.name}`; - expect(ds.targetContainsTemplate(query)).toEqual(true); - }); - - it('should return false when a variable is used in a different part of the query', () => { - const query = createMockQuery(); - const templateSrv = new TemplateSrv(); - templateSrv.init([singleVariable]); - - const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv); - query.queryType = AzureQueryType.AzureMonitor; - query.azureLogAnalytics = { resources: [`$${singleVariable.name}`] }; - expect(ds.targetContainsTemplate(query)).toEqual(false); - }); - }); - it('should return an empty array for a Metric that does not have dimensions', () => { return ctx.ds.azureMonitorDatasource .getMetricMetadata({ diff --git a/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.ts b/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.ts index 72bbd2acc9c..4a1f5f61bfa 100644 --- a/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.ts +++ b/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.ts @@ -3,7 +3,6 @@ import { find, startsWith } from 'lodash'; import { DataSourceInstanceSettings, ScopedVars } from '@grafana/data'; import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime'; -import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { getAuthType, getAzureCloud, getAzurePortalUrl } from '../credentials'; import TimegrainConverter from '../time_grain_converter'; @@ -49,14 +48,13 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend) { + constructor( + private instanceSettings: DataSourceInstanceSettings, + private readonly templateSrv: TemplateSrv = getTemplateSrv() + ) { super(instanceSettings); - this.timeSrv = getTimeSrv(); - this.templateSrv = getTemplateSrv(); this.defaultSubscriptionId = instanceSettings.jsonData.subscriptionId; const cloud = getAzureCloud(instanceSettings); @@ -92,14 +90,12 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend this.replaceTemplateVariables(r, scopedVars)).flat(); - const metricNamespace = templateSrv.replace(migratedQuery.metricNamespace, scopedVars); - const customNamespace = templateSrv.replace(migratedQuery.customNamespace, scopedVars); - const timeGrain = templateSrv.replace((migratedQuery.timeGrain || '').toString(), scopedVars); - const aggregation = templateSrv.replace(migratedQuery.aggregation, scopedVars); - const top = templateSrv.replace(migratedQuery.top || '', scopedVars); + const metricNamespace = this.templateSrv.replace(migratedQuery.metricNamespace, scopedVars); + const customNamespace = this.templateSrv.replace(migratedQuery.customNamespace, scopedVars); + const timeGrain = this.templateSrv.replace((migratedQuery.timeGrain || '').toString(), scopedVars); + const aggregation = this.templateSrv.replace(migratedQuery.aggregation, scopedVars); + const top = this.templateSrv.replace(migratedQuery.top || '', scopedVars); const dimensionFilters = (migratedQuery.dimensionFilters ?? []) .filter((f) => f.dimension && f.dimension !== 'None') .map((f) => { - const filters = f.filters?.map((filter) => templateSrv.replace(filter ?? '', scopedVars)); + const filters = f.filters?.map((filter) => this.templateSrv.replace(filter ?? '', scopedVars)); return { - dimension: templateSrv.replace(f.dimension, scopedVars), + dimension: this.templateSrv.replace(f.dimension, scopedVars), operator: f.operator || 'eq', filters: filters || [], }; @@ -143,8 +142,8 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend [subscriptionsVariable, singleVariable, multiVariable], - getVariableWithName: jest.fn(), - getFilteredVariables: jest.fn(), +let getTempVars = () => [] as CustomVariableModel[]; +let replace = () => ''; + +jest.mock('@grafana/runtime', () => { + return { + __esModule: true, + ...jest.requireActual('@grafana/runtime'), + getTemplateSrv: () => ({ + replace: replace, + getVariables: getTempVars, + updateTimeRange: jest.fn(), + containsTemplate: jest.fn(), + }), + }; }); -jest.mock('app/core/services/backend_srv'); -jest.mock('@grafana/runtime', () => ({ - ...jest.requireActual('@grafana/runtime'), - getBackendSrv: () => backendSrv, - getTemplateSrv: () => templateSrv, -})); - describe('AzureResourceGraphDatasource', () => { - const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest'); - - beforeEach(() => { - jest.clearAllMocks(); - datasourceRequestMock.mockImplementation(jest.fn()); - }); - let ctx: Context; - beforeEach(() => { - ctx = createContext({ - instanceSettings: { - url: 'http://azureresourcegraphapi', - jsonData: { subscriptionId: '9935389e-9122-4ef9-95f9-1513dd24753f', cloudName: 'azuremonitor' }, - }, - }); - }); - describe('When performing interpolateVariablesInQueries for azure_resource_graph', () => { beforeEach(() => { - templateSrv.init([]); + ctx = createContext({ + instanceSettings: { + url: 'http://azureresourcegraphapi', + jsonData: { subscriptionId: '9935389e-9122-4ef9-95f9-1513dd24753f', cloudName: 'azuremonitor' }, + }, + }); + getTempVars = () => [] as CustomVariableModel[]; + replace = (target?: string) => target || ''; }); it('should return a query unchanged if no template variables are provided', () => { const query = createMockQuery(); query.queryType = AzureQueryType.AzureResourceGraph; - const templatedQuery = ctx.datasource.azureResourceGraphDatasource.interpolateVariablesInQueries([query], {}); - expect(templatedQuery[0]).toEqual(query); + const templatedQuery = ctx.datasource.interpolateVariablesInQueries([query], {}); + expect(templatedQuery).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)); + replace = () => 'query-template-variable'; const query = createMockQuery(); const azureResourceGraph = {}; for (const [path, templateVariable] of templateVariables.entries()) { @@ -69,7 +61,7 @@ describe('AzureResourceGraphDatasource', () => { ...query.azureResourceGraph, ...azureResourceGraph, }; - const templatedQuery = ctx.datasource.azureResourceGraphDatasource.interpolateVariablesInQueries([query], {}); + const templatedQuery = ctx.datasource.interpolateVariablesInQueries([query], {}); expect(templatedQuery[0]).toHaveProperty('datasource'); for (const [path, templateVariable] of templateVariables.entries()) { expect(get(templatedQuery[0].azureResourceGraph, path)).toEqual( @@ -81,7 +73,8 @@ describe('AzureResourceGraphDatasource', () => { describe('When applying template variables', () => { beforeEach(() => { - templateSrv.init([subscriptionsVariable, singleVariable, multiVariable]); + getTempVars = () => [] as CustomVariableModel[]; + replace = (target?: string) => target || ''; }); it('should expand single value template variable', () => { @@ -92,6 +85,10 @@ describe('AzureResourceGraphDatasource', () => { resultFormat: '', }, }); + getTempVars = () => + Array.from([subscriptionsVariable, singleVariable, multiVariable].values()).map((item) => item); + replace = (target?: string | undefined) => + target === 'Resources | $var1' ? 'Resources | var1-foo' : target || ''; expect(ctx.datasource.azureResourceGraphDatasource.applyTemplateVariables(target, {})).toEqual( expect.objectContaining({ ...target, @@ -110,6 +107,14 @@ describe('AzureResourceGraphDatasource', () => { resultFormat: '', }, }); + getTempVars = () => + Array.from([subscriptionsVariable, singleVariable, multiVariable].values()).map((item) => item); + replace = (target?: string | undefined) => { + if (target === 'resources | where $__contains(name, $var3)') { + return "resources | where $__contains(name, 'var3-foo','var3-baz')"; + } + return target || ''; + }; expect(ctx.datasource.azureResourceGraphDatasource.applyTemplateVariables(target, {})).toEqual( expect.objectContaining({ ...target, @@ -128,14 +133,16 @@ describe('AzureResourceGraphDatasource', () => { const target = createMockQuery({ subscriptions: ['$subs'], azureResourceGraph: { - query: 'resources | where $__contains(name, $var3)', + query: 'resources | where $__contains(name)', resultFormat: '', }, }); + getTempVars = () => Array.from([subscriptionsVariable, singleVariable, multiVariable].values()).map((item) => item); + replace = (target?: string | undefined) => (target === '$subs' ? 'sub-foo,sub-baz' : target || ''); expect(ctx.datasource.azureResourceGraphDatasource.applyTemplateVariables(target, {})).toEqual( expect.objectContaining({ azureResourceGraph: { - query: `resources | where $__contains(name, 'var3-foo','var3-baz')`, + query: `resources | where $__contains(name)`, resultFormat: 'table', }, queryType: 'Azure Resource Graph', @@ -143,47 +150,4 @@ describe('AzureResourceGraphDatasource', () => { }) ); }); - - describe('When performing targetContainsTemplate', () => { - it('should return false when no variable is being used', () => { - const query = createMockQuery(); - const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv); - query.queryType = AzureQueryType.AzureResourceGraph; - expect(ds.targetContainsTemplate(query)).toEqual(false); - }); - - it('should return true when resource field is using a variable', () => { - const query = createMockQuery(); - const templateSrv = new TemplateSrv(); - templateSrv.init([singleVariable]); - - const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv); - query.queryType = AzureQueryType.AzureResourceGraph; - query.azureResourceGraph = { query: `$${singleVariable.name}` }; - expect(ds.targetContainsTemplate(query)).toEqual(true); - }); - - it('should return true when resource field is using a variable in the subscriptions field', () => { - const query = createMockQuery(); - const templateSrv = new TemplateSrv(); - templateSrv.init([multiVariable]); - - const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv); - query.queryType = AzureQueryType.AzureResourceGraph; - query.subscriptions = [multiVariable.name]; - query.azureResourceGraph = { query: `$${multiVariable.name}` }; - expect(ds.targetContainsTemplate(query)).toEqual(true); - }); - - it('should return false when a variable is used in a different part of the query', () => { - const query = createMockQuery(); - const templateSrv = new TemplateSrv(); - templateSrv.init([singleVariable]); - - const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv); - query.queryType = AzureQueryType.AzureResourceGraph; - query.azureMonitor = { metricName: `$${singleVariable.name}` }; - expect(ds.targetContainsTemplate(query)).toEqual(false); - }); - }); }); diff --git a/public/app/plugins/datasource/azuremonitor/azure_resource_graph/azure_resource_graph_datasource.ts b/public/app/plugins/datasource/azuremonitor/azure_resource_graph/azure_resource_graph_datasource.ts index 4eebe3b35cb..93e9b497853 100644 --- a/public/app/plugins/datasource/azuremonitor/azure_resource_graph/azure_resource_graph_datasource.ts +++ b/public/app/plugins/datasource/azuremonitor/azure_resource_graph/azure_resource_graph_datasource.ts @@ -16,15 +16,14 @@ export default class AzureResourceGraphDatasource extends DataSourceWithBackend< } applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): AzureMonitorQuery { + const ts = getTemplateSrv(); const item = target.azureResourceGraph; if (!item) { return target; } - - const templateSrv = getTemplateSrv(); - const variableNames = templateSrv.getVariables().map((v) => `$${v.name}`); + const variableNames = ts.getVariables().map((v) => `$${v.name}`); const subscriptionVar = _.find(target.subscriptions, (sub) => _.includes(variableNames, sub)); - const interpolatedSubscriptions = templateSrv + const interpolatedSubscriptions = ts .replace(subscriptionVar, scopedVars, (v: string[] | string) => v) .split(',') .filter((v) => v.length > 0); @@ -32,7 +31,7 @@ export default class AzureResourceGraphDatasource extends DataSourceWithBackend< ...interpolatedSubscriptions, ..._.filter(target.subscriptions, (sub) => !_.includes(variableNames, sub)), ]; - const query = templateSrv.replace(item.query, scopedVars, interpolateVariable); + const query = ts.replace(item.query, scopedVars, interpolateVariable); return { ...target, diff --git a/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.tsx b/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.tsx index 9ac7201c0c7..22056cceed0 100644 --- a/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.tsx @@ -34,6 +34,7 @@ const QueryEditor = ({ onChange, onRunQuery: baseOnRunQuery, data, + range, }: AzureMonitorQueryEditorProps) => { const [errorMessage, setError] = useLastError(); const onRunQuery = useMemo(() => debounce(baseOnRunQuery, 500), [baseOnRunQuery]); @@ -66,6 +67,7 @@ const QueryEditor = ({ onChange={onQueryChange} variableOptionGroup={variableOptionGroup} setError={setError} + range={range} /> {errorMessage && ( @@ -94,6 +96,7 @@ const EditorForQueryType = ({ variableOptionGroup, onChange, setError, + range, }: EditorForQueryTypeProps) => { switch (query.queryType) { case AzureQueryType.AzureMonitor: @@ -141,6 +144,7 @@ const EditorForQueryType = ({ onChange={onChange} variableOptionGroup={variableOptionGroup} setError={setError} + range={range} /> ); diff --git a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filter.tsx b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filter.tsx index fb807206218..3707f29a8ec 100644 --- a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filter.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filter.tsx @@ -2,7 +2,7 @@ import { cx } from '@emotion/css'; import React, { RefCallback, SyntheticEvent, useState } from 'react'; import { lastValueFrom } from 'rxjs'; -import { CoreApp, DataFrame, SelectableValue, TimeRange } from '@grafana/data'; +import { CoreApp, DataFrame, getDefaultTimeRange, SelectableValue, TimeRange } from '@grafana/data'; import { AccessoryButton } from '@grafana/experimental'; import { HorizontalGroup, @@ -24,10 +24,10 @@ export interface FilterProps { datasource: Datasource; propertyMap: Map; setPropertyMap: React.Dispatch>>>>; - timeRange: TimeRange; queryTraceTypes: string[]; properties: string[]; variableOptionGroup: VariableOptionGroup; + range?: TimeRange; } const onFieldChange = ( @@ -50,11 +50,11 @@ const onFieldChange = ( const getTraceProperties = async ( query: AzureMonitorQuery, datasource: Datasource, - timeRange: TimeRange, traceTypes: string[], propertyMap: Map, setPropertyMap: React.Dispatch>>>>, - filter?: Partial + filter?: Partial, + range?: TimeRange ): Promise => { const { azureTraces } = query; if (!azureTraces) { @@ -97,7 +97,7 @@ const getTraceProperties = async ( queryType: AzureQueryType.LogAnalytics, }, ], - range: timeRange, + range: range || getDefaultTimeRange(), }) ); if (results.data.length > 0) { @@ -191,13 +191,13 @@ const Filter = ( datasource, propertyMap, setPropertyMap, - timeRange, queryTraceTypes, properties, item, onChange, onDelete, variableOptionGroup, + range, } = props; const [loading, setLoading] = useState(false); const [values, setValues] = useState | VariableOptionGroup>>( @@ -215,11 +215,11 @@ const Filter = ( const promise = await getTraceProperties( query, datasource, - timeRange, queryTraceTypes, propertyMap, setPropertyMap, - item + item, + range ); setValues(addValueToOptions(promise, variableOptionGroup)); setLoading(false); diff --git a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filters.test.tsx b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filters.test.tsx index 6ecfccbe464..37676d0ee1a 100644 --- a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filters.test.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filters.test.tsx @@ -16,6 +16,19 @@ import { AzureMonitorQuery } from '../../types'; import Filters from './Filters'; import { setFilters } from './setQueryValue'; +jest.mock('@grafana/runtime', () => { + return { + __esModule: true, + ...jest.requireActual('@grafana/runtime'), + getTemplateSrv: () => ({ + replace: jest.fn(), + getVariables: jest.fn(), + updateTimeRange: jest.fn(), + containsTemplate: jest.fn(), + }), + }; +}); + const variableOptionGroup = { label: 'Template variables', options: [], diff --git a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filters.tsx b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filters.tsx index 100567dcc66..64e7aa5fe85 100644 --- a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filters.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filters.tsx @@ -1,7 +1,7 @@ import { uniq } from 'lodash'; import React, { useEffect, useMemo, useState } from 'react'; -import { SelectableValue, TimeRange } from '@grafana/data'; +import { SelectableValue } from '@grafana/data'; import { EditorList } from '@grafana/experimental'; import { Field } from '@grafana/ui'; @@ -11,7 +11,7 @@ import { makeRenderItem } from './Filter'; import { tablesSchema } from './consts'; import { setFilters } from './setQueryValue'; -const Filters = ({ query, datasource, onQueryChange, variableOptionGroup }: AzureQueryEditorFieldProps) => { +const Filters = ({ query, datasource, onQueryChange, variableOptionGroup, range }: AzureQueryEditorFieldProps) => { const { azureTraces } = query; const queryTraceTypes = azureTraces?.traceTypes ? azureTraces.traceTypes : Object.keys(tablesSchema); @@ -34,23 +34,9 @@ const Filters = ({ query, datasource, onQueryChange, variableOptionGroup }: Azur const queryFilters = useMemo(() => query.azureTraces?.filters ?? [], [query.azureTraces?.filters]); const [filters, updateFilters] = useState(queryFilters); - const timeSrv = datasource.azureLogAnalyticsDatasource.timeSrv; - const [timeRange, setTimeRange] = useState(timeSrv.timeRange()); - - const useTime = (time: TimeRange) => { - if ( - timeRange !== null && - (timeRange.raw.from.toString() !== time.raw.from.toString() || - timeRange.raw.to.toString() !== time.raw.to.toString()) - ) { - setTimeRange({ ...time }); - } - }; - useTime(timeSrv.timeRange()); - useEffect(() => { setPropertyMap(new Map>>()); - }, [timeRange, query.azureTraces?.resources, query.azureTraces?.traceTypes, query.azureTraces?.operationId]); + }, [query.azureTraces?.resources, query.azureTraces?.traceTypes, query.azureTraces?.operationId]); const changedFunc = (changed: Array>) => { let updateQuery = false; @@ -82,10 +68,10 @@ const Filters = ({ query, datasource, onQueryChange, variableOptionGroup }: Azur datasource, propertyMap, setPropertyMap, - timeRange, queryTraceTypes, properties, variableOptionGroup, + range, })} /> diff --git a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/TracesQueryEditor.tsx b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/TracesQueryEditor.tsx index 129bc8cad80..2c295ece8e6 100644 --- a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/TracesQueryEditor.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/TracesQueryEditor.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { usePrevious } from 'react-use'; +import { TimeRange } from '@grafana/data'; import { EditorFieldGroup, EditorRow, EditorRows } from '@grafana/experimental'; import { Input } from '@grafana/ui'; @@ -25,6 +26,7 @@ interface TracesQueryEditorProps { onChange: (newQuery: AzureMonitorQuery) => void; variableOptionGroup: { label: string; options: AzureMonitorOption[] }; setError: (source: string, error: AzureMonitorErrorish | undefined) => void; + range?: TimeRange; } const TracesQueryEditor = ({ @@ -34,6 +36,7 @@ const TracesQueryEditor = ({ variableOptionGroup, onChange, setError, + range, }: TracesQueryEditorProps) => { const disableRow = (row: ResourceRow, selectedRows: ResourceRowGroup) => { if (selectedRows.length === 0) { @@ -102,6 +105,7 @@ const TracesQueryEditor = ({ )} selectionNotice={() => 'You may only choose items of the same resource type.'} + range={range} /> @@ -113,6 +117,7 @@ const TracesQueryEditor = ({ query={query} setError={setError} variableOptionGroup={variableOptionGroup} + range={range} /> @@ -152,6 +158,7 @@ const TracesQueryEditor = ({ defaultValue={ResultFormat.Table} setFormatAs={setFormatAs} resultFormat={query.azureTraces?.resultFormat} + range={range} /> diff --git a/public/app/plugins/datasource/azuremonitor/datasource.test.ts b/public/app/plugins/datasource/azuremonitor/datasource.test.ts index 6c4713cd651..7f41cc4b745 100644 --- a/public/app/plugins/datasource/azuremonitor/datasource.test.ts +++ b/public/app/plugins/datasource/azuremonitor/datasource.test.ts @@ -1,6 +1,28 @@ import { createMockInstanceSetttings } from './__mocks__/instanceSettings'; import createMockQuery from './__mocks__/query'; +import { singleVariable } from './__mocks__/variables'; import Datasource from './datasource'; +import { AzureQueryType } from './types'; + +jest.mock('@grafana/runtime', () => { + return { + __esModule: true, + ...jest.requireActual('@grafana/runtime'), + getTemplateSrv: () => ({ + replace: (target?: string) => { + if (target === '$resourceGroup') { + return 'the-resource-group'; + } + return target || ''; + }, + getVariables: jest.fn(), + updateTimeRange: jest.fn(), + containsTemplate: (target?: string) => { + return (target || '').includes('$'); + }, + }), + }; +}); describe('Azure Monitor Datasource', () => { describe('interpolateVariablesInQueries()', () => { @@ -49,4 +71,45 @@ describe('Azure Monitor Datasource', () => { delete query.queryType; expect(ds.filterQuery(query)).toBe(false); }); + + describe('When performing targetContainsTemplate', () => { + it('should return false when no variable is being used', () => { + const query = { + ...createMockQuery(), + queryType: AzureQueryType.AzureResourceGraph, + }; + const ds = new Datasource(createMockInstanceSetttings()); + expect(ds.targetContainsTemplate(query)).toEqual(false); + }); + + it('should return true when resource field is using a variable', () => { + const query = { + ...createMockQuery(), + queryType: AzureQueryType.AzureResourceGraph, + azureResourceGraph: { query: '$temp-var' }, + }; + const ds = new Datasource(createMockInstanceSetttings()); + expect(ds.targetContainsTemplate(query)).toEqual(true); + }); + + it('should return true when resource field is using a variable in the subscriptions field', () => { + const query = { + ...createMockQuery(), + queryType: AzureQueryType.AzureResourceGraph, + subscriptions: ['$temp-var'], + }; + const ds = new Datasource(createMockInstanceSetttings()); + expect(ds.targetContainsTemplate(query)).toEqual(true); + }); + + it('should return false when a variable is used in a different part of the query', () => { + const query = { + ...createMockQuery(), + queryType: AzureQueryType.AzureResourceGraph, + azureMonitor: { metricName: `$${singleVariable.name}` }, + }; + const ds = new Datasource(createMockInstanceSetttings()); + expect(ds.targetContainsTemplate(query)).toEqual(false); + }); + }); }); diff --git a/public/app/plugins/datasource/azuremonitor/datasource.ts b/public/app/plugins/datasource/azuremonitor/datasource.ts index dfc1aeb9476..f29bf2e973b 100644 --- a/public/app/plugins/datasource/azuremonitor/datasource.ts +++ b/public/app/plugins/datasource/azuremonitor/datasource.ts @@ -10,8 +10,7 @@ import { LoadingState, ScopedVars, } from '@grafana/data'; -import { DataSourceWithBackend } from '@grafana/runtime'; -import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; +import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime'; import AzureLogAnalyticsDatasource from './azure_log_analytics/azure_log_analytics_datasource'; import AzureMonitorDatasource from './azure_monitor/azure_monitor_datasource'; diff --git a/public/app/plugins/datasource/azuremonitor/types/types.ts b/public/app/plugins/datasource/azuremonitor/types/types.ts index de4fd6b0446..85cfca2caaa 100644 --- a/public/app/plugins/datasource/azuremonitor/types/types.ts +++ b/public/app/plugins/datasource/azuremonitor/types/types.ts @@ -6,6 +6,7 @@ import { DataSourceSettings, PanelData, SelectableValue, + TimeRange, } from '@grafana/data'; import Datasource from '../datasource'; @@ -142,6 +143,7 @@ export interface AzureQueryEditorFieldProps { subscriptionId?: string; variableOptionGroup: VariableOptionGroup; schema?: EngineSchema; + range?: TimeRange; onQueryChange: (newQuery: AzureMonitorQuery) => void; setError: (source: string, error: AzureMonitorErrorish | undefined) => void; diff --git a/public/app/plugins/datasource/azuremonitor/utils/common.ts b/public/app/plugins/datasource/azuremonitor/utils/common.ts index bc6d284bfb6..e54018d9f3c 100644 --- a/public/app/plugins/datasource/azuremonitor/utils/common.ts +++ b/public/app/plugins/datasource/azuremonitor/utils/common.ts @@ -1,7 +1,6 @@ import { map } from 'lodash'; -import { SelectableValue } from '@grafana/data'; -import { VariableWithMultiSupport } from 'app/features/variables/types'; +import { SelectableValue, VariableWithMultiSupport } from '@grafana/data'; import { AzureMonitorOption, VariableOptionGroup } from '../types'; diff --git a/public/app/plugins/datasource/azuremonitor/variables.ts b/public/app/plugins/datasource/azuremonitor/variables.ts index 54fbf99d5e6..0fdfc779e09 100644 --- a/public/app/plugins/datasource/azuremonitor/variables.ts +++ b/public/app/plugins/datasource/azuremonitor/variables.ts @@ -7,7 +7,7 @@ import { MetricFindValue, toDataFrame, } from '@grafana/data'; -import { getTemplateSrv } from '@grafana/runtime'; +import { getTemplateSrv, TemplateSrv } from '@grafana/runtime'; import VariableEditor from './components/VariableEditor/VariableEditor'; import DataSource from './datasource'; @@ -17,12 +17,12 @@ import { GrafanaTemplateVariableQuery } from './types/templateVariables'; import messageFromError from './utils/messageFromError'; export class VariableSupport extends CustomVariableSupport { - templateSrv = getTemplateSrv(); - - constructor(private readonly datasource: DataSource) { + constructor( + private readonly datasource: DataSource, + private readonly templateSrv: TemplateSrv = getTemplateSrv() + ) { super(); this.datasource = datasource; - this.templateSrv = getTemplateSrv(); } editor = VariableEditor; @@ -169,6 +169,6 @@ export class VariableSupport extends CustomVariableSupport