From 01c22ee366cb1ea2225da6bc14203d3084ea430a Mon Sep 17 00:00:00 2001 From: Kevin Yu Date: Wed, 20 Apr 2022 05:00:39 -0700 Subject: [PATCH] Azure Monitor: Migrate metrics queries to use a resource URI (#47696) * migrate old query to new query * remove new prefixed data fetchers * fix lint * add test cases for v7 and v8 query versions * add mock ids in test * add clarifying comment * add types for azure monitor api data fetchers * add additional comment to explain the types --- .../__mocks__/datasource.ts | 14 +- .../azure_monitor_datasource.test.ts | 129 +++++++------ .../azure_monitor/azure_monitor_datasource.ts | 102 +++------- .../azure_monitor/url_builder.test.ts | 180 ++++++++---------- .../azure_monitor/url_builder.ts | 61 +++--- .../MetricsQueryEditor.test.tsx | 2 +- .../MetricsQueryEditor/dataHooks.test.ts | 4 +- .../MetricsQueryEditor/dataHooks.ts | 17 +- .../MetricsQueryEditor.test.tsx | 2 +- .../NewMetricsQueryEditor/dataHooks.test.ts | 4 +- .../NewMetricsQueryEditor/dataHooks.ts | 6 +- .../datasource.ts | 43 ----- .../types/types.ts | 47 +++++ .../utils/migrateQuery.test.ts | 81 +++++++- .../utils/migrateQuery.ts | 31 ++- .../variables.test.ts | 20 +- .../variables.ts | 15 +- 17 files changed, 395 insertions(+), 363 deletions(-) diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/datasource.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/datasource.ts index 1793aa8a397..b8a37e18fc2 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/datasource.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/datasource.ts @@ -16,9 +16,9 @@ export default function createMockDatasource(overrides?: DeepPartial }, getSubscriptions: jest.fn().mockResolvedValueOnce([]), defaultSubscriptionId: 'subscriptionId', - newGetMetricNamespaces: jest.fn().mockResolvedValueOnce([]), - newGetMetricNames: jest.fn().mockResolvedValueOnce([]), - newGetMetricMetadata: jest.fn().mockResolvedValueOnce({ + getMetricNamespaces: jest.fn().mockResolvedValueOnce([]), + getMetricNames: jest.fn().mockResolvedValueOnce([]), + getMetricMetadata: jest.fn().mockResolvedValueOnce({ primaryAggType: 'Average', supportedAggTypes: ['Average', 'Maximum', 'Minimum'], supportedTimeGrains: [], @@ -31,14 +31,6 @@ export default function createMockDatasource(overrides?: DeepPartial getResourceGroups: jest.fn().mockResolvedValueOnce([]), getMetricDefinitions: jest.fn().mockResolvedValueOnce([]), getResourceNames: jest.fn().mockResolvedValueOnce([]), - getMetricNamespaces: jest.fn().mockResolvedValueOnce([]), - getMetricNames: jest.fn().mockResolvedValueOnce([]), - getMetricMetadata: jest.fn().mockResolvedValueOnce({ - primaryAggType: 'Average', - supportedAggTypes: ['Average', 'Maximum', 'Minimum'], - supportedTimeGrains: [], - dimensions: [], - }), azureLogAnalyticsDatasource: { getKustoSchema: () => Promise.resolve(), 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 a6cbb496067..96e1baa2cd9 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 @@ -26,7 +26,7 @@ describe('AzureMonitorDatasource', () => { ctx.instanceSettings = { name: 'test', url: 'http://azuremonitor.com', - jsonData: { subscriptionId: '9935389e-9122-4ef9-95f9-1513dd24753f', cloudName: 'azuremonitor' }, + jsonData: { subscriptionId: 'mock-subscription-id', cloudName: 'azuremonitor' }, } as unknown as DataSourceInstanceSettings; ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings); }); @@ -76,11 +76,11 @@ describe('AzureMonitorDatasource', () => { }); }); - describe('When performing newGetMetricNamespaces', () => { + describe('When performing getMetricNamespaces', () => { const response = { value: [ { - id: '/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp/providers/microsoft.insights/components/resource1/providers/microsoft.insights/metricNamespaces/Azure.ApplicationInsights', + id: '/subscriptions/mock-subscription-id/resourceGroups/nodeapp/providers/microsoft.insights/components/resource1/providers/microsoft.insights/metricNamespaces/Azure.ApplicationInsights', name: 'Azure.ApplicationInsights', type: 'Microsoft.Insights/metricNamespaces', classification: 'Custom', @@ -89,7 +89,7 @@ describe('AzureMonitorDatasource', () => { }, }, { - id: '/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp/providers/microsoft.insights/components/resource1/providers/microsoft.insights/metricNamespaces/microsoft.insights-components', + id: '/subscriptions/mock-subscription-id/resourceGroups/nodeapp/providers/microsoft.insights/components/resource1/providers/microsoft.insights/metricNamespaces/microsoft.insights-components', name: 'microsoft.insights-components', type: 'Microsoft.Insights/metricNamespaces', classification: 'Platform', @@ -102,7 +102,7 @@ describe('AzureMonitorDatasource', () => { beforeEach(() => { ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => { - const basePath = 'azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp'; + const basePath = 'azuremonitor/subscriptions/mock-subscription-id/resourceGroups/nodeapp'; const expected = basePath + '/providers/microsoft.insights/components/resource1' + @@ -114,9 +114,10 @@ describe('AzureMonitorDatasource', () => { it('should return list of Metric Namspaces', () => { return ctx.ds.azureMonitorDatasource - .newGetMetricNamespaces( - '/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp/providers/microsoft.insights/components/resource1' - ) + .getMetricNamespaces({ + resourceUri: + '/subscriptions/mock-subscription-id/resourceGroups/nodeapp/providers/microsoft.insights/components/resource1', + }) .then((results: Array<{ text: string; value: string }>) => { expect(results.length).toEqual(2); expect(results[0].text).toEqual('Azure.ApplicationInsights'); @@ -127,7 +128,7 @@ describe('AzureMonitorDatasource', () => { }); }); - describe('When performing newGetMetricNames', () => { + describe('When performing getMetricNames', () => { const response = { value: [ { @@ -165,7 +166,7 @@ describe('AzureMonitorDatasource', () => { beforeEach(() => { ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => { - const basePath = 'azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp'; + const basePath = 'azuremonitor/subscriptions/mock-subscription-id/resourceGroups/nodeapp'; const expected = basePath + '/providers/microsoft.insights/components/resource1' + @@ -177,10 +178,11 @@ describe('AzureMonitorDatasource', () => { it('should return list of Metric Definitions', () => { return ctx.ds.azureMonitorDatasource - .newGetMetricNames( - '/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp/providers/microsoft.insights/components/resource1', - 'default' - ) + .getMetricNames({ + resourceUri: + '/subscriptions/mock-subscription-id/resourceGroups/nodeapp/providers/microsoft.insights/components/resource1', + metricNamespace: 'default', + }) .then((results: Array<{ text: string; value: string }>) => { expect(results.length).toEqual(2); expect(results[0].text).toEqual('Used capacity'); @@ -191,7 +193,7 @@ describe('AzureMonitorDatasource', () => { }); }); - describe('When performing newGetMetricMetadata', () => { + describe('When performing getMetricMetadata', () => { const response = { value: [ { @@ -229,7 +231,7 @@ describe('AzureMonitorDatasource', () => { beforeEach(() => { ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => { - const basePath = 'azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp'; + const basePath = 'azuremonitor/subscriptions/mock-subscription-id/resourceGroups/nodeapp'; const expected = basePath + '/providers/microsoft.insights/components/resource1' + @@ -241,11 +243,12 @@ describe('AzureMonitorDatasource', () => { it('should return Aggregation metadata for a Metric', () => { return ctx.ds.azureMonitorDatasource - .newGetMetricMetadata( - '/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp/providers/microsoft.insights/components/resource1', - 'default', - 'UsedCapacity' - ) + .getMetricMetadata({ + resourceUri: + '/subscriptions/mock-subscription-id/resourceGroups/nodeapp/providers/microsoft.insights/components/resource1', + metricNamespace: 'default', + metricName: 'UsedCapacity', + }) .then((results) => { expect(results.primaryAggType).toEqual('Total'); expect(results.supportedAggTypes.length).toEqual(6); @@ -342,7 +345,7 @@ describe('AzureMonitorDatasource', () => { beforeEach(() => { ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => { - const basePath = 'azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups'; + const basePath = 'azuremonitor/subscriptions/mock-subscription-id/resourceGroups'; expect(path).toBe(basePath + '/nodesapp/resources?api-version=2021-04-01'); return Promise.resolve(response); }); @@ -350,7 +353,7 @@ describe('AzureMonitorDatasource', () => { it('should return list of Metric Definitions with no duplicates and no unsupported namespaces', () => { return ctx.ds - .getMetricDefinitions('9935389e-9122-4ef9-95f9-1513dd24753f', 'nodesapp') + .getMetricDefinitions('mock-subscription-id', 'nodesapp') .then((results: Array<{ text: string; value: string }>) => { expect(results.length).toEqual(7); expect(results[0].text).toEqual('Network interface'); @@ -372,12 +375,12 @@ describe('AzureMonitorDatasource', () => { }); describe('When performing getResourceNames', () => { - let subscription = '9935389e-9122-4ef9-95f9-1513dd24753f'; + let subscription = 'mock-subscription-id'; let resourceGroup = 'nodeapp'; let metricDefinition = 'microsoft.insights/components'; beforeEach(() => { - subscription = '9935389e-9122-4ef9-95f9-1513dd24753f'; + subscription = 'mock-subscription-id'; resourceGroup = 'nodeapp'; metricDefinition = 'microsoft.insights/components'; }); @@ -552,7 +555,7 @@ describe('AzureMonitorDatasource', () => { beforeEach(() => { ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => { - const basePath = 'azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp'; + const basePath = 'azuremonitor/subscriptions/mock-subscription-id/resourceGroups/nodeapp'; const expected = basePath + '/providers/microsoft.insights/components/resource1' + @@ -563,14 +566,14 @@ describe('AzureMonitorDatasource', () => { }); it('should return list of Metric Definitions', () => { - return ctx.ds - .getMetricNames( - '9935389e-9122-4ef9-95f9-1513dd24753f', - 'nodeapp', - 'microsoft.insights/components', - 'resource1', - 'default' - ) + return ctx.ds.azureMonitorDatasource + .getMetricNames({ + subscription: 'mock-subscription-id', + resourceGroup: 'nodeapp', + metricDefinition: 'microsoft.insights/components', + resourceName: 'resource1', + metricNamespace: 'default', + }) .then((results: Array<{ text: string; value: string }>) => { expect(results.length).toEqual(2); expect(results[0].text).toEqual('Used capacity'); @@ -619,7 +622,7 @@ describe('AzureMonitorDatasource', () => { beforeEach(() => { ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => { - const basePath = 'azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp'; + const basePath = 'azuremonitor/subscriptions/mock-subscription-id/resourceGroups/nodeapp'; const expected = basePath + '/providers/microsoft.insights/components/resource1' + @@ -630,15 +633,15 @@ describe('AzureMonitorDatasource', () => { }); it('should return Aggregation metadata for a Metric', () => { - return ctx.ds - .getMetricMetadata( - '9935389e-9122-4ef9-95f9-1513dd24753f', - 'nodeapp', - 'microsoft.insights/components', - 'resource1', - 'default', - 'UsedCapacity' - ) + return ctx.ds.azureMonitorDatasource + .getMetricMetadata({ + subscription: 'mock-subscription-id', + resourceGroup: 'nodeapp', + metricDefinition: 'microsoft.insights/components', + resourceName: 'resource1', + metricNamespace: 'default', + metricName: 'UsedCapacity', + }) .then((results) => { expect(results.primaryAggType).toEqual('Total'); expect(results.supportedAggTypes.length).toEqual(6); @@ -688,7 +691,7 @@ describe('AzureMonitorDatasource', () => { beforeEach(() => { ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => { - const basePath = 'azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp'; + const basePath = 'azuremonitor/subscriptions/mock-subscription-id/resourceGroups/nodeapp'; const expected = basePath + '/providers/microsoft.insights/components/resource1' + @@ -699,15 +702,15 @@ describe('AzureMonitorDatasource', () => { }); it('should return dimensions for a Metric that has dimensions', () => { - return ctx.ds - .getMetricMetadata( - '9935389e-9122-4ef9-95f9-1513dd24753f', - 'nodeapp', - 'microsoft.insights/components', - 'resource1', - 'default', - 'Transactions' - ) + return ctx.ds.azureMonitorDatasource + .getMetricMetadata({ + subscription: 'mock-subscription-id', + resourceGroup: 'nodeapp', + metricDefinition: 'microsoft.insights/components', + resourceName: 'resource1', + metricNamespace: 'default', + metricName: 'Transactions', + }) .then((results: any) => { expect(results.dimensions).toMatchInlineSnapshot(` Array [ @@ -759,15 +762,15 @@ describe('AzureMonitorDatasource', () => { }); it('should return an empty array for a Metric that does not have dimensions', () => { - return ctx.ds - .getMetricMetadata( - '9935389e-9122-4ef9-95f9-1513dd24753f', - 'nodeapp', - 'microsoft.insights/components', - 'resource1', - 'default', - 'FreeCapacity' - ) + return ctx.ds.azureMonitorDatasource + .getMetricMetadata({ + subscription: 'mock-subscription-id', + resourceGroup: 'nodeapp', + metricDefinition: 'microsoft.insights/components', + resourceName: 'resource1', + metricNamespace: 'default', + metricName: 'FreeCapacity', + }) .then((results: any) => { expect(results.dimensions.length).toEqual(0); }); 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 b302061d483..8e257114e0b 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 @@ -16,6 +16,9 @@ import { AzureMonitorResourceGroupsResponse, AzureQueryType, DatasourceValidationResult, + GetMetricNamespacesQuery, + GetMetricNamesQuery, + GetMetricMetadataQuery, } from '../types'; import { routeNames } from '../utils/common'; import ResponseParser from './response_parser'; @@ -236,102 +239,35 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend { - return ResponseParser.parseResponseValues(result, 'name', 'properties.metricNamespaceName'); - }); - } - - newGetMetricNamespaces(resourceUri: string) { - const templateSrv = getTemplateSrv(); - const url = UrlBuilder.newBuildAzureMonitorGetMetricNamespacesUrl( - this.resourcePath, - templateSrv.replace(resourceUri), - this.apiPreviewVersion - ); - return this.getResource(url).then((result: AzureMonitorMetricNamespacesResponse) => { return ResponseParser.parseResponseValues(result, 'name', 'properties.metricNamespaceName'); }); } - getMetricNames( - subscriptionId: string, - resourceGroup: string, - metricDefinition: string, - resourceName: string, - metricNamespace: string - ) { + getMetricNames(query: GetMetricNamesQuery) { const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl( this.resourcePath, - subscriptionId, - resourceGroup, - metricDefinition, - resourceName, - metricNamespace, - this.apiVersion + this.apiVersion, + this.replaceTemplateVariables(query) ); - - return this.getResource(url).then((result: any) => { - return ResponseParser.parseResponseValues(result, 'name.localizedValue', 'name.value'); - }); - } - - newGetMetricNames(resourceUri: string, metricNamespace: string) { - const templateSrv = getTemplateSrv(); - const url = UrlBuilder.newBuildAzureMonitorGetMetricNamesUrl( - this.resourcePath, - templateSrv.replace(resourceUri), - templateSrv.replace(metricNamespace), - this.apiVersion - ); - return this.getResource(url).then((result: AzureMonitorMetricNamesResponse) => { return ResponseParser.parseResponseValues(result, 'name.localizedValue', 'name.value'); }); } - getMetricMetadata( - subscriptionId: string, - resourceGroup: string, - metricDefinition: string, - resourceName: string, - metricNamespace: string, - metricName: string - ) { + getMetricMetadata(query: GetMetricMetadataQuery) { + const { metricName } = query; const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl( this.resourcePath, - subscriptionId, - resourceGroup, - metricDefinition, - resourceName, - metricNamespace, - this.apiVersion + this.apiVersion, + this.replaceTemplateVariables(query) ); - - return this.getResource(url).then((result: any) => { - return ResponseParser.parseMetadata(result, metricName); - }); - } - - newGetMetricMetadata(resourceUri: string, metricNamespace: string, metricName: string) { - const templateSrv = getTemplateSrv(); - const url = UrlBuilder.newBuildAzureMonitorGetMetricNamesUrl( - this.resourcePath, - templateSrv.replace(resourceUri), - templateSrv.replace(metricNamespace), - this.apiVersion - ); - return this.getResource(url).then((result: AzureMonitorMetricsMetadataResponse) => { return ResponseParser.parseMetadata(result, metricName); }); @@ -398,4 +334,16 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend 0; } + + private replaceTemplateVariables(query: T) { + const templateSrv = getTemplateSrv(); + + const workingQuery: { [K in keyof T]: string } = { ...query }; + const keys = Object.keys(query) as Array; + keys.forEach((key) => { + workingQuery[key] = templateSrv.replace(workingQuery[key]); + }); + + return workingQuery; + } } diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.test.ts index 4aa8b984548..ed61968eed7 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.test.ts @@ -1,13 +1,19 @@ import UrlBuilder from './url_builder'; describe('AzureMonitorUrlBuilder', () => { + describe('buildResourceUri', () => { + it('builds a resource uri when the required properties are provided', () => { + expect(UrlBuilder.buildResourceUri('sub', 'group', 'Microsoft.NetApp/netAppAccounts', 'name')).toEqual( + '/subscriptions/sub/resourceGroups/group/providers/Microsoft.NetApp/netAppAccounts/name' + ); + }); + }); + describe('when a resource uri is provided', () => { it('builds a getMetricNamesnamespace url', () => { - const url = UrlBuilder.newBuildAzureMonitorGetMetricNamespacesUrl( - '', - '/subscriptions/sub/resource-uri/resource', - '2017-05-01-preview' - ); + const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl('', '2017-05-01-preview', { + resourceUri: '/subscriptions/sub/resource-uri/resource', + }); expect(url).toBe( '/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview' ); @@ -16,12 +22,10 @@ describe('AzureMonitorUrlBuilder', () => { describe('when a resource uri and metric namespace is provided', () => { it('builds a getMetricNames url', () => { - const url = UrlBuilder.newBuildAzureMonitorGetMetricNamesUrl( - '', - '/subscriptions/sub/resource-uri/resource', - 'Microsoft.Sql/servers', - '2017-05-01-preview' - ); + const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', { + resourceUri: '/subscriptions/sub/resource-uri/resource', + metricNamespace: 'Microsoft.Sql/servers', + }); expect(url).toBe( '/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=Microsoft.Sql%2Fservers' ); @@ -31,14 +35,12 @@ describe('AzureMonitorUrlBuilder', () => { describe('Legacy query object', () => { describe('when metric definition is Microsoft.NetApp/netAppAccounts/capacityPools/volumes', () => { it('should build the getMetricNamespaces url in the even longer format', () => { - const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl( - '', - 'sub1', - 'rg', - 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes', - 'rn1/rn2/rn3', - '2017-05-01-preview' - ); + const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl('', '2017-05-01-preview', { + subscription: 'sub1', + resourceGroup: 'rg', + metricDefinition: 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes', + resourceName: 'rn1/rn2/rn3', + }); expect(url).toBe( '/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.NetApp/netAppAccounts/rn1/capacityPools/rn2/volumes/rn3/' + 'providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview' @@ -48,14 +50,12 @@ describe('AzureMonitorUrlBuilder', () => { describe('when metric definition is Microsoft.Sql/servers/databases', () => { it('should build the getMetricNamespaces url in the longer format', () => { - const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl( - '', - 'sub1', - 'rg', - 'Microsoft.Sql/servers/databases', - 'rn1/rn2', - '2017-05-01-preview' - ); + const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl('', '2017-05-01-preview', { + subscription: 'sub1', + resourceGroup: 'rg', + metricDefinition: 'Microsoft.Sql/servers/databases', + resourceName: 'rn1/rn2', + }); expect(url).toBe( '/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Sql/servers/rn1/databases/rn2/' + 'providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview' @@ -65,14 +65,12 @@ describe('AzureMonitorUrlBuilder', () => { describe('when metric definition is Microsoft.Sql/servers', () => { it('should build the getMetricNamespaces url in the shorter format', () => { - const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl( - '', - 'sub1', - 'rg', - 'Microsoft.Sql/servers', - 'rn', - '2017-05-01-preview' - ); + const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl('', '2017-05-01-preview', { + subscription: 'sub1', + resourceGroup: 'rg', + metricDefinition: 'Microsoft.Sql/servers', + resourceName: 'rn', + }); expect(url).toBe( '/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Sql/servers/rn/' + 'providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview' @@ -82,15 +80,13 @@ describe('AzureMonitorUrlBuilder', () => { describe('when metric definition is Microsoft.NetApp/netAppAccounts/capacityPools/volumes and the metricNamespace is default', () => { it('should build the getMetricNames url in the even longer format', () => { - const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl( - '', - 'sub1', - 'rg', - 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes', - 'rn1/rn2/rn3', - 'default', - '2017-05-01-preview' - ); + const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', { + subscription: 'sub1', + resourceGroup: 'rg', + metricDefinition: 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes', + resourceName: 'rn1/rn2/rn3', + metricNamespace: 'default', + }); expect(url).toBe( '/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.NetApp/netAppAccounts/rn1/capacityPools/rn2/volumes/rn3/' + 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default' @@ -100,15 +96,13 @@ describe('AzureMonitorUrlBuilder', () => { describe('when metric definition is Microsoft.Sql/servers/databases and the metricNamespace is default', () => { it('should build the getMetricNames url in the longer format', () => { - const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl( - '', - 'sub1', - 'rg', - 'Microsoft.Sql/servers/databases', - 'rn1/rn2', - 'default', - '2017-05-01-preview' - ); + const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', { + subscription: 'sub1', + resourceGroup: 'rg', + metricDefinition: 'Microsoft.Sql/servers/databases', + resourceName: 'rn1/rn2', + metricNamespace: 'default', + }); expect(url).toBe( '/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Sql/servers/rn1/databases/rn2/' + 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default' @@ -118,15 +112,13 @@ describe('AzureMonitorUrlBuilder', () => { describe('when metric definition is Microsoft.Sql/servers and the metricNamespace is default', () => { it('should build the getMetricNames url in the shorter format', () => { - const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl( - '', - 'sub1', - 'rg', - 'Microsoft.Sql/servers', - 'rn', - 'default', - '2017-05-01-preview' - ); + const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', { + subscription: 'sub1', + resourceGroup: 'rg', + metricDefinition: 'Microsoft.Sql/servers', + resourceName: 'rn', + metricNamespace: 'default', + }); expect(url).toBe( '/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Sql/servers/rn/' + 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default' @@ -136,15 +128,13 @@ describe('AzureMonitorUrlBuilder', () => { describe('when metric definition is Microsoft.Storage/storageAccounts/blobServices and the metricNamespace is default', () => { it('should build the getMetricNames url in the longer format', () => { - const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl( - '', - 'sub1', - 'rg', - 'Microsoft.Storage/storageAccounts/blobServices', - 'rn1/default', - 'default', - '2017-05-01-preview' - ); + const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', { + subscription: 'sub1', + resourceGroup: 'rg', + metricDefinition: 'Microsoft.Storage/storageAccounts/blobServices', + resourceName: 'rn1/default', + metricNamespace: 'default', + }); expect(url).toBe( '/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/blobServices/default/' + 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default' @@ -154,15 +144,13 @@ describe('AzureMonitorUrlBuilder', () => { describe('when metric definition is Microsoft.Storage/storageAccounts/fileServices and the metricNamespace is default', () => { it('should build the getMetricNames url in the longer format', () => { - const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl( - '', - 'sub1', - 'rg', - 'Microsoft.Storage/storageAccounts/fileServices', - 'rn1/default', - 'default', - '2017-05-01-preview' - ); + const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', { + subscription: 'sub1', + resourceGroup: 'rg', + metricDefinition: 'Microsoft.Storage/storageAccounts/fileServices', + resourceName: 'rn1/default', + metricNamespace: 'default', + }); expect(url).toBe( '/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/fileServices/default/' + 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default' @@ -172,15 +160,13 @@ describe('AzureMonitorUrlBuilder', () => { describe('when metric definition is Microsoft.Storage/storageAccounts/tableServices and the metricNamespace is default', () => { it('should build the getMetricNames url in the longer format', () => { - const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl( - '', - 'sub1', - 'rg', - 'Microsoft.Storage/storageAccounts/tableServices', - 'rn1/default', - 'default', - '2017-05-01-preview' - ); + const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', { + subscription: 'sub1', + resourceGroup: 'rg', + metricDefinition: 'Microsoft.Storage/storageAccounts/tableServices', + resourceName: 'rn1/default', + metricNamespace: 'default', + }); expect(url).toBe( '/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/tableServices/default/' + 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default' @@ -190,15 +176,13 @@ describe('AzureMonitorUrlBuilder', () => { describe('when metric definition is Microsoft.Storage/storageAccounts/queueServices and the metricNamespace is default', () => { it('should build the getMetricNames url in the longer format', () => { - const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl( - '', - 'sub1', - 'rg', - 'Microsoft.Storage/storageAccounts/queueServices', - 'rn1/default', - 'default', - '2017-05-01-preview' - ); + const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', { + subscription: 'sub1', + resourceGroup: 'rg', + metricDefinition: 'Microsoft.Storage/storageAccounts/queueServices', + resourceName: 'rn1/default', + metricNamespace: 'default', + }); expect(url).toBe( '/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/queueServices/default/' + 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default' diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.ts index 1d18c89a09d..7684a543e07 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.ts @@ -1,58 +1,47 @@ +import { GetMetricNamespacesQuery, GetMetricNamesQuery } from '../types'; + export default class UrlBuilder { - static buildAzureMonitorGetMetricNamespacesUrl( - baseUrl: string, + static buildResourceUri( subscriptionId: string, resourceGroup: string, metricDefinition: string, - resourceName: string, - apiVersion: string + resourceName: string ) { const metricDefinitionArray = metricDefinition.split('/'); const resourceNameArray = resourceName.split('/'); const provider = metricDefinitionArray.shift(); - const urlArray = [baseUrl, 'subscriptions', subscriptionId, 'resourceGroups', resourceGroup, 'providers', provider]; + const urlArray = ['/subscriptions', subscriptionId, 'resourceGroups', resourceGroup, 'providers', provider]; for (const i in metricDefinitionArray) { urlArray.push(metricDefinitionArray[i]); urlArray.push(resourceNameArray[i]); } - const urlPrefix = urlArray.join('/'); - return `${urlPrefix}/providers/microsoft.insights/metricNamespaces?api-version=${apiVersion}`; + return urlArray.join('/'); } - static buildAzureMonitorGetMetricNamesUrl( - baseUrl: string, - subscriptionId: string, - resourceGroup: string, - metricDefinition: string, - resourceName: string, - metricNamespace: string, - apiVersion: string - ) { - const metricDefinitionArray = metricDefinition.split('/'); - const resourceNameArray = resourceName.split('/'); - const provider = metricDefinitionArray.shift(); - const urlArray = [baseUrl, 'subscriptions', subscriptionId, 'resourceGroups', resourceGroup, 'providers', provider]; - for (const i in metricDefinitionArray) { - urlArray.push(metricDefinitionArray[i]); - urlArray.push(resourceNameArray[i]); + static buildAzureMonitorGetMetricNamespacesUrl(baseUrl: string, apiVersion: string, query: GetMetricNamespacesQuery) { + let resourceUri: string; + + if ('resourceUri' in query) { + resourceUri = query.resourceUri; + } else { + const { subscription, resourceGroup, metricDefinition, resourceName } = query; + resourceUri = UrlBuilder.buildResourceUri(subscription, resourceGroup, metricDefinition, resourceName); } - const urlPrefix = urlArray.join('/'); - return ( - `${urlPrefix}/providers/microsoft.insights/metricdefinitions?api-version=${apiVersion}` + - `&metricnamespace=${encodeURIComponent(metricNamespace)}` - ); - } - static newBuildAzureMonitorGetMetricNamespacesUrl(baseUrl: string, resourceUri: string, apiVersion: string) { return `${baseUrl}${resourceUri}/providers/microsoft.insights/metricNamespaces?api-version=${apiVersion}`; } - static newBuildAzureMonitorGetMetricNamesUrl( - baseUrl: string, - resourceUri: string, - metricNamespace: string, - apiVersion: string - ) { + static buildAzureMonitorGetMetricNamesUrl(baseUrl: string, apiVersion: string, query: GetMetricNamesQuery) { + let resourceUri: string; + const { metricNamespace } = query; + + if ('resourceUri' in query) { + resourceUri = query.resourceUri; + } else { + const { subscription, resourceGroup, metricDefinition, resourceName } = query; + resourceUri = UrlBuilder.buildResourceUri(subscription, resourceGroup, metricDefinition, resourceName); + } + return ( `${baseUrl}${resourceUri}/providers/microsoft.insights/metricdefinitions?api-version=${apiVersion}` + `&metricnamespace=${encodeURIComponent(metricNamespace)}` diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx index 0d013ea943c..76f0c7e8f9c 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx @@ -79,7 +79,7 @@ describe('Azure Monitor QueryEditor', () => { const mockDatasource = createMockDatasource(); const onChange = jest.fn(); const mockQuery = createMockQuery(); - mockDatasource.getMetricNames = jest.fn().mockResolvedValue([ + mockDatasource.azureMonitorDatasource.getMetricNames = jest.fn().mockResolvedValue([ { value: 'metric-a', text: 'Metric A', diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/dataHooks.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/dataHooks.test.ts index 2b25f47ad2c..97a50e4bb6a 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/dataHooks.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/dataHooks.test.ts @@ -247,11 +247,11 @@ describe('AzureMonitor: metrics dataHooks', () => { .fn() .mockResolvedValue([opt('Web server', 'web-server'), opt('Job server', 'job-server')]); - datasource.getMetricNames = jest + datasource.azureMonitorDatasource.getMetricNames = jest .fn() .mockResolvedValue([opt('Percentage CPU', 'percentage-cpu'), opt('Free memory', 'free-memory')]); - datasource.getMetricNamespaces = jest + datasource.azureMonitorDatasource.getMetricNamespaces = jest .fn() .mockResolvedValue([opt('Compute Virtual Machine', 'azure/vmc'), opt('Database NS', 'azure/dbns')]); }); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/dataHooks.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/dataHooks.ts index c340e9929bb..76259355994 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/dataHooks.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/dataHooks.ts @@ -153,7 +153,12 @@ export const useMetricNamespaces: DataHook = (query, datasource, onChange, setEr return; } - const results = await datasource.getMetricNamespaces(subscription, resourceGroup, metricDefinition, resourceName); + const results = await datasource.azureMonitorDatasource.getMetricNamespaces({ + subscription, + resourceGroup, + metricDefinition, + resourceName, + }); const options = formatOptions(results, metricNamespace); // Do some cleanup of the query state if need be @@ -180,13 +185,13 @@ export const useMetricNames: DataHook = (query, datasource, onChange, setError) return; } - const results = await datasource.getMetricNames( + const results = await datasource.azureMonitorDatasource.getMetricNames({ subscription, resourceGroup, metricDefinition, resourceName, - metricNamespace - ); + metricNamespace, + }); const options = formatOptions(results, metricName); @@ -217,8 +222,8 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour return; } - datasource - .getMetricMetadata(subscription, resourceGroup, metricDefinition, resourceName, metricNamespace, metricName) + datasource.azureMonitorDatasource + .getMetricMetadata({ subscription, resourceGroup, metricDefinition, resourceName, metricNamespace, metricName }) .then((metadata) => { // TODO: Move the aggregationTypes and timeGrain defaults into `getMetricMetadata` const aggregations = (metadata.supportedAggTypes || [metadata.primaryAggType]).map((v) => ({ diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/MetricsQueryEditor.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/MetricsQueryEditor.test.tsx index 792d3b76123..6b7347e2d71 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/MetricsQueryEditor.test.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/MetricsQueryEditor.test.tsx @@ -160,7 +160,7 @@ describe('MetricsQueryEditor', () => { const mockDatasource = createMockDatasource({ resourcePickerData }); const onChange = jest.fn(); const mockQuery = createMockQuery(); - mockDatasource.azureMonitorDatasource.newGetMetricNames = jest.fn().mockResolvedValue([ + mockDatasource.azureMonitorDatasource.getMetricNames = jest.fn().mockResolvedValue([ { value: 'metric-a', text: 'Metric A', diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/dataHooks.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/dataHooks.test.ts index 7a784885c91..b50d71a24af 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/dataHooks.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/dataHooks.test.ts @@ -130,11 +130,11 @@ describe('AzureMonitor: metrics dataHooks', () => { .fn() .mockResolvedValue([opt('Web server', 'web-server'), opt('Job server', 'job-server')]); - datasource.azureMonitorDatasource.newGetMetricNames = jest + datasource.azureMonitorDatasource.getMetricNames = jest .fn() .mockResolvedValue([opt('Percentage CPU', 'percentage-cpu'), opt('Free memory', 'free-memory')]); - datasource.azureMonitorDatasource.newGetMetricNamespaces = jest + datasource.azureMonitorDatasource.getMetricNamespaces = jest .fn() .mockResolvedValue([opt('Compute Virtual Machine', 'azure/vmc'), opt('Database NS', 'azure/dbns')]); }); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/dataHooks.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/dataHooks.ts index a8c11ebd38b..e66a83b49e0 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/dataHooks.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/dataHooks.ts @@ -29,7 +29,7 @@ export const useMetricNamespaces: DataHook = (query, datasource, onChange, setEr return; } - const results = await datasource.azureMonitorDatasource.newGetMetricNamespaces(resourceUri); + const results = await datasource.azureMonitorDatasource.getMetricNamespaces({ resourceUri }); const options = formatOptions(results, metricNamespace); // Do some cleanup of the query state if need be @@ -55,7 +55,7 @@ export const useMetricNames: DataHook = (query, datasource, onChange, setError) return; } - const results = await datasource.azureMonitorDatasource.newGetMetricNames(resourceUri, metricNamespace); + const results = await datasource.azureMonitorDatasource.getMetricNames({ resourceUri, metricNamespace }); const options = formatOptions(results, metricName); return options; @@ -87,7 +87,7 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour } datasource.azureMonitorDatasource - .newGetMetricMetadata(resourceUri, metricNamespace, metricName) + .getMetricMetadata({ resourceUri, metricNamespace, metricName }) .then((metadata) => { // TODO: Move the aggregationTypes and timeGrain defaults into `getMetricMetadata` const aggregations = (metadata.supportedAggTypes || [metadata.primaryAggType]).map((v) => ({ diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts index 42997fb09f8..2f9e008fca6 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts @@ -218,49 +218,6 @@ export default class Datasource extends DataSourceApi //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: 'mock-workspace-id', + }, + azureMonitor: { + aggregation: 'Average', + allowedTimeGrainsMs: [60000, 300000, 900000, 1800000, 3600000, 21600000, 43200000, 86400000], + dimensionFilters: [{ dimension: 'dependency/success', filter: '', operator: 'eq' }], + metricDefinition: 'microsoft.insights/components', + metricName: 'dependencies/duration', + metricNamespace: 'microsoft.insights/components', + resourceGroup: 'cloud-datasources', + resourceName: 'AppInsightsTestData', + timeGrain: 'auto', + top: '10', + }, + insightsAnalytics: { + query: '', + resultFormat: 'time_series', + }, + queryType: AzureQueryType.AzureMonitor, + refId: 'A', + subscription: '44693801-6ee6-49de-9b2d-9106972f9572', +}; + +const azureMonitorQueryV8 = { + azureMonitor: { + aggregation: 'Average', + dimensionFilters: [], + metricDefinition: 'microsoft.insights/components', + metricName: 'dependencies/duration', + metricNamespace: 'microsoft.insights/components', + resourceGroup: 'cloud-datasources', + resourceName: 'AppInsightsTestData', + timeGrain: 'auto', + }, + datasource: { + type: 'grafana-azure-monitor-datasource', + uid: 'sD-ZuB87k', + }, + queryType: AzureQueryType.AzureMonitor, + refId: 'A', + subscription: '44693801-6ee6-49de-9b2d-9106972f9572', +}; + const modernMetricsQuery: AzureMonitorQuery = { appInsights: { dimension: [], metricName: 'select', timeGrain: 'auto' }, azureLogAnalytics: { query: '//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', + workspace: 'mock-workspace-id', }, azureMonitor: { aggregation: 'Average', @@ -19,6 +68,8 @@ const modernMetricsQuery: AzureMonitorQuery = { metricNamespace: 'microsoft.insights/components', resourceGroup: 'cloud-datasources', resourceName: 'AppInsightsTestData', + resourceUri: + '/subscriptions/44693801-6ee6-49de-9b2d-9106972f9572/resourceGroups/cloud-datasources/providers/microsoft.insights/components/AppInsightsTestData', timeGrain: 'PT5M', top: '10', }, @@ -37,4 +88,32 @@ describe('AzureMonitor: migrateQuery', () => { // MUST use .toBe because we want to assert that the identity of unmigrated queries remains the same expect(modernMetricsQuery).toBe(result); }); + + describe('migrating from a v7 query to the latest query version', () => { + it('should build a resource uri', () => { + const result = migrateQuery(azureMonitorQueryV7); + expect(result).toMatchObject( + expect.objectContaining({ + azureMonitor: expect.objectContaining({ + resourceUri: + '/subscriptions/44693801-6ee6-49de-9b2d-9106972f9572/resourceGroups/cloud-datasources/providers/microsoft.insights/components/AppInsightsTestData', + }), + }) + ); + }); + }); + + describe('migrating from a v8 query to the latest query version', () => { + it('should build a resource uri', () => { + const result = migrateQuery(azureMonitorQueryV8); + expect(result).toMatchObject( + expect.objectContaining({ + azureMonitor: expect.objectContaining({ + resourceUri: + '/subscriptions/44693801-6ee6-49de-9b2d-9106972f9572/resourceGroups/cloud-datasources/providers/microsoft.insights/components/AppInsightsTestData', + }), + }) + ); + }); + }); }); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/migrateQuery.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/migrateQuery.ts index 3a9043718e0..78e55d70bf2 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/migrateQuery.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/migrateQuery.ts @@ -4,6 +4,7 @@ import { setTimeGrain as setMetricsTimeGrain, } from '../components/MetricsQueryEditor/setQueryValue'; import TimegrainConverter from '../time_grain_converter'; +import UrlBuilder from '../azure_monitor/url_builder'; import { AzureMonitorQuery, AzureQueryType, DeprecatedAzureQueryType } from '../types'; const OLD_DEFAULT_DROPDOWN_VALUE = 'select'; @@ -21,6 +22,7 @@ export default function migrateQuery(query: AzureMonitorQuery): AzureMonitorQuer workingQuery = migrateToDefaultNamespace(workingQuery); workingQuery = migrateApplicationInsightsDimensions(workingQuery); workingQuery = migrateMetricsDimensionFilters(workingQuery); + workingQuery = migrateResourceUri(workingQuery); return workingQuery; } @@ -121,7 +123,6 @@ function migrateApplicationInsightsDimensions(query: AzureMonitorQuery): AzureMo return query; } -// Exported because its also used directly in the datasource.ts for some reason function migrateMetricsDimensionFilters(query: AzureMonitorQuery): AzureMonitorQuery { let workingQuery = query; @@ -133,6 +134,33 @@ function migrateMetricsDimensionFilters(query: AzureMonitorQuery): AzureMonitorQ return workingQuery; } +// Azure Monitor metric queries prior to Grafana version 9 did not include a `resourceUri`. +// The resourceUri was previously constructed with the subscription id, resource group, +// metric definition (a.k.a. resource type), and the resource name. +function migrateResourceUri(query: AzureMonitorQuery): AzureMonitorQuery { + const azureMonitorQuery = query.azureMonitor; + + if (!azureMonitorQuery || azureMonitorQuery.resourceUri) { + return query; + } + + const { subscription } = query; + const { resourceGroup, metricDefinition, resourceName } = azureMonitorQuery; + if (!(subscription && resourceGroup && metricDefinition && resourceName)) { + return query; + } + + const resourceUri = UrlBuilder.buildResourceUri(subscription, resourceGroup, metricDefinition, resourceName); + + return { + ...query, + azureMonitor: { + ...azureMonitorQuery, + resourceUri, + }, + }; +} + // datasource.ts also contains some migrations, which have been moved to here. Unsure whether // they should also do all the other migrations... export function datasourceMigrations(query: AzureMonitorQuery): AzureMonitorQuery { @@ -159,6 +187,7 @@ export function datasourceMigrations(query: AzureMonitorQuery): AzureMonitorQuer if (workingQuery.queryType === AzureQueryType.AzureMonitor && workingQuery.azureMonitor) { workingQuery = migrateMetricsDimensionFilters(workingQuery); + workingQuery = migrateResourceUri(workingQuery); } return workingQuery; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.test.ts index e7f5ef9779e..82c6a566e37 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.test.ts @@ -270,7 +270,9 @@ describe('VariableSupport', () => { azureLogAnalyticsDatasource: { defaultSubscriptionId: 'defaultSubscriptionId', }, - getMetricNamespaces: jest.fn().mockResolvedValueOnce(expectedResults), + azureMonitorDatasource: { + getMetricNamespaces: jest.fn().mockResolvedValueOnce(expectedResults), + }, }) ); const mockRequest = { @@ -296,7 +298,9 @@ describe('VariableSupport', () => { const expectedResults = ['test']; const variableSupport = new VariableSupport( createMockDatasource({ - getMetricNamespaces: jest.fn().mockResolvedValueOnce(expectedResults), + azureMonitorDatasource: { + getMetricNamespaces: jest.fn().mockResolvedValueOnce(expectedResults), + }, }) ); const mockRequest = { @@ -325,7 +329,9 @@ describe('VariableSupport', () => { azureLogAnalyticsDatasource: { defaultSubscriptionId: 'defaultSubscriptionId', }, - getMetricNames: jest.fn().mockResolvedValueOnce(expectedResults), + azureMonitorDatasource: { + getMetricNames: jest.fn().mockResolvedValueOnce(expectedResults), + }, }) ); const mockRequest = { @@ -351,7 +357,9 @@ describe('VariableSupport', () => { const expectedResults = ['test']; const variableSupport = new VariableSupport( createMockDatasource({ - getMetricNames: jest.fn().mockResolvedValueOnce(expectedResults), + azureMonitorDatasource: { + getMetricNames: jest.fn().mockResolvedValueOnce(expectedResults), + }, }) ); const mockRequest = { @@ -478,7 +486,9 @@ describe('VariableSupport', () => { azureLogAnalyticsDatasource: { defaultSubscriptionId: 'defaultSubscriptionId', }, - getMetricNames: jest.fn().mockResolvedValueOnce([]), + azureMonitorDatasource: { + getMetricNames: jest.fn().mockResolvedValueOnce([]), + }, }) ); const mockRequest = { diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.ts index 6c70fa479ab..abe9901a3df 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.ts @@ -81,22 +81,11 @@ export class VariableSupport extends CustomVariableSupport