AzureMonitor: Automate location retrieval (#59602)

* Automate location retrieval

- Add function to retrieve provider information
- Add function to retrieve subscription locations
- Add parsers for each function
- Add types
- Update resourcePickerData with new functions
- Remove old location metadata file

* Improve types

- Add types for responses
- Use correct name parameter
- Update response parser (parseProvider unnecessary)

* Fix tests

- Add necessary mocks

* Fix logic and ensure maps are consistent

* Add tests

* Review

* Lint issue
This commit is contained in:
Andreas Christou 2022-12-02 13:23:15 +00:00 committed by GitHub
parent 020b9960c8
commit c52d4e2a64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 273 additions and 373 deletions

View File

@ -24,6 +24,17 @@ export default function createMockDatasource(overrides?: DeepPartial<Datasource>
supportedTimeGrains: [], supportedTimeGrains: [],
dimensions: [], dimensions: [],
}), }),
getProvider: jest.fn().mockResolvedValueOnce({
namespace: 'Microsoft.Insights',
resourceTypes: [
{ resourceType: 'logs', locations: ['North Europe'], apiVersions: ['2022-11-11'], capabilities: '' },
],
}),
getLocations: jest
.fn()
.mockResolvedValueOnce(
new Map([['northeurope', { displayName: 'North Europe', name: 'northeurope', supportsLogs: false }]])
),
}, },
getAzureLogAnalyticsWorkspaces: jest.fn().mockResolvedValueOnce([]), getAzureLogAnalyticsWorkspaces: jest.fn().mockResolvedValueOnce([]),

View File

@ -1,4 +1,5 @@
import { ResourceRowGroup, ResourceRowType } from '../components/ResourcePicker/types'; import { ResourceRowGroup, ResourceRowType } from '../components/ResourcePicker/types';
import { AzureMonitorLocations } from '../types';
export const createMockSubscriptions = (): ResourceRowGroup => [ export const createMockSubscriptions = (): ResourceRowGroup => [
{ {
@ -117,3 +118,6 @@ export const mockSearchResults = (): ResourceRowGroup => [
location: 'northeurope', location: 'northeurope',
}, },
]; ];
export const mockGetValidLocations = (): Map<string, AzureMonitorLocations> =>
new Map([['northeurope', { displayName: 'North Europe', name: 'northeurope', supportsLogs: true }]]);

View File

@ -1,3 +1,2 @@
export * from './locations';
export * from './resourceTypes'; export * from './resourceTypes';
export * from './logsResourceTypes'; export * from './logsResourceTypes';

View File

@ -1,359 +0,0 @@
// Unfortunately this list is manually maintained as there's no (nice) automated way to get
// data from Azure.
export const locationMetadata = [
{
location: 'eastus',
displayName: 'East US',
supportsLogs: true,
},
{
location: 'eastus2',
displayName: 'East US 2',
supportsLogs: true,
},
{
location: 'southcentralus',
displayName: 'South Central US',
supportsLogs: true,
},
{
location: 'westus2',
displayName: 'West US 2',
supportsLogs: true,
},
{
location: 'westus3',
displayName: 'West US 3',
supportsLogs: true,
},
{
location: 'australiaeast',
displayName: 'Australia East',
supportsLogs: true,
},
{
location: 'southeastasia',
displayName: 'Southeast Asia',
supportsLogs: true,
},
{
location: 'northeurope',
displayName: 'North Europe',
supportsLogs: true,
},
{
location: 'uksouth',
displayName: 'UK South',
supportsLogs: true,
},
{
location: 'westeurope',
displayName: 'West Europe',
supportsLogs: true,
},
{
location: 'centralus',
displayName: 'Central US',
supportsLogs: true,
},
{
location: 'northcentralus',
displayName: 'North Central US',
supportsLogs: true,
},
{
location: 'westus',
displayName: 'West US',
supportsLogs: true,
},
{
location: 'southafricanorth',
displayName: 'South Africa North',
supportsLogs: true,
},
{
location: 'centralindia',
displayName: 'Central India',
supportsLogs: true,
},
{
location: 'eastasia',
displayName: 'East Asia',
supportsLogs: true,
},
{
location: 'japaneast',
displayName: 'Japan East',
supportsLogs: true,
},
{
location: 'jioindiawest',
displayName: 'Jio India West',
supportsLogs: true,
},
{
location: 'koreacentral',
displayName: 'Korea Central',
supportsLogs: true,
},
{
location: 'canadacentral',
displayName: 'Canada Central',
supportsLogs: true,
},
{
location: 'francecentral',
displayName: 'France Central',
supportsLogs: true,
},
{
location: 'germanywestcentral',
displayName: 'Germany West Central',
supportsLogs: true,
},
{
location: 'norwayeast',
displayName: 'Norway East',
supportsLogs: true,
},
{
location: 'switzerlandnorth',
displayName: 'Switzerland North',
supportsLogs: true,
},
{
location: 'uaenorth',
displayName: 'UAE North',
supportsLogs: true,
},
{
location: 'brazilsouth',
displayName: 'Brazil South',
supportsLogs: true,
},
{
location: 'centralusstage',
displayName: 'Central US (Stage)',
supportsLogs: true,
},
{
location: 'eastusstage',
displayName: 'East US (Stage)',
supportsLogs: true,
},
{
location: 'eastus2stage',
displayName: 'East US 2 (Stage)',
supportsLogs: true,
},
{
location: 'northcentralusstage',
displayName: 'North Central US (Stage)',
supportsLogs: true,
},
{
location: 'southcentralusstage',
displayName: 'South Central US (Stage)',
supportsLogs: true,
},
{
location: 'westusstage',
displayName: 'West US (Stage)',
supportsLogs: true,
},
{
location: 'westus2stage',
displayName: 'West US 2 (Stage)',
supportsLogs: true,
},
{
location: 'asia',
displayName: 'Asia',
supportsLogs: true,
},
{
location: 'asiapacific',
displayName: 'Asia Pacific',
supportsLogs: true,
},
{
location: 'australia',
displayName: 'Australia',
supportsLogs: true,
},
{
location: 'brazil',
displayName: 'Brazil',
supportsLogs: true,
},
{
location: 'canada',
displayName: 'Canada',
supportsLogs: true,
},
{
location: 'europe',
displayName: 'Europe',
supportsLogs: true,
},
{
location: 'global',
displayName: 'Global',
supportsLogs: true,
},
{
location: 'india',
displayName: 'India',
supportsLogs: true,
},
{
location: 'japan',
displayName: 'Japan',
supportsLogs: true,
},
{
location: 'uk',
displayName: 'United Kingdom',
supportsLogs: true,
},
{
location: 'unitedstates',
displayName: 'United States',
supportsLogs: true,
},
{
location: 'eastasiastage',
displayName: 'East Asia (Stage)',
supportsLogs: true,
},
{
location: 'southeastasiastage',
displayName: 'Southeast Asia (Stage)',
supportsLogs: true,
},
{
location: 'westcentralus',
displayName: 'West Central US',
supportsLogs: true,
},
{
location: 'southafricawest',
displayName: 'South Africa West',
supportsLogs: true,
},
{
location: 'australiacentral',
displayName: 'Australia Central',
supportsLogs: true,
},
{
location: 'australiacentral2',
displayName: 'Australia Central 2',
supportsLogs: true,
},
{
location: 'australiasoutheast',
displayName: 'Australia Southeast',
supportsLogs: true,
},
{
location: 'japanwest',
displayName: 'Japan West',
supportsLogs: true,
},
{
location: 'koreasouth',
displayName: 'Korea South',
supportsLogs: true,
},
{
location: 'southindia',
displayName: 'South India',
supportsLogs: true,
},
{
location: 'westindia',
displayName: 'West India',
supportsLogs: true,
},
{
location: 'canadaeast',
displayName: 'Canada East',
supportsLogs: true,
},
{
location: 'francesouth',
displayName: 'France South',
supportsLogs: true,
},
{
location: 'germanynorth',
displayName: 'Germany North',
supportsLogs: true,
},
{
location: 'norwaywest',
displayName: 'Norway West',
supportsLogs: true,
},
{
location: 'switzerlandwest',
displayName: 'Switzerland West',
supportsLogs: true,
},
{
location: 'ukwest',
displayName: 'UK West',
supportsLogs: true,
},
{
location: 'uaecentral',
displayName: 'UAE Central',
supportsLogs: true,
},
{
location: 'brazilsoutheast',
displayName: 'Brazil Southeast',
supportsLogs: true,
},
{
location: 'chinaeast',
displayName: 'China Eest',
supportsLogs: true,
},
{
location: 'chinaeast2',
displayName: 'China Eest 2',
supportsLogs: true,
},
{
location: 'chinanorth',
displayName: 'China North',
supportsLogs: true,
},
{
location: 'chinanorth2',
displayName: 'China North 2',
supportsLogs: true,
},
{
location: 'chinanorth3',
displayName: 'China North 3',
supportsLogs: true,
},
];
export const logsSupportedLocationsKusto = locationMetadata
.filter((v) => v.supportsLogs)
.map((v) => `"${v.location}"`)
.join(',');
// Object, keyed by location ID
export const locationDisplayNames: Record<string, string> = locationMetadata.reduce(
(acc, location) => ({
...acc,
[location.location]: location.displayName,
}),
{}
);

View File

@ -7,7 +7,7 @@ import createMockQuery from '../__mocks__/query';
import { createTemplateVariables } from '../__mocks__/utils'; import { createTemplateVariables } from '../__mocks__/utils';
import { singleVariable, subscriptionsVariable } from '../__mocks__/variables'; import { singleVariable, subscriptionsVariable } from '../__mocks__/variables';
import AzureMonitorDatasource from '../datasource'; import AzureMonitorDatasource from '../datasource';
import { AzureDataSourceJsonData, AzureQueryType } from '../types'; import { AzureDataSourceJsonData, AzureMonitorLocationsResponse, AzureQueryType } from '../types';
const templateSrv = new TemplateSrv(); const templateSrv = new TemplateSrv();
@ -351,6 +351,89 @@ describe('AzureMonitorDatasource', () => {
}); });
}); });
describe('When performing getLocations', () => {
const sub1Response: AzureMonitorLocationsResponse = {
value: [
{
id: '/subscriptions/mock-subscription-id-1/locations/northeurope',
name: 'northeurope',
displayName: 'North Europe',
regionalDisplayName: '(Europe) North Europe',
metadata: {
regionType: 'Physical',
regionCategory: 'Recommended',
geographyGroup: 'EU',
longitude: '-0',
latitude: '0',
physicalLocation: 'Europe',
pairedRegion: [],
},
},
],
};
const sub2Response: AzureMonitorLocationsResponse = {
value: [
{
id: '/subscriptions/mock-subscription-id-2/locations/eastus2',
name: 'eastus2',
displayName: 'East US 2',
regionalDisplayName: '(US) East US 2',
metadata: {
regionType: 'Physical',
regionCategory: 'Recommended',
geographyGroup: 'US',
longitude: '-0',
latitude: '0',
physicalLocation: 'US',
pairedRegion: [],
},
},
],
};
beforeEach(() => {
ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => {
const expectedPaths = [1, 2].map(
(sub) => `azuremonitor/subscriptions/mock-subscription-id-${sub}/locations?api-version=2020-01-01`
);
expect(expectedPaths).toContain(path);
if (path.includes('mock-subscription-id-1')) {
return Promise.resolve(sub1Response);
} else {
return Promise.resolve(sub2Response);
}
});
});
it('should return a locations map', async () => {
const result = await ctx.ds.azureMonitorDatasource.getLocations(['mock-subscription-id-1']);
expect(result.size).toBe(1);
expect(result.has('northeurope')).toBe(true);
expect(result.get('northeurope')?.name).toBe('northeurope');
expect(result.get('northeurope')?.displayName).toBe('North Europe');
expect(result.get('northeurope')?.supportsLogs).toBe(undefined);
});
it('should return a locations map with locations deduped', async () => {
const result = await ctx.ds.azureMonitorDatasource.getLocations([
'mock-subscription-id-1',
'mock-subscription-id-2',
]);
expect(result.size).toBe(2);
expect(result.has('northeurope')).toBe(true);
expect(result.get('northeurope')?.name).toBe('northeurope');
expect(result.get('northeurope')?.displayName).toBe('North Europe');
expect(result.get('northeurope')?.supportsLogs).toBe(undefined);
expect(result.has('eastus2')).toBe(true);
expect(result.get('eastus2')?.name).toBe('eastus2');
expect(result.get('eastus2')?.displayName).toBe('East US 2');
expect(result.get('eastus2')?.supportsLogs).toBe(undefined);
});
});
describe('Legacy Azure Monitor Query Object data fetchers', () => { describe('Legacy Azure Monitor Query Object data fetchers', () => {
describe('When performing getSubscriptions', () => { describe('When performing getSubscriptions', () => {
const response = { const response = {

View File

@ -19,6 +19,9 @@ import {
GetMetricNamesQuery, GetMetricNamesQuery,
GetMetricMetadataQuery, GetMetricMetadataQuery,
AzureMetricQuery, AzureMetricQuery,
AzureMonitorLocations,
AzureMonitorProvidersResponse,
AzureMonitorLocationsResponse,
} from '../types'; } from '../types';
import { routeNames } from '../utils/common'; import { routeNames } from '../utils/common';
import migrateQuery from '../utils/migrateQuery'; import migrateQuery from '../utils/migrateQuery';
@ -36,6 +39,8 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
apiVersion = '2018-01-01'; apiVersion = '2018-01-01';
apiPreviewVersion = '2017-12-01-preview'; apiPreviewVersion = '2017-12-01-preview';
listByResourceGroupApiVersion = '2021-04-01'; listByResourceGroupApiVersion = '2021-04-01';
providerApiVersion = '2021-04-01';
locationsApiVersion = '2020-01-01';
defaultSubscriptionId?: string; defaultSubscriptionId?: string;
resourcePath: string; resourcePath: string;
azurePortalUrl: string; azurePortalUrl: string;
@ -296,4 +301,26 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
return workingQuery; return workingQuery;
} }
async getProvider(providerName: string) {
return await this.getResource<AzureMonitorProvidersResponse>(
`${routeNames.azureMonitor}/providers/${providerName}?api-version=${this.providerApiVersion}`
);
}
async getLocations(subscriptions: string[]) {
const locationMap = new Map<string, AzureMonitorLocations>();
for (const subscription of subscriptions) {
const subLocations = ResponseParser.parseLocations(
await this.getResource<AzureMonitorLocationsResponse>(
`${routeNames.azureMonitor}/subscriptions/${subscription}/locations?api-version=${this.locationsApiVersion}`
)
);
for (const location of subLocations) {
locationMap.set(location.name, location);
}
}
return locationMap;
}
} }

View File

@ -3,9 +3,11 @@ import { find, get } from 'lodash';
import TimeGrainConverter from '../time_grain_converter'; import TimeGrainConverter from '../time_grain_converter';
import { import {
AzureMonitorLocalizedValue, AzureMonitorLocalizedValue,
AzureMonitorLocations,
AzureMonitorMetricAvailabilityMetadata, AzureMonitorMetricAvailabilityMetadata,
AzureMonitorMetricsMetadataResponse, AzureMonitorMetricsMetadataResponse,
AzureMonitorOption, AzureMonitorOption,
AzureMonitorLocationsResponse,
} from '../types'; } from '../types';
export default class ResponseParser { export default class ResponseParser {
static parseResponseValues( static parseResponseValues(
@ -170,4 +172,18 @@ export default class ResponseParser {
return list; return list;
} }
static parseLocations(result: AzureMonitorLocationsResponse) {
const locations: AzureMonitorLocations[] = [];
if (!result) {
return locations;
}
for (const location of result.value) {
locations.push({ name: location.name, displayName: location.displayName, supportsLogs: undefined });
}
return locations;
}
} }

View File

@ -10,6 +10,7 @@ import createMockQuery from '../../__mocks__/query';
import { import {
createMockResourceGroupsBySubscription, createMockResourceGroupsBySubscription,
createMockSubscriptions, createMockSubscriptions,
mockGetValidLocations,
mockResourcesByResourceGroup, mockResourcesByResourceGroup,
} from '../../__mocks__/resourcePickerRows'; } from '../../__mocks__/resourcePickerRows';
import ResourcePickerData from '../../resourcePicker/resourcePickerData'; import ResourcePickerData from '../../resourcePicker/resourcePickerData';
@ -44,7 +45,7 @@ export function createMockResourcePickerData() {
mockResourcePicker.getResourcesForResourceGroup = jest.fn().mockResolvedValue(mockResourcesByResourceGroup()); mockResourcePicker.getResourcesForResourceGroup = jest.fn().mockResolvedValue(mockResourcesByResourceGroup());
mockResourcePicker.getResourceURIFromWorkspace = jest.fn().mockReturnValue(''); mockResourcePicker.getResourceURIFromWorkspace = jest.fn().mockReturnValue('');
mockResourcePicker.getResourceURIDisplayProperties = jest.fn().mockResolvedValue({}); mockResourcePicker.getResourceURIDisplayProperties = jest.fn().mockResolvedValue({});
mockResourcePicker.getLogsLocations = jest.fn().mockResolvedValue(mockGetValidLocations());
return mockResourcePicker; return mockResourcePicker;
} }

View File

@ -5,6 +5,7 @@ import {
} from '../__mocks__/argResourcePickerResponse'; } from '../__mocks__/argResourcePickerResponse';
import createMockDatasource from '../__mocks__/datasource'; import createMockDatasource from '../__mocks__/datasource';
import { createMockInstanceSetttings } from '../__mocks__/instanceSettings'; import { createMockInstanceSetttings } from '../__mocks__/instanceSettings';
import { mockGetValidLocations } from '../__mocks__/resourcePickerRows';
import { AzureGraphResponse } from '../types'; import { AzureGraphResponse } from '../types';
import ResourcePickerData from './resourcePickerData'; import ResourcePickerData from './resourcePickerData';
@ -21,9 +22,13 @@ const createResourcePickerData = (responses: AzureGraphResponse[]) => {
postResource.mockResolvedValueOnce(res); postResource.mockResolvedValueOnce(res);
}); });
resourcePickerData.postResource = postResource; resourcePickerData.postResource = postResource;
const logLocationsMap = mockGetValidLocations();
return { resourcePickerData, postResource }; const getLogsLocations = jest.spyOn(resourcePickerData, 'getLogsLocations').mockResolvedValue(logLocationsMap);
resourcePickerData.logLocationsMap = logLocationsMap;
resourcePickerData.logLocations = Array.from(logLocationsMap.values()).map((location) => `"${location.name}"`);
return { resourcePickerData, postResource, mockDatasource, getValidLocations: getLogsLocations };
}; };
describe('AzureMonitor resourcePickerData', () => { describe('AzureMonitor resourcePickerData', () => {
describe('getSubscriptions', () => { describe('getSubscriptions', () => {
it('makes 1 call to ARG with the correct path and query arguments', async () => { it('makes 1 call to ARG with the correct path and query arguments', async () => {
@ -375,4 +380,35 @@ describe('AzureMonitor resourcePickerData', () => {
} }
}); });
}); });
describe('getValidLocations', () => {
it('returns a locations map', async () => {
const { resourcePickerData, getValidLocations } = createResourcePickerData([createMockARGSubscriptionResponse()]);
getValidLocations.mockRestore();
const subscriptions = await resourcePickerData.getSubscriptions();
const locations = await resourcePickerData.getLogsLocations(subscriptions);
expect(locations.size).toBe(1);
expect(locations.has('northeurope')).toBe(true);
expect(locations.get('northeurope')?.name).toBe('northeurope');
expect(locations.get('northeurope')?.displayName).toBe('North Europe');
expect(locations.get('northeurope')?.supportsLogs).toBe(true);
});
it('returns the raw locations map if provider is undefined', async () => {
const { resourcePickerData, mockDatasource, getValidLocations } = createResourcePickerData([
createMockARGSubscriptionResponse(),
]);
getValidLocations.mockRestore();
mockDatasource.azureMonitorDatasource.getProvider = jest.fn().mockResolvedValue(undefined);
const subscriptions = await resourcePickerData.getSubscriptions();
const locations = await resourcePickerData.getLogsLocations(subscriptions);
expect(locations.size).toBe(1);
expect(locations.has('northeurope')).toBe(true);
expect(locations.get('northeurope')?.name).toBe('northeurope');
expect(locations.get('northeurope')?.displayName).toBe('North Europe');
expect(locations.get('northeurope')?.supportsLogs).toBe(false);
});
});
}); });

View File

@ -3,12 +3,7 @@ import { uniq } from 'lodash';
import { DataSourceInstanceSettings } from '@grafana/data'; import { DataSourceInstanceSettings } from '@grafana/data';
import { DataSourceWithBackend } from '@grafana/runtime'; import { DataSourceWithBackend } from '@grafana/runtime';
import { import { logsResourceTypes, resourceTypeDisplayNames } from '../azureMetadata';
locationDisplayNames,
logsSupportedLocationsKusto,
logsResourceTypes,
resourceTypeDisplayNames,
} from '../azureMetadata';
import AzureMonitorDatasource from '../azure_monitor/azure_monitor_datasource'; import AzureMonitorDatasource from '../azure_monitor/azure_monitor_datasource';
import { ResourceRow, ResourceRowGroup, ResourceRowType } from '../components/ResourcePicker/types'; import { ResourceRow, ResourceRowGroup, ResourceRowType } from '../components/ResourcePicker/types';
import { addResources, parseResourceDetails, parseResourceURI } from '../components/ResourcePicker/utils'; import { addResources, parseResourceDetails, parseResourceURI } from '../components/ResourcePicker/utils';
@ -16,6 +11,7 @@ import {
AzureDataSourceJsonData, AzureDataSourceJsonData,
AzureGraphResponse, AzureGraphResponse,
AzureMetricResource, AzureMetricResource,
AzureMonitorLocations,
AzureMonitorQuery, AzureMonitorQuery,
AzureResourceGraphOptions, AzureResourceGraphOptions,
AzureResourceSummaryItem, AzureResourceSummaryItem,
@ -36,6 +32,8 @@ export default class ResourcePickerData extends DataSourceWithBackend<AzureMonit
resultLimit = 200; resultLimit = 200;
azureMonitorDatasource; azureMonitorDatasource;
supportedMetricNamespaces = ''; supportedMetricNamespaces = '';
logLocationsMap: Map<string, AzureMonitorLocations> = new Map();
logLocations: string[] = [];
constructor( constructor(
instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>, instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>,
@ -51,6 +49,12 @@ export default class ResourcePickerData extends DataSourceWithBackend<AzureMonit
currentSelection?: AzureMetricResource currentSelection?: AzureMetricResource
): Promise<ResourceRowGroup> { ): Promise<ResourceRowGroup> {
const subscriptions = await this.getSubscriptions(); const subscriptions = await this.getSubscriptions();
if (this.logLocationsMap.size === 0) {
this.logLocationsMap = await this.getLogsLocations(subscriptions);
this.logLocations = Array.from(this.logLocationsMap.values()).map((location) => `"${location.name}"`);
}
if (!currentSelection) { if (!currentSelection) {
return subscriptions; return subscriptions;
} }
@ -119,7 +123,7 @@ export default class ResourcePickerData extends DataSourceWithBackend<AzureMonit
resourceGroupName: item.resourceGroup, resourceGroupName: item.resourceGroup,
type, type,
typeLabel: resourceTypeDisplayNames[item.type] || item.type, typeLabel: resourceTypeDisplayNames[item.type] || item.type,
location: locationDisplayNames[item.location] || item.location, location: this.logLocationsMap.get(item.location)?.displayName || item.location,
}; };
}); });
}; };
@ -222,10 +226,13 @@ export default class ResourcePickerData extends DataSourceWithBackend<AzureMonit
resourceGroupId: string, resourceGroupId: string,
type: ResourcePickerQueryType type: ResourcePickerQueryType
): Promise<ResourceRowGroup> { ): Promise<ResourceRowGroup> {
if (!this.logLocations) {
return [];
}
const { data: response } = await this.makeResourceGraphRequest<RawAzureResourceItem[]>(` const { data: response } = await this.makeResourceGraphRequest<RawAzureResourceItem[]>(`
resources resources
| where id hasprefix "${resourceGroupId}" | where id hasprefix "${resourceGroupId}"
${await this.filterByType(type)} and location in (${logsSupportedLocationsKusto}) ${await this.filterByType(type)} and location in (${this.logLocations})
`); `);
return response.map((item) => { return response.map((item) => {
@ -240,7 +247,7 @@ export default class ResourcePickerData extends DataSourceWithBackend<AzureMonit
resourceGroupName: item.resourceGroup, resourceGroupName: item.resourceGroup,
type: ResourceRowType.Resource, type: ResourceRowType.Resource,
typeLabel: resourceTypeDisplayNames[item.type] || item.type, typeLabel: resourceTypeDisplayNames[item.type] || item.type,
location: locationDisplayNames[item.location] || item.location, location: this.logLocationsMap.get(item.location)?.displayName || item.location,
}; };
}); });
} }
@ -359,4 +366,34 @@ export default class ResourcePickerData extends DataSourceWithBackend<AzureMonit
} }
this.supportedMetricNamespaces = uniq(supportedMetricNamespaces).join(','); this.supportedMetricNamespaces = uniq(supportedMetricNamespaces).join(',');
} }
async getLogsLocations(subscriptions: ResourceRowGroup): Promise<Map<string, AzureMonitorLocations>> {
const subscriptionIds = subscriptions.map((sub) => sub.id);
const locations = await this.azureMonitorDatasource.getLocations(subscriptionIds);
const insightsProvider = await this.azureMonitorDatasource.getProvider('Microsoft.Insights');
const logsProvider = insightsProvider?.resourceTypes.find((provider) => provider.resourceType === 'logs');
if (!logsProvider) {
return locations;
}
const logsLocations = logsProvider.locations.map((location) => ({
displayName: location,
name: '',
supportsLogs: true,
}));
const logLocationsMap = new Map<string, AzureMonitorLocations>();
for (const logLocation of logsLocations) {
const name =
Array.from(locations.values()).find((location) => logLocation.displayName === location.displayName)?.name || '';
if (name !== '') {
logLocationsMap.set(name, { ...logLocation, name });
}
}
return logLocationsMap;
}
} }

View File

@ -258,3 +258,48 @@ export interface LegacyAzureGetMetricMetadataQuery {
customNamespace?: string; customNamespace?: string;
metricName: string; metricName: string;
} }
export interface AzureMonitorLocations {
displayName: string;
name: string;
supportsLogs?: boolean;
}
export interface AzureMonitorProvidersResponse {
namespace: string;
resourceTypes: ProviderResourceType[];
}
export interface ProviderResourceType {
resourceType: string;
locations: string[];
apiVersions: string[];
capabilities: string;
}
export interface AzureMonitorLocationsResponse {
value: Location[];
}
interface Location {
id: string;
name: string;
displayName: string;
regionalDisplayName: string;
metadata: LocationMetadata;
}
interface LocationMetadata {
regionType: string;
regionCategory: string;
geographyGroup: string;
longitude: string;
latitude: string;
physicalLocation: string;
pairedRegion: LocationPairedRegion[];
}
interface LocationPairedRegion {
name: string;
id: string;
}