diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.test.ts index 0256f2a144c..cb768c49ea3 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.test.ts @@ -1,7 +1,7 @@ import AzureMonitorDatasource from '../datasource'; import FakeSchemaData from './__mocks__/schema'; import { TemplateSrv } from 'app/features/templating/template_srv'; -import { AzureLogsVariable } from '../types'; +import { AzureLogsVariable, DatasourceValidationResult } from '../types'; import { toUtc } from '@grafana/data'; import { backendSrv } from 'app/core/services/backend_srv'; @@ -112,17 +112,15 @@ describe('AzureLogAnalyticsDatasource', () => { }; beforeEach(() => { - ctx.instanceSettings.jsonData.logAnalyticsSubscriptionId = 'xxx'; - ctx.instanceSettings.jsonData.logAnalyticsTenantId = 'xxx'; - ctx.instanceSettings.jsonData.logAnalyticsClientId = 'xxx'; + ctx.instanceSettings.jsonData.azureAuthType = 'msi'; datasourceRequestMock.mockImplementation(() => Promise.reject(error)); }); it('should return error status and a detailed error message', () => { - return ctx.ds.testDatasource().then((results: any) => { - expect(results.status).toEqual('error'); - expect(results.message).toEqual( - '1. Azure Log Analytics: Bad Request: InvalidApiVersionParameter. An error message. ' + return ctx.ds.azureLogAnalyticsDatasource.testDatasource().then((result: DatasourceValidationResult) => { + expect(result.status).toEqual('error'); + expect(result.message).toEqual( + 'Azure Log Analytics requires access to Azure Monitor but had the following error: Bad Request: InvalidApiVersionParameter. An error message.' ); }); }); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.ts index bb33ac360cb..b7a73820e25 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.ts @@ -37,7 +37,7 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< baseUrl: string; applicationId: string; - subscriptionId: string; + defaultSubscriptionId?: string; azureMonitorUrl: string; defaultOrFirstWorkspace: string; @@ -55,12 +55,24 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< this.azureMonitorUrl = `/${managementRoute}/subscriptions`; this.url = instanceSettings.url || ''; - this.subscriptionId = this.instanceSettings.jsonData.logAnalyticsSubscriptionId || ''; + this.defaultSubscriptionId = this.instanceSettings.jsonData.logAnalyticsSubscriptionId || ''; this.defaultOrFirstWorkspace = this.instanceSettings.jsonData.logAnalyticsDefaultWorkspace || ''; } isConfigured(): boolean { - return !!this.subscriptionId && this.subscriptionId.length > 0; + // If validation didn't return any error then the data source is properly configured + return !this.validateDatasource(); + } + + async getSubscriptions(): Promise> { + if (!this.isConfigured()) { + return []; + } + + const url = `${this.azureMonitorUrl}?api-version=2019-03-01`; + return await this.doRequest(url).then((result: any) => { + return ResponseParser.parseSubscriptions(result); + }); } async getWorkspaces(subscription: string): Promise { @@ -73,8 +85,8 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< ); } - getWorkspaceList(subscription: string): Promise { - const subscriptionId = getTemplateSrv().replace(subscription || this.subscriptionId); + private getWorkspaceList(subscription: string): Promise { + const subscriptionId = getTemplateSrv().replace(subscription || this.defaultSubscriptionId); const workspaceListUrl = this.azureMonitorUrl + @@ -109,7 +121,7 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< workspace = this.defaultOrFirstWorkspace; } - const subscriptionId = templateSrv.replace(target.subscription || this.subscriptionId, scopedVars); + const subscriptionId = templateSrv.replace(target.subscription || this.defaultSubscriptionId, scopedVars); const query = templateSrv.replace(item.query, scopedVars, this.interpolateVariable); return { @@ -182,10 +194,10 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< } async getWorkspaceDetails(workspaceId: string) { - if (!this.subscriptionId) { + if (!this.defaultSubscriptionId) { return {}; } - const response = await this.getWorkspaceList(this.subscriptionId); + const response = await this.getWorkspaceList(this.defaultSubscriptionId); const details = response.data.value.find((o: any) => { return o.properties.customerId === workspaceId; @@ -216,8 +228,8 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< metricFindQueryInternal(query: string): Promise { // workspaces() - Get workspaces in the default subscription const workspacesQuery = query.match(/^workspaces\(\)/i); - if (workspacesQuery) { - return this.getWorkspaces(this.subscriptionId); + if (workspacesQuery && this.defaultSubscriptionId) { + return this.getWorkspaces(this.defaultSubscriptionId); } // workspaces("abc-def-etc") - Get workspaces a specified subscription @@ -228,6 +240,10 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< // Execute the query as KQL to the default or first workspace return this.getDefaultOrFirstWorkspace().then((resourceURI) => { + if (!resourceURI) { + return []; + } + const queries = this.buildQuery(query, null, resourceURI); const promises = this.doQueries(queries); @@ -299,16 +315,32 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< return quotedValues.join(','); } - getDefaultOrFirstWorkspace() { + async getDefaultOrFirstSubscription(): Promise { + if (this.defaultSubscriptionId) { + return this.defaultSubscriptionId; + } + const subscriptions = await this.getSubscriptions(); + return subscriptions[0]?.value; + } + + async getDefaultOrFirstWorkspace(): Promise { if (this.defaultOrFirstWorkspace) { - return Promise.resolve(this.defaultOrFirstWorkspace); + return this.defaultOrFirstWorkspace; } - return this.getWorkspaces(this.subscriptionId).then((workspaces) => { - this.defaultOrFirstWorkspace = workspaces[0].value; + const subscriptionId = await this.getDefaultOrFirstSubscription(); + if (!subscriptionId) { + return undefined; + } - return this.defaultOrFirstWorkspace; - }); + const workspaces = await this.getWorkspaces(subscriptionId); + const workspace = workspaces[0]?.value; + + if (workspace) { + this.defaultOrFirstWorkspace = workspace; + } + + return workspace; } annotationQuery(options: any) { @@ -371,21 +403,36 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< } // TODO: update to be completely resource-centric - testDatasource(): Promise { + async testDatasource(): Promise { const validationError = this.validateDatasource(); if (validationError) { - return Promise.resolve(validationError); + return validationError; } - return this.getDefaultOrFirstWorkspace() - .then((resourceOrWorkspace) => { - const url = isGUIDish(resourceOrWorkspace) - ? `${this.baseUrl}/v1/workspaces/${resourceOrWorkspace}/metadata` - : `${this.baseUrl}/v1${resourceOrWorkspace}/metadata`; + let resourceOrWorkspace: string; + try { + const result = await this.getDefaultOrFirstWorkspace(); + if (!result) { + return { + status: 'error', + message: 'Workspace not found.', + }; + } + resourceOrWorkspace = result; + } catch (e) { + let message = 'Azure Log Analytics requires access to Azure Monitor but had the following error: '; + return { + status: 'error', + message: this.getErrorMessage(message, e), + }; + } - return this.doRequest(url); - }) - .then((response: any) => { + try { + const url = isGUIDish(resourceOrWorkspace) + ? `${this.baseUrl}/v1/workspaces/${resourceOrWorkspace}/metadata` + : `${this.baseUrl}/v1${resourceOrWorkspace}/metadata`; + + return await this.doRequest(url).then((response: any) => { if (response.status === 200) { return { status: 'success', @@ -398,20 +445,14 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< status: 'error', message: 'Returned http status code ' + response.status, }; - }) - .catch((error: any) => { - let message = 'Azure Log Analytics: '; - if (error.config && error.config.url && error.config.url.indexOf('workspacesloganalytics') > -1) { - message = 'Azure Log Analytics requires access to Azure Monitor but had the following error: '; - } - - message = this.getErrorMessage(message, error); - - return { - status: 'error', - message: message, - }; }); + } catch (e) { + let message = 'Azure Log Analytics: '; + return { + status: 'error', + message: this.getErrorMessage(message, e), + }; + } } private getErrorMessage(message: string, error: any) { @@ -447,13 +488,6 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend< } } - if (!this.isValidConfigField(this.subscriptionId)) { - return { - status: 'error', - message: 'The Subscription Id field is required.', - }; - } - return undefined; } diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/response_parser.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/response_parser.ts index 233fca46591..190dd4d9114 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/response_parser.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/response_parser.ts @@ -1,4 +1,4 @@ -import { concat, find, flattenDeep, forEach, map } from 'lodash'; +import { concat, find, flattenDeep, forEach, get, map } from 'lodash'; import { AnnotationEvent, dateTime, TimeSeries } from '@grafana/data'; import { AzureLogsTableData, AzureLogsVariable } from '../types'; import { AzureLogAnalyticsMetadata } from '../types/logAnalyticsMetadata'; @@ -147,6 +147,27 @@ export default class ResponseParser { static dateTimeToEpoch(dateTimeValue: any) { return dateTime(dateTimeValue).valueOf(); } + + static parseSubscriptions(result: any): Array<{ text: string; value: string }> { + const list: Array<{ text: string; value: string }> = []; + + if (!result) { + return list; + } + + const valueFieldName = 'subscriptionId'; + const textFieldName = 'displayName'; + for (let i = 0; i < result.data.value.length; i++) { + if (!find(list, ['value', get(result.data.value[i], valueFieldName)])) { + list.push({ + text: `${get(result.data.value[i], textFieldName)}`, + value: get(result.data.value[i], valueFieldName), + }); + } + } + + return list; + } } // matches (name):(type) = (defaultValue) 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 23dc1a772e1..372f94bb8a9 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 @@ -3,7 +3,7 @@ import AzureMonitorDatasource from '../datasource'; import { TemplateSrv } from 'app/features/templating/template_srv'; import { DataSourceInstanceSettings } from '@grafana/data'; import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__ -import { AzureDataSourceJsonData } from '../types'; +import { AzureDataSourceJsonData, DatasourceValidationResult } from '../types'; const templateSrv = new TemplateSrv(); @@ -47,17 +47,14 @@ describe('AzureMonitorDatasource', () => { }; beforeEach(() => { - ctx.instanceSettings.jsonData.tenantId = 'xxx'; - ctx.instanceSettings.jsonData.clientId = 'xxx'; + ctx.instanceSettings.jsonData.azureAuthType = 'msi'; datasourceRequestMock.mockImplementation(() => Promise.reject(error)); }); it('should return error status and a detailed error message', () => { - return ctx.ds.testDatasource().then((results: any) => { - expect(results.status).toEqual('error'); - expect(results.message).toEqual( - '1. Azure Monitor: Bad Request: InvalidApiVersionParameter. An error message. ' - ); + return ctx.ds.azureMonitorDatasource.testDatasource().then((result: DatasourceValidationResult) => { + expect(result.status).toEqual('error'); + expect(result.message).toEqual('Azure Monitor: Bad Request: InvalidApiVersionParameter. An error message.'); }); }); }); @@ -78,8 +75,8 @@ describe('AzureMonitorDatasource', () => { }); it('should return success status', () => { - return ctx.ds.testDatasource().then((results: any) => { - expect(results.status).toEqual('success'); + return ctx.ds.azureMonitorDatasource.testDatasource().then((result: DatasourceValidationResult) => { + expect(result.status).toEqual('success'); }); }); }); @@ -99,6 +96,7 @@ describe('AzureMonitorDatasource', () => { }; beforeEach(() => { + ctx.instanceSettings.jsonData.azureAuthType = 'msi'; datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); }); @@ -515,10 +513,11 @@ describe('AzureMonitorDatasource', () => { }; beforeEach(() => { + ctx.instanceSettings.jsonData.azureAuthType = 'msi'; datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); }); - it('should return list of Resource Groups', () => { + it('should return list of subscriptions', () => { return ctx.ds.getSubscriptions().then((results: Array<{ text: string; value: string }>) => { expect(results.length).toEqual(1); expect(results[0].text).toEqual('Primary Subscription'); 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 3a201a468c8..5f105aa4fc0 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 @@ -44,7 +44,7 @@ const aggregationTypeMap: Record = { export default class AzureMonitorDatasource extends DataSourceWithBackend { apiVersion = '2018-01-01'; apiPreviewVersion = '2017-12-01-preview'; - subscriptionId: string; + defaultSubscriptionId?: string; baseUrl: string; resourceGroup: string; resourceName: string; @@ -56,7 +56,7 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend 0; + // If validation didn't return any error then the data source is properly configured + return !this.validateDatasource(); } filterQuery(item: AzureMonitorQuery): boolean { @@ -105,9 +106,13 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend { if (res.data) { for (const df of res.data) { - const metricQuery = metricQueries[df.refId]?.azureMonitor; - if (metricQuery) { - const url = this.buildAzurePortalUrl(metricQuery, this.subscriptionId, this.timeSrv.timeRange()); + const metricQuery = metricQueries[df.refId]; + if (metricQuery && metricQuery.azureMonitor) { + const url = this.buildAzurePortalUrl( + metricQuery.azureMonitor, + metricQuery.subscription, + this.timeSrv.timeRange() + ); for (const field of df.fields) { field.config.links = [ @@ -174,7 +179,7 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend> { + if (!this.isConfigured()) { + return []; + } + const url = `${this.baseUrl}?api-version=2019-03-01`; - return this.doRequest(url).then((result: any) => { + return await this.doRequest(url).then((result: any) => { return ResponseParser.parseSubscriptions(result); }); } @@ -459,15 +474,16 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend { + async testDatasource(): Promise { const validationError = this.validateDatasource(); if (validationError) { return Promise.resolve(validationError); } - const url = `${this.baseUrl}?api-version=2019-03-01`; - return this.doRequest(url) - .then((response: any) => { + try { + const url = `${this.baseUrl}?api-version=2019-03-01`; + + return await this.doRequest(url).then((response: any) => { if (response.status === 200) { return { status: 'success', @@ -480,25 +496,25 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend { - let message = 'Azure Monitor: '; - message += error.statusText ? error.statusText + ': ' : ''; - - if (error.data && error.data.error && error.data.error.code) { - message += error.data.error.code + '. ' + error.data.error.message; - } else if (error.data && error.data.error) { - message += error.data.error; - } else if (error.data) { - message += error.data; - } else { - message += 'Cannot connect to Azure Monitor REST API.'; - } - return { - status: 'error', - message: message, - }; }); + } catch (e) { + let message = 'Azure Monitor: '; + message += e.statusText ? e.statusText + ': ' : ''; + + if (e.data && e.data.error && e.data.error.code) { + message += e.data.error.code + '. ' + e.data.error.message; + } else if (e.data && e.data.error) { + message += e.data.error; + } else if (e.data) { + message += e.data; + } else { + message += 'Cannot connect to Azure Monitor REST API.'; + } + return { + status: 'error', + message: message, + }; + } } private validateDatasource(): DatasourceValidationResult | undefined { @@ -520,13 +536,6 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend void; variableOptionGroup: { label: string; options: AzureMonitorOption[] }; setError: (source: string, error: AzureMonitorErrorish | undefined) => void; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.tsx index 3ff9b32cc35..af5fa69f081 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.tsx @@ -29,7 +29,7 @@ export const AzureCredentialsForm: FunctionComponent = (props: Props) => const hasRequiredFields = isCredentialsComplete(credentials); const [subscriptions, setSubscriptions] = useState>>([]); - const [loadSubscriptions, onLoadSubscriptions] = useReducer((val) => val + 1, 0); + const [loadSubscriptionsClicked, onLoadSubscriptions] = useReducer((val) => val + 1, 0); useEffect(() => { if (!getSubscriptions || !hasRequiredFields) { updateSubscriptions([]); @@ -38,7 +38,7 @@ export const AzureCredentialsForm: FunctionComponent = (props: Props) => let canceled = false; getSubscriptions().then((result) => { if (!canceled) { - updateSubscriptions(result); + updateSubscriptions(result, loadSubscriptionsClicked); } }); return () => { @@ -46,18 +46,18 @@ export const AzureCredentialsForm: FunctionComponent = (props: Props) => }; // This effect is intended to be called only once initially and on Load Subscriptions click // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loadSubscriptions]); + }, [loadSubscriptionsClicked]); - const updateSubscriptions = (received: Array>) => { + const updateSubscriptions = (received: Array>, autoSelect = false) => { setSubscriptions(received); if (getSubscriptions) { - if (!credentials.defaultSubscriptionId && received.length > 0) { - // Setting the default subscription if subscriptions received but no default subscription selected + if (autoSelect && !credentials.defaultSubscriptionId && received.length > 0) { + // Selecting the default subscription if subscriptions received but no default subscription selected onSubscriptionChange(received[0]); } else if (credentials.defaultSubscriptionId) { const found = received.find((opt) => opt.value === credentials.defaultSubscriptionId); if (!found) { - // Unsetting the default found if it isn't found among the received subscriptions + // Unselecting the default subscription if it isn't found among the received subscriptions onSubscriptionChange(undefined); } } diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx index e4e7ab85232..7a57f51ccf1 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/LogsQueryEditor.tsx @@ -10,7 +10,7 @@ import useMigrations from './useMigrations'; interface LogsQueryEditorProps { query: AzureMonitorQuery; datasource: Datasource; - subscriptionId: string; + subscriptionId?: string; onChange: (newQuery: AzureMonitorQuery) => void; variableOptionGroup: { label: string; options: AzureMonitorOption[] }; setError: (source: string, error: AzureMonitorErrorish | undefined) => void; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/WorkspaceField.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/WorkspaceField.tsx index e222cdd6a1f..9326624cf18 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/WorkspaceField.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/LogsQueryEditor/WorkspaceField.tsx @@ -19,6 +19,7 @@ const WorkspaceField: React.FC = ({ useEffect(() => { if (!subscriptionId) { workspaces.length > 0 && setWorkspaces([]); + return; } datasource diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.tsx index b9246a71d9d..842a358ba93 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricsQueryEditor.tsx @@ -19,7 +19,7 @@ import { InlineFieldRow } from '@grafana/ui'; interface MetricsQueryEditorProps { query: AzureMonitorQuery; datasource: Datasource; - subscriptionId: string; + subscriptionId?: string; onChange: (newQuery: AzureMonitorQuery) => void; variableOptionGroup: { label: string; options: AzureMonitorOption[] }; setError: (source: string, error: AzureMonitorErrorish | undefined) => void; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryEditor.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryEditor.tsx index 42d6af3b9fb..3de7f2a5347 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryEditor.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryEditor.tsx @@ -20,7 +20,7 @@ interface BaseQueryEditorProps { const QueryEditor: React.FC = ({ query, datasource, onChange }) => { const [errorMessage, setError] = useLastError(); - const subscriptionId = query.subscription || datasource.azureMonitorDatasource.subscriptionId; + const subscriptionId = query.subscription || datasource.azureMonitorDatasource.defaultSubscriptionId; const variableOptionGroup = { label: 'Template Variables', options: datasource.getVariables().map((v) => ({ label: v, value: v })), @@ -52,7 +52,7 @@ const QueryEditor: React.FC = ({ query, datasource, onChan }; interface EditorForQueryTypeProps extends BaseQueryEditorProps { - subscriptionId: string; + subscriptionId?: string; setError: (source: string, error: AzureMonitorErrorish | undefined) => void; } diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/SubscriptionField.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/SubscriptionField.tsx index 46db33ad2ad..9c636febbf5 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/SubscriptionField.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/SubscriptionField.tsx @@ -23,10 +23,6 @@ const SubscriptionField: React.FC = ({ const [subscriptions, setSubscriptions] = useState([]); useEffect(() => { - if (!datasource.azureMonitorDatasource.isConfigured()) { - return; - } - datasource.azureMonitorDatasource .getSubscriptions() .then((results) => { @@ -34,28 +30,22 @@ const SubscriptionField: React.FC = ({ setSubscriptions(newSubscriptions); setError(ERROR_SOURCE, undefined); - // Set a default subscription ID, if we can - let newSubscription = query.subscription; - - if (!newSubscription && query.queryType === AzureQueryType.AzureMonitor) { - newSubscription = datasource.azureMonitorDatasource.subscriptionId; - } else if (!query.subscription && query.queryType === AzureQueryType.LogAnalytics) { - newSubscription = datasource.azureLogAnalyticsDatasource.subscriptionId; - } + let newSubscription = query.subscription || datasource.azureMonitorDatasource.defaultSubscriptionId; if (!newSubscription && newSubscriptions.length > 0) { newSubscription = newSubscriptions[0].value; } - newSubscription !== query.subscription && + if (newSubscription && newSubscription !== query.subscription) { onQueryChange({ ...query, subscription: newSubscription, }); + } }) .catch((err) => setError(ERROR_SOURCE, err)); }, [ - datasource.azureLogAnalyticsDatasource?.subscriptionId, + datasource.azureMonitorDatasource?.defaultSubscriptionId, datasource.azureMonitorDatasource, onQueryChange, query, diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/metrics.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/metrics.ts index d6dd2af54e6..1fbf9b2af18 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/metrics.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/metrics.ts @@ -14,7 +14,7 @@ export interface MetricMetadata { export function useMetricsMetadata( datasource: Datasource, query: AzureMonitorQuery, - subscriptionId: string, + subscriptionId: string | undefined, onQueryChange: (newQuery: AzureMonitorQuery) => void ) { const [metricMetadata, setMetricMetadata] = useState({ 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 3376523f79f..7ca7a041022 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts @@ -150,26 +150,13 @@ export default class Datasource extends DataSourceApi { const promises: Array> = []; - if (this.azureMonitorDatasource.isConfigured()) { - promises.push(this.azureMonitorDatasource.testDatasource()); - } + promises.push(this.azureMonitorDatasource.testDatasource()); + promises.push(this.azureLogAnalyticsDatasource.testDatasource()); if (this.appInsightsDatasource.isConfigured()) { promises.push(this.appInsightsDatasource.testDatasource()); } - if (this.azureLogAnalyticsDatasource.isConfigured()) { - promises.push(this.azureLogAnalyticsDatasource.testDatasource()); - } - - if (promises.length === 0) { - return { - status: 'error', - message: `Nothing configured. At least one of the API's must be configured.`, - title: 'Error', - }; - } - return await Promise.all(promises).then((results) => { let status: 'success' | 'error' = 'success'; let message = ''; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/types/index.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/types/index.ts index cb696c186da..606be553933 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/types/index.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/types/index.ts @@ -222,7 +222,7 @@ export interface AzureMonitorOption { export interface AzureQueryEditorFieldProps { query: AzureMonitorQuery; datasource: Datasource; - subscriptionId: string; + subscriptionId?: string; variableOptionGroup: { label: string; options: AzureMonitorOption[] }; onQueryChange: (newQuery: AzureMonitorQuery) => void;