AzureMonitor: Credentials UI logic encapsulation (#33620)
* Convert credentials form to a functional component * Encapsulation of Azure credentials logic * Fix selection of subscriptions * Load subscriptions on init and save options only if they're unsaved * Functions for resolution of routes * Resolve routes of all API requests * Reset default value if not found anymore * Fix tests * Symbol to conceal secret
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
export function getManagementApiRoute(azureCloud: string): string {
|
||||
switch (azureCloud) {
|
||||
case 'azuremonitor':
|
||||
return 'azuremonitor';
|
||||
case 'chinaazuremonitor':
|
||||
return 'chinaazuremonitor';
|
||||
case 'govazuremonitor':
|
||||
return 'govazuremonitor';
|
||||
case 'germanyazuremonitor':
|
||||
return 'germanyazuremonitor';
|
||||
default:
|
||||
throw new Error('The cloud not supported.');
|
||||
}
|
||||
}
|
||||
|
||||
export function getLogAnalyticsManagementApiRoute(azureCloud: string): string {
|
||||
switch (azureCloud) {
|
||||
case 'azuremonitor':
|
||||
return 'workspacesloganalytics';
|
||||
case 'chinaazuremonitor':
|
||||
return 'chinaworkspacesloganalytics';
|
||||
case 'govazuremonitor':
|
||||
return 'govworkspacesloganalytics';
|
||||
default:
|
||||
throw new Error('The cloud not supported.');
|
||||
}
|
||||
}
|
||||
|
||||
export function getLogAnalyticsApiRoute(azureCloud: string): string {
|
||||
switch (azureCloud) {
|
||||
case 'azuremonitor':
|
||||
return 'loganalyticsazure';
|
||||
case 'chinaazuremonitor':
|
||||
return 'chinaloganalyticsazure';
|
||||
case 'govazuremonitor':
|
||||
return 'govloganalyticsazure';
|
||||
default:
|
||||
throw new Error('The cloud not supported.');
|
||||
}
|
||||
}
|
||||
|
||||
export function getAppInsightsApiRoute(azureCloud: string): string {
|
||||
switch (azureCloud) {
|
||||
case 'azuremonitor':
|
||||
return 'appinsights';
|
||||
case 'chinaazuremonitor':
|
||||
return 'chinaappinsights';
|
||||
default:
|
||||
throw new Error('The cloud not supported.');
|
||||
}
|
||||
}
|
||||
+6
-15
@@ -5,11 +5,14 @@ import { isString } from 'lodash';
|
||||
import TimegrainConverter from '../time_grain_converter';
|
||||
import { AzureDataSourceJsonData, AzureMonitorQuery, AzureQueryType } from '../types';
|
||||
import ResponseParser from './response_parser';
|
||||
import { getAzureCloud } from '../credentials';
|
||||
import { getAppInsightsApiRoute } from '../api/routes';
|
||||
|
||||
export interface LogAnalyticsColumn {
|
||||
text: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export default class AppInsightsDatasource extends DataSourceWithBackend<AzureMonitorQuery, AzureDataSourceJsonData> {
|
||||
url: string;
|
||||
baseUrl: string;
|
||||
@@ -21,21 +24,9 @@ export default class AppInsightsDatasource extends DataSourceWithBackend<AzureMo
|
||||
super(instanceSettings);
|
||||
this.applicationId = instanceSettings.jsonData.appInsightsAppId || '';
|
||||
|
||||
switch (instanceSettings.jsonData?.cloudName) {
|
||||
// Azure US Government
|
||||
case 'govazuremonitor':
|
||||
break;
|
||||
// Azure Germany
|
||||
case 'germanyazuremonitor':
|
||||
break;
|
||||
// Azue China
|
||||
case 'chinaazuremonitor':
|
||||
this.baseUrl = `/chinaappinsights/${this.version}/apps/${this.applicationId}`;
|
||||
break;
|
||||
// Azure Global
|
||||
default:
|
||||
this.baseUrl = `/appinsights/${this.version}/apps/${this.applicationId}`;
|
||||
}
|
||||
const cloud = getAzureCloud(instanceSettings);
|
||||
const route = getAppInsightsApiRoute(cloud);
|
||||
this.baseUrl = `/${route}/${this.version}/apps/${this.applicationId}`;
|
||||
|
||||
this.url = instanceSettings.url || '';
|
||||
}
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ describe('AzureLogAnalyticsDatasource', () => {
|
||||
});
|
||||
|
||||
it('should use the loganalyticsazure plugin route', () => {
|
||||
expect(workspacesUrl).toContain('azuremonitor');
|
||||
expect(workspacesUrl).toContain('workspacesloganalytics');
|
||||
expect(azureLogAnalyticsUrl).toContain('loganalyticsazure');
|
||||
});
|
||||
});
|
||||
|
||||
+10
-49
@@ -12,6 +12,8 @@ import {
|
||||
import { getBackendSrv, getTemplateSrv, DataSourceWithBackend, FetchResponse } from '@grafana/runtime';
|
||||
import { Observable, from } from 'rxjs';
|
||||
import { mergeMap } from 'rxjs/operators';
|
||||
import { getAzureCloud } from '../credentials';
|
||||
import { getLogAnalyticsApiRoute, getLogAnalyticsManagementApiRoute } from '../api/routes';
|
||||
import { AzureLogAnalyticsMetadata } from '../types/logAnalyticsMetadata';
|
||||
|
||||
export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
||||
@@ -22,11 +24,6 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
||||
baseUrl: string;
|
||||
applicationId: string;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* TODO: Which one of these values should be used? Was there a migration?
|
||||
* */
|
||||
logAnalyticsSubscriptionId: string;
|
||||
subscriptionId: string;
|
||||
|
||||
azureMonitorUrl: string;
|
||||
@@ -37,56 +34,20 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
||||
super(instanceSettings);
|
||||
this.cache = new Map();
|
||||
|
||||
switch (this.instanceSettings.jsonData.cloudName) {
|
||||
case 'govazuremonitor': // Azure US Government
|
||||
this.baseUrl = '/govloganalyticsazure';
|
||||
break;
|
||||
case 'germanyazuremonitor': // Azure Germany
|
||||
break;
|
||||
case 'chinaazuremonitor': // Azure China
|
||||
this.baseUrl = '/chinaloganalyticsazure';
|
||||
break;
|
||||
default:
|
||||
// Azure Global
|
||||
this.baseUrl = '/loganalyticsazure';
|
||||
}
|
||||
const cloud = getAzureCloud(instanceSettings);
|
||||
const logAnalyticsRoute = getLogAnalyticsApiRoute(cloud);
|
||||
this.baseUrl = `/${logAnalyticsRoute}`;
|
||||
|
||||
const managementRoute = getLogAnalyticsManagementApiRoute(cloud);
|
||||
this.azureMonitorUrl = `/${managementRoute}/subscriptions`;
|
||||
|
||||
this.url = instanceSettings.url || '';
|
||||
this.subscriptionId = this.instanceSettings.jsonData.logAnalyticsSubscriptionId || '';
|
||||
this.defaultOrFirstWorkspace = this.instanceSettings.jsonData.logAnalyticsDefaultWorkspace || '';
|
||||
|
||||
this.setWorkspaceUrl();
|
||||
}
|
||||
|
||||
isConfigured(): boolean {
|
||||
return (
|
||||
(!!this.instanceSettings.jsonData.logAnalyticsSubscriptionId &&
|
||||
this.instanceSettings.jsonData.logAnalyticsSubscriptionId.length > 0) ||
|
||||
!!this.instanceSettings.jsonData.azureLogAnalyticsSameAs
|
||||
);
|
||||
}
|
||||
|
||||
setWorkspaceUrl() {
|
||||
if (!!this.instanceSettings.jsonData.subscriptionId || !!this.instanceSettings.jsonData.azureLogAnalyticsSameAs) {
|
||||
this.subscriptionId = this.instanceSettings.jsonData.subscriptionId;
|
||||
const azureCloud = this.instanceSettings.jsonData.cloudName || 'azuremonitor';
|
||||
this.azureMonitorUrl = `/${azureCloud}/subscriptions`;
|
||||
} else {
|
||||
this.subscriptionId = this.instanceSettings.jsonData.logAnalyticsSubscriptionId || '';
|
||||
|
||||
switch (this.instanceSettings.jsonData.cloudName) {
|
||||
case 'govazuremonitor': // Azure US Government
|
||||
this.azureMonitorUrl = `/govworkspacesloganalytics/subscriptions`;
|
||||
break;
|
||||
case 'germanyazuremonitor': // Azure Germany
|
||||
break;
|
||||
case 'chinaazuremonitor': // Azure China
|
||||
this.azureMonitorUrl = `/chinaworkspacesloganalytics/subscriptions`;
|
||||
break;
|
||||
default:
|
||||
// Azure Global
|
||||
this.azureMonitorUrl = `/workspacesloganalytics/subscriptions`;
|
||||
}
|
||||
}
|
||||
return !!this.subscriptionId && this.subscriptionId.length > 0;
|
||||
}
|
||||
|
||||
async getWorkspaces(subscription: string): Promise<AzureLogsVariable[]> {
|
||||
|
||||
+11
-7
@@ -25,6 +25,8 @@ import { from, Observable } from 'rxjs';
|
||||
import { mergeMap } from 'rxjs/operators';
|
||||
|
||||
import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import { getAzureCloud } from '../credentials';
|
||||
import { getManagementApiRoute } from '../api/routes';
|
||||
|
||||
const defaultDropdownValue = 'select';
|
||||
|
||||
@@ -46,7 +48,6 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
resourceGroup: string;
|
||||
resourceName: string;
|
||||
url: string;
|
||||
cloudName: string;
|
||||
supportedMetricNamespaces: string[] = [];
|
||||
timeSrv: TimeSrv;
|
||||
|
||||
@@ -55,10 +56,13 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
|
||||
this.timeSrv = getTimeSrv();
|
||||
this.subscriptionId = instanceSettings.jsonData.subscriptionId;
|
||||
this.cloudName = instanceSettings.jsonData.cloudName || 'azuremonitor';
|
||||
this.baseUrl = `/${this.cloudName}/subscriptions`;
|
||||
|
||||
const cloud = getAzureCloud(instanceSettings);
|
||||
const route = getManagementApiRoute(cloud);
|
||||
this.baseUrl = `/${route}/subscriptions`;
|
||||
|
||||
this.url = instanceSettings.url!;
|
||||
this.supportedMetricNamespaces = new SupportedNamespaces(this.cloudName).get();
|
||||
this.supportedMetricNamespaces = new SupportedNamespaces(cloud).get();
|
||||
}
|
||||
|
||||
isConfigured(): boolean {
|
||||
@@ -313,8 +317,8 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
return getTemplateSrv().replace((metric || '').trim());
|
||||
}
|
||||
|
||||
getSubscriptions(route?: string) {
|
||||
const url = `/${route || this.cloudName}/subscriptions?api-version=2019-03-01`;
|
||||
getSubscriptions() {
|
||||
const url = `${this.baseUrl}?api-version=2019-03-01`;
|
||||
return this.doRequest(url).then((result: any) => {
|
||||
return ResponseParser.parseSubscriptions(result);
|
||||
});
|
||||
@@ -469,7 +473,7 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
});
|
||||
}
|
||||
|
||||
const url = `/${this.cloudName}/subscriptions?api-version=2019-03-01`;
|
||||
const url = `${this.baseUrl}?api-version=2019-03-01`;
|
||||
return this.doRequest(url)
|
||||
.then((response: any) => {
|
||||
if (response.status === 200) {
|
||||
|
||||
+31
-24
@@ -2,8 +2,8 @@ import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import AnalyticsConfig, { Props } from './AnalyticsConfig';
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props: Props = {
|
||||
const setup = (propsFunc?: (props: Props) => Props) => {
|
||||
let props: Props = {
|
||||
options: {
|
||||
id: 21,
|
||||
uid: 'x',
|
||||
@@ -38,18 +38,14 @@ const setup = (propOverrides?: object) => {
|
||||
version: 1,
|
||||
readOnly: false,
|
||||
},
|
||||
subscriptions: [],
|
||||
workspaces: [],
|
||||
makeSameAs: jest.fn(),
|
||||
onUpdateDatasourceOptions: jest.fn(),
|
||||
onUpdateJsonDataOption: jest.fn(),
|
||||
onUpdateSecureJsonDataOption: jest.fn(),
|
||||
onResetOptionKey: jest.fn(),
|
||||
onLoadSubscriptions: jest.fn(),
|
||||
onLoadWorkspaces: jest.fn(),
|
||||
updateOptions: jest.fn(),
|
||||
getSubscriptions: jest.fn(),
|
||||
getWorkspaces: jest.fn(),
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
if (propsFunc) {
|
||||
props = propsFunc(props);
|
||||
}
|
||||
|
||||
return shallow(<AnalyticsConfig {...props} />);
|
||||
};
|
||||
@@ -62,24 +58,35 @@ describe('Render', () => {
|
||||
});
|
||||
|
||||
it('should disable log analytics credentials form', () => {
|
||||
const wrapper = setup({
|
||||
jsonData: {
|
||||
azureLogAnalyticsSameAs: true,
|
||||
const wrapper = setup((props) => ({
|
||||
...props,
|
||||
options: {
|
||||
...props.options,
|
||||
jsonData: {
|
||||
...props.options.jsonData,
|
||||
azureLogAnalyticsSameAs: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
}));
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should enable azure log analytics load workspaces button', () => {
|
||||
const wrapper = setup({
|
||||
jsonData: {
|
||||
logAnalyticsDefaultWorkspace: '',
|
||||
logAnalyticsTenantId: 'e7f3f661-a933-4b3f-8176-51c4f982ec48',
|
||||
logAnalyticsClientId: '44693801-6ee6-49de-9b2d-9106972f9572',
|
||||
logAnalyticsSubscriptionId: 'e3fe4fde-ad5e-4d60-9974-e2f3562ffdf2',
|
||||
logAnalyticsClientSecret: 'cddcc020-2c94-460a-a3d0-df3147ffa792',
|
||||
const wrapper = setup((props) => ({
|
||||
...props,
|
||||
options: {
|
||||
...props.options,
|
||||
jsonData: {
|
||||
...props.options.jsonData,
|
||||
azureLogAnalyticsSameAs: false,
|
||||
logAnalyticsDefaultWorkspace: '',
|
||||
logAnalyticsTenantId: 'e7f3f661-a933-4b3f-8176-51c4f982ec48',
|
||||
logAnalyticsClientId: '44693801-6ee6-49de-9b2d-9106972f9572',
|
||||
logAnalyticsSubscriptionId: 'e3fe4fde-ad5e-4d60-9974-e2f3562ffdf2',
|
||||
logAnalyticsClientSecret: 'cddcc020-2c94-460a-a3d0-df3147ffa792',
|
||||
},
|
||||
},
|
||||
});
|
||||
}));
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
+161
-175
@@ -1,209 +1,195 @@
|
||||
import React, { PureComponent, ChangeEvent } from 'react';
|
||||
import React, { FunctionComponent, useEffect, useMemo, useReducer, useState } from 'react';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { AzureCredentialsForm } from './AzureCredentialsForm';
|
||||
import { InlineFormLabel, LegacyForms, Button } from '@grafana/ui';
|
||||
const { Select, Switch } = LegacyForms;
|
||||
import { AzureDataSourceSettings } from '../types';
|
||||
|
||||
export interface State {
|
||||
sameAsSwitched: boolean;
|
||||
}
|
||||
import { AzureDataSourceSettings, AzureCredentials } from '../types';
|
||||
import {
|
||||
getCredentials,
|
||||
getLogAnalyticsCredentials,
|
||||
isCredentialsComplete,
|
||||
updateLogAnalyticsCredentials,
|
||||
updateLogAnalyticsSameAs,
|
||||
} from '../credentials';
|
||||
|
||||
export interface Props {
|
||||
options: AzureDataSourceSettings;
|
||||
subscriptions: SelectableValue[];
|
||||
workspaces: SelectableValue[];
|
||||
makeSameAs: () => void;
|
||||
onUpdateDatasourceOptions: (options: AzureDataSourceSettings) => void;
|
||||
onUpdateJsonDataOption: (key: string, val: any) => void;
|
||||
onUpdateSecureJsonDataOption: (key: string, val: any) => void;
|
||||
onResetOptionKey: (key: string) => void;
|
||||
onLoadSubscriptions: (type?: string) => void;
|
||||
onLoadWorkspaces: (type?: string) => void;
|
||||
updateOptions: (optionsFunc: (options: AzureDataSourceSettings) => AzureDataSourceSettings) => void;
|
||||
getSubscriptions: () => Promise<Array<SelectableValue<string>>>;
|
||||
getWorkspaces: (subscriptionId: string) => Promise<Array<SelectableValue<string>>>;
|
||||
}
|
||||
export class AnalyticsConfig extends PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
sameAsSwitched: false,
|
||||
export const AnalyticsConfig: FunctionComponent<Props> = (props: Props) => {
|
||||
const { updateOptions, getSubscriptions, getWorkspaces } = props;
|
||||
const primaryCredentials = useMemo(() => getCredentials(props.options), [props.options]);
|
||||
const logAnalyticsCredentials = useMemo(() => getLogAnalyticsCredentials(props.options), [props.options]);
|
||||
const subscriptionId = logAnalyticsCredentials
|
||||
? props.options.jsonData.logAnalyticsSubscriptionId
|
||||
: props.options.jsonData.subscriptionId;
|
||||
|
||||
const hasRequiredFields =
|
||||
subscriptionId &&
|
||||
(logAnalyticsCredentials
|
||||
? isCredentialsComplete(logAnalyticsCredentials)
|
||||
: isCredentialsComplete(primaryCredentials));
|
||||
|
||||
const defaultWorkspace = props.options.jsonData.logAnalyticsDefaultWorkspace;
|
||||
|
||||
const [workspaces, setWorkspaces] = useState<SelectableValue[]>([]);
|
||||
const [loadWorkspaces, onLoadWorkspaces] = useReducer((val) => val + 1, 0);
|
||||
useEffect(() => {
|
||||
if (!hasRequiredFields || !subscriptionId) {
|
||||
updateWorkspaces([]);
|
||||
return;
|
||||
}
|
||||
let canceled = false;
|
||||
getWorkspaces(subscriptionId).then((result) => {
|
||||
if (!canceled) {
|
||||
updateWorkspaces(result);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
canceled = true;
|
||||
};
|
||||
}
|
||||
// This effect is intended to be called only once initially and on Load Workspaces click
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loadWorkspaces, subscriptionId]);
|
||||
|
||||
onLogAnalyticsTenantIdChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.props.onUpdateJsonDataOption('logAnalyticsTenantId', event.target.value);
|
||||
const updateWorkspaces = (received: Array<SelectableValue<string>>) => {
|
||||
setWorkspaces(received);
|
||||
if (!defaultWorkspace && received.length > 0) {
|
||||
// Setting the default workspace if workspaces received but no default workspace selected
|
||||
updateOptions((options) => {
|
||||
return {
|
||||
...options,
|
||||
jsonData: {
|
||||
...options.jsonData,
|
||||
logAnalyticsDefaultWorkspace: received[0].value,
|
||||
},
|
||||
};
|
||||
});
|
||||
} else if (defaultWorkspace) {
|
||||
const found = received.find((opt) => opt.value === defaultWorkspace);
|
||||
if (!found) {
|
||||
// Unsetting the default workspace if it isn't found among the received workspaces
|
||||
updateOptions((options) => {
|
||||
return {
|
||||
...options,
|
||||
jsonData: {
|
||||
...options.jsonData,
|
||||
logAnalyticsDefaultWorkspace: undefined,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onLogAnalyticsClientIdChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.props.onUpdateJsonDataOption('logAnalyticsClientId', event.target.value);
|
||||
const [sameAsSwitched, setSameAsSwitched] = useState(false);
|
||||
|
||||
const onCredentialsChange = (updatedCredentials: AzureCredentials) => {
|
||||
updateOptions((options) => updateLogAnalyticsCredentials(options, updatedCredentials));
|
||||
};
|
||||
|
||||
onLogAnalyticsClientSecretChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.props.onUpdateSecureJsonDataOption('logAnalyticsClientSecret', event.target.value);
|
||||
const onLogAnalyticsSameAsChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
const sameAs = event.currentTarget.checked;
|
||||
updateOptions((options) => updateLogAnalyticsSameAs(options, sameAs));
|
||||
setSameAsSwitched(true);
|
||||
};
|
||||
|
||||
onLogAnalyticsSubscriptionSelect = (logAnalyticsSubscription: SelectableValue<string>) => {
|
||||
this.props.onUpdateJsonDataOption('logAnalyticsSubscriptionId', logAnalyticsSubscription.value);
|
||||
};
|
||||
|
||||
onWorkspaceSelectChange = (logAnalyticsDefaultWorkspace: SelectableValue<string>) => {
|
||||
this.props.onUpdateJsonDataOption('logAnalyticsDefaultWorkspace', logAnalyticsDefaultWorkspace.value);
|
||||
};
|
||||
|
||||
onAzureLogAnalyticsSameAsChange = () => {
|
||||
const { options, onUpdateDatasourceOptions, makeSameAs } = this.props;
|
||||
|
||||
if (!options.jsonData.azureLogAnalyticsSameAs && options.secureJsonData!.clientSecret) {
|
||||
makeSameAs();
|
||||
} else if (!options.jsonData.azureLogAnalyticsSameAs) {
|
||||
// if currently off, clear monitor secret
|
||||
onUpdateDatasourceOptions({
|
||||
const onLogAnalyticsDefaultSubscriptionChange = (subscriptionId: string | undefined) => {
|
||||
updateOptions((options) => {
|
||||
return {
|
||||
...options,
|
||||
jsonData: {
|
||||
...options.jsonData,
|
||||
azureLogAnalyticsSameAs: !options.jsonData.azureLogAnalyticsSameAs,
|
||||
logAnalyticsSubscriptionId: subscriptionId || '',
|
||||
},
|
||||
secureJsonData: {
|
||||
...options.secureJsonData,
|
||||
clientSecret: '',
|
||||
},
|
||||
secureJsonFields: {
|
||||
clientSecret: false,
|
||||
},
|
||||
});
|
||||
|
||||
this.setState({
|
||||
sameAsSwitched: true,
|
||||
});
|
||||
} else {
|
||||
this.props.onUpdateJsonDataOption('azureLogAnalyticsSameAs', !options.jsonData.azureLogAnalyticsSameAs);
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
onLogAnalyticsResetClientSecret = () => {
|
||||
this.props.onResetOptionKey('logAnalyticsClientSecret');
|
||||
const onDefaultWorkspaceChange = (selected: SelectableValue<string>) => {
|
||||
updateOptions((options) => {
|
||||
return {
|
||||
...options,
|
||||
jsonData: {
|
||||
...options.jsonData,
|
||||
logAnalyticsDefaultWorkspace: selected.value || '',
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
hasWorkspaceRequiredFields = () => {
|
||||
const {
|
||||
options: { jsonData, secureJsonData, secureJsonFields },
|
||||
} = this.props;
|
||||
|
||||
if (jsonData.azureLogAnalyticsSameAs) {
|
||||
return (
|
||||
jsonData.tenantId &&
|
||||
jsonData.clientId &&
|
||||
jsonData.subscriptionId &&
|
||||
(secureJsonData!.clientSecret || secureJsonFields.clientSecret)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
jsonData.logAnalyticsTenantId &&
|
||||
jsonData.logAnalyticsTenantId.length &&
|
||||
jsonData.logAnalyticsClientId &&
|
||||
jsonData.logAnalyticsClientId.length &&
|
||||
jsonData.logAnalyticsSubscriptionId &&
|
||||
(secureJsonFields.logAnalyticsClientSecret || secureJsonData!.logAnalyticsClientSecret)
|
||||
);
|
||||
const tooltipAttribute = {
|
||||
...(!logAnalyticsCredentials && {
|
||||
tooltip: 'Workspaces are pulled from default subscription selected above.',
|
||||
}),
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
options: { jsonData, secureJsonData, secureJsonFields },
|
||||
subscriptions,
|
||||
workspaces,
|
||||
} = this.props;
|
||||
const showSameAsHelpMsg = sameAsSwitched && !primaryCredentials.clientSecret;
|
||||
|
||||
const { sameAsSwitched } = this.state;
|
||||
|
||||
if (!jsonData.hasOwnProperty('azureLogAnalyticsSameAs')) {
|
||||
jsonData.azureLogAnalyticsSameAs = true;
|
||||
}
|
||||
|
||||
const addtlAttrs = {
|
||||
...(jsonData.azureLogAnalyticsSameAs && {
|
||||
tooltip: 'Workspaces are pulled from default subscription selected above.',
|
||||
}),
|
||||
};
|
||||
|
||||
const showSameAsHelpMsg =
|
||||
sameAsSwitched &&
|
||||
jsonData.azureLogAnalyticsSameAs &&
|
||||
secureJsonFields &&
|
||||
!secureJsonFields.clientSecret &&
|
||||
!secureJsonData!.clientSecret;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3 className="page-heading">Azure Monitor Logs Details</h3>
|
||||
<Switch
|
||||
label="Same details as Azure Monitor API"
|
||||
checked={jsonData.azureLogAnalyticsSameAs ?? false}
|
||||
onChange={this.onAzureLogAnalyticsSameAsChange}
|
||||
{...addtlAttrs}
|
||||
return (
|
||||
<>
|
||||
<h3 className="page-heading">Azure Monitor Logs Details</h3>
|
||||
<Switch
|
||||
label="Same details as Azure Monitor API"
|
||||
checked={!logAnalyticsCredentials}
|
||||
onChange={onLogAnalyticsSameAsChange}
|
||||
{...tooltipAttribute}
|
||||
/>
|
||||
{showSameAsHelpMsg && (
|
||||
<div className="grafana-info-box m-t-2">
|
||||
<div className="alert-body">
|
||||
<p>Re-enter your Azure Monitor Client Secret to use this setting.</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{logAnalyticsCredentials && (
|
||||
<AzureCredentialsForm
|
||||
credentials={logAnalyticsCredentials}
|
||||
defaultSubscription={subscriptionId}
|
||||
onCredentialsChange={onCredentialsChange}
|
||||
onDefaultSubscriptionChange={onLogAnalyticsDefaultSubscriptionChange}
|
||||
getSubscriptions={getSubscriptions}
|
||||
/>
|
||||
{showSameAsHelpMsg && (
|
||||
<div className="grafana-info-box m-t-2">
|
||||
<div className="alert-body">
|
||||
<p>Re-enter your Azure Monitor Client Secret to use this setting.</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!jsonData.azureLogAnalyticsSameAs && (
|
||||
<AzureCredentialsForm
|
||||
subscriptionOptions={subscriptions}
|
||||
selectedSubscription={jsonData.logAnalyticsSubscriptionId}
|
||||
tenantId={jsonData.logAnalyticsTenantId}
|
||||
clientId={jsonData.logAnalyticsClientId}
|
||||
clientSecret={secureJsonData!.logAnalyticsClientSecret}
|
||||
clientSecretConfigured={secureJsonFields.logAnalyticsClientSecret}
|
||||
onSubscriptionSelectChange={this.onLogAnalyticsSubscriptionSelect}
|
||||
onTenantIdChange={this.onLogAnalyticsTenantIdChange}
|
||||
onClientIdChange={this.onLogAnalyticsClientIdChange}
|
||||
onClientSecretChange={this.onLogAnalyticsClientSecretChange}
|
||||
onResetClientSecret={this.onLogAnalyticsResetClientSecret}
|
||||
onLoadSubscriptions={() => this.props.onLoadSubscriptions('workspacesloganalytics')}
|
||||
/>
|
||||
)}
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel
|
||||
className="width-12"
|
||||
tooltip="Choose the default/preferred Workspace for Azure Log Analytics queries."
|
||||
>
|
||||
Default Workspace
|
||||
</InlineFormLabel>
|
||||
<div className="width-25">
|
||||
<Select
|
||||
value={workspaces.find((workspace) => workspace.value === jsonData.logAnalyticsDefaultWorkspace)}
|
||||
options={workspaces}
|
||||
defaultValue={jsonData.logAnalyticsDefaultWorkspace}
|
||||
onChange={this.onWorkspaceSelectChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<div className="max-width-30 gf-form-inline">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
type="button"
|
||||
onClick={() => this.props.onLoadWorkspaces()}
|
||||
disabled={!this.hasWorkspaceRequiredFields()}
|
||||
>
|
||||
Load Workspaces
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel
|
||||
className="width-12"
|
||||
tooltip="Choose the default/preferred Workspace for Azure Log Analytics queries."
|
||||
>
|
||||
Default Workspace
|
||||
</InlineFormLabel>
|
||||
<div className="width-25">
|
||||
<Select
|
||||
value={workspaces.find((opt) => opt.value === defaultWorkspace)}
|
||||
options={workspaces}
|
||||
onChange={onDefaultWorkspaceChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<div className="max-width-30 gf-form-inline">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
type="button"
|
||||
onClick={onLoadWorkspaces}
|
||||
disabled={!hasRequiredFields}
|
||||
>
|
||||
Load Workspaces
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnalyticsConfig;
|
||||
|
||||
+33
-23
@@ -2,31 +2,29 @@ import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import AzureCredentialsForm, { Props } from './AzureCredentialsForm';
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props: Props = {
|
||||
selectedAzureCloud: 'azuremonitor',
|
||||
selectedSubscription: '44987801-6nn6-49he-9b2d-9106972f9789',
|
||||
const setup = (propsFunc?: (props: Props) => Props) => {
|
||||
let props: Props = {
|
||||
credentials: {
|
||||
azureCloud: 'azuremonitor',
|
||||
tenantId: 'e7f3f661-a933-3h3f-0294-31c4f962ec48',
|
||||
clientId: '34509fad-c0r9-45df-9e25-f1ee34af6900',
|
||||
clientSecret: undefined,
|
||||
},
|
||||
defaultSubscription: '44987801-6nn6-49he-9b2d-9106972f9789',
|
||||
azureCloudOptions: [
|
||||
{ value: 'azuremonitor', label: 'Azure' },
|
||||
{ value: 'govazuremonitor', label: 'Azure US Government' },
|
||||
{ value: 'germanyazuremonitor', label: 'Azure Germany' },
|
||||
{ value: 'chinaazuremonitor', label: 'Azure China' },
|
||||
],
|
||||
tenantId: 'e7f3f661-a933-3h3f-0294-31c4f962ec48',
|
||||
clientId: '34509fad-c0r9-45df-9e25-f1ee34af6900',
|
||||
clientSecret: '',
|
||||
clientSecretConfigured: false,
|
||||
subscriptionOptions: [],
|
||||
onAzureCloudChange: jest.fn(),
|
||||
onSubscriptionSelectChange: jest.fn(),
|
||||
onTenantIdChange: jest.fn(),
|
||||
onClientIdChange: jest.fn(),
|
||||
onClientSecretChange: jest.fn(),
|
||||
onResetClientSecret: jest.fn(),
|
||||
onLoadSubscriptions: jest.fn(),
|
||||
onCredentialsChange: jest.fn(),
|
||||
onDefaultSubscriptionChange: jest.fn(),
|
||||
getSubscriptions: jest.fn(),
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
if (propsFunc) {
|
||||
props = propsFunc(props);
|
||||
}
|
||||
|
||||
return shallow(<AzureCredentialsForm {...props} />);
|
||||
};
|
||||
@@ -38,16 +36,28 @@ describe('Render', () => {
|
||||
});
|
||||
|
||||
it('should disable azure monitor secret input', () => {
|
||||
const wrapper = setup({
|
||||
clientSecretConfigured: true,
|
||||
});
|
||||
const wrapper = setup((props) => ({
|
||||
...props,
|
||||
credentials: {
|
||||
azureCloud: 'azuremonitor',
|
||||
tenantId: 'e7f3f661-a933-3h3f-0294-31c4f962ec48',
|
||||
clientId: '34509fad-c0r9-45df-9e25-f1ee34af6900',
|
||||
clientSecret: Symbol(),
|
||||
},
|
||||
}));
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should enable azure monitor load subscriptions button', () => {
|
||||
const wrapper = setup({
|
||||
clientSecret: 'e7f3f661-a933-4b3f-8176-51c4f982ec48',
|
||||
});
|
||||
const wrapper = setup((props) => ({
|
||||
...props,
|
||||
credentials: {
|
||||
azureCloud: 'azuremonitor',
|
||||
tenantId: 'e7f3f661-a933-3h3f-0294-31c4f962ec48',
|
||||
clientId: '34509fad-c0r9-45df-9e25-f1ee34af6900',
|
||||
clientSecret: 'e7f3f661-a933-4b3f-8176-51c4f982ec48',
|
||||
},
|
||||
}));
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
+200
-129
@@ -1,158 +1,229 @@
|
||||
import React, { ChangeEvent, PureComponent } from 'react';
|
||||
import React, { ChangeEvent, FunctionComponent, useEffect, useReducer, useState } from 'react';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { InlineFormLabel, LegacyForms, Button } from '@grafana/ui';
|
||||
import { AzureCredentials } from '../types';
|
||||
import { isCredentialsComplete } from '../credentials';
|
||||
const { Select, Input } = LegacyForms;
|
||||
|
||||
export interface Props {
|
||||
selectedAzureCloud?: string;
|
||||
selectedSubscription?: string;
|
||||
credentials: AzureCredentials;
|
||||
defaultSubscription?: string;
|
||||
azureCloudOptions?: SelectableValue[];
|
||||
tenantId?: string;
|
||||
clientId?: string;
|
||||
clientSecret?: string;
|
||||
clientSecretConfigured?: boolean;
|
||||
subscriptionOptions?: SelectableValue[];
|
||||
onAzureCloudChange?: (value: SelectableValue<string>) => void;
|
||||
onSubscriptionSelectChange?: (value: SelectableValue<string>) => void;
|
||||
onTenantIdChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||
onClientIdChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||
onClientSecretChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||
onResetClientSecret: () => void;
|
||||
onLoadSubscriptions?: () => void;
|
||||
onCredentialsChange: (updatedCredentials: AzureCredentials) => void;
|
||||
onDefaultSubscriptionChange?: (subscriptionId: string | undefined) => void;
|
||||
getSubscriptions?: () => Promise<SelectableValue[]>;
|
||||
}
|
||||
|
||||
export class AzureCredentialsForm extends PureComponent<Props> {
|
||||
render() {
|
||||
const {
|
||||
selectedAzureCloud,
|
||||
selectedSubscription,
|
||||
tenantId,
|
||||
clientId,
|
||||
clientSecret,
|
||||
clientSecretConfigured,
|
||||
azureCloudOptions,
|
||||
subscriptionOptions,
|
||||
onAzureCloudChange,
|
||||
onSubscriptionSelectChange,
|
||||
onTenantIdChange,
|
||||
onClientIdChange,
|
||||
onClientSecretChange,
|
||||
onResetClientSecret,
|
||||
onLoadSubscriptions,
|
||||
} = this.props;
|
||||
const hasRequiredFields = tenantId && clientId && (clientSecret || clientSecretConfigured);
|
||||
const hasSubscriptions = onLoadSubscriptions && subscriptionOptions;
|
||||
export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) => {
|
||||
const {
|
||||
credentials,
|
||||
defaultSubscription,
|
||||
azureCloudOptions,
|
||||
onCredentialsChange,
|
||||
onDefaultSubscriptionChange,
|
||||
getSubscriptions,
|
||||
} = props;
|
||||
const hasRequiredFields = isCredentialsComplete(credentials);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="gf-form-group">
|
||||
{azureCloudOptions && (
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel className="width-12" tooltip="Choose an Azure Cloud.">
|
||||
Azure Cloud
|
||||
</InlineFormLabel>
|
||||
<Select
|
||||
className="width-15"
|
||||
value={azureCloudOptions.find((azureCloud) => azureCloud.value === selectedAzureCloud)}
|
||||
options={azureCloudOptions}
|
||||
defaultValue={selectedAzureCloud}
|
||||
onChange={onAzureCloudChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
const [subscriptions, setSubscriptions] = useState<Array<SelectableValue<string>>>([]);
|
||||
const [loadSubscriptions, onLoadSubscriptions] = useReducer((val) => val + 1, 0);
|
||||
useEffect(() => {
|
||||
if (!getSubscriptions || !hasRequiredFields) {
|
||||
updateSubscriptions([]);
|
||||
return;
|
||||
}
|
||||
let canceled = false;
|
||||
getSubscriptions().then((result) => {
|
||||
if (!canceled) {
|
||||
updateSubscriptions(result);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
canceled = true;
|
||||
};
|
||||
// This effect is intended to be called only once initially and on Load Subscriptions click
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loadSubscriptions]);
|
||||
|
||||
const updateSubscriptions = (received: Array<SelectableValue<string>>) => {
|
||||
setSubscriptions(received);
|
||||
if (onDefaultSubscriptionChange) {
|
||||
if (!defaultSubscription && received.length > 0) {
|
||||
// Setting the default subscription if subscriptions received but no default subscription selected
|
||||
onDefaultSubscriptionChange(received[0].value);
|
||||
} else if (defaultSubscription) {
|
||||
const found = received.find((opt) => opt.value === defaultSubscription);
|
||||
if (!found) {
|
||||
// Unsetting the default found if it isn't found among the received subscriptions
|
||||
onDefaultSubscriptionChange(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onAzureCloudChange = (selected: SelectableValue<string>) => {
|
||||
if (onCredentialsChange) {
|
||||
const updated: AzureCredentials = {
|
||||
...credentials,
|
||||
azureCloud: selected.value,
|
||||
};
|
||||
onCredentialsChange(updated);
|
||||
}
|
||||
};
|
||||
|
||||
const onTenantIdChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (onCredentialsChange) {
|
||||
const updated: AzureCredentials = {
|
||||
...credentials,
|
||||
tenantId: event.target.value,
|
||||
};
|
||||
onCredentialsChange(updated);
|
||||
}
|
||||
};
|
||||
|
||||
const onClientIdChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (onCredentialsChange) {
|
||||
const updated: AzureCredentials = {
|
||||
...credentials,
|
||||
clientId: event.target.value,
|
||||
};
|
||||
onCredentialsChange(updated);
|
||||
}
|
||||
};
|
||||
|
||||
const onClientSecretChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (onCredentialsChange) {
|
||||
const updated: AzureCredentials = {
|
||||
...credentials,
|
||||
clientSecret: event.target.value,
|
||||
};
|
||||
onCredentialsChange(updated);
|
||||
}
|
||||
};
|
||||
|
||||
const onClientSecretReset = () => {
|
||||
if (onCredentialsChange) {
|
||||
const updated: AzureCredentials = {
|
||||
...credentials,
|
||||
clientSecret: '',
|
||||
};
|
||||
onCredentialsChange(updated);
|
||||
}
|
||||
};
|
||||
|
||||
const onSubscriptionChange = (selected: SelectableValue<string>) => {
|
||||
if (onDefaultSubscriptionChange) {
|
||||
onDefaultSubscriptionChange(selected?.value);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="gf-form-group">
|
||||
{azureCloudOptions && (
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel className="width-12">Directory (tenant) ID</InlineFormLabel>
|
||||
<InlineFormLabel className="width-12" tooltip="Choose an Azure Cloud.">
|
||||
Azure Cloud
|
||||
</InlineFormLabel>
|
||||
<Select
|
||||
className="width-15"
|
||||
value={azureCloudOptions.find((opt) => opt.value === credentials.azureCloud)}
|
||||
options={azureCloudOptions}
|
||||
onChange={onAzureCloudChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel className="width-12">Directory (tenant) ID</InlineFormLabel>
|
||||
<div className="width-15">
|
||||
<Input
|
||||
className="width-30"
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value={credentials.tenantId || ''}
|
||||
onChange={onTenantIdChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel className="width-12">Application (client) ID</InlineFormLabel>
|
||||
<div className="width-15">
|
||||
<Input
|
||||
className="width-30"
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value={credentials.clientId || ''}
|
||||
onChange={onClientIdChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{typeof credentials.clientSecret === 'symbol' ? (
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel className="width-12">Client Secret</InlineFormLabel>
|
||||
<Input className="width-25" placeholder="configured" disabled={true} />
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<div className="max-width-30 gf-form-inline">
|
||||
<Button variant="secondary" type="button" onClick={onClientSecretReset}>
|
||||
reset
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel className="width-12">Client Secret</InlineFormLabel>
|
||||
<div className="width-15">
|
||||
<Input
|
||||
className="width-30"
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value={tenantId || ''}
|
||||
onChange={onTenantIdChange}
|
||||
value={credentials.clientSecret || ''}
|
||||
onChange={onClientSecretChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel className="width-12">Application (client) ID</InlineFormLabel>
|
||||
<div className="width-15">
|
||||
<Input
|
||||
className="width-30"
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value={clientId || ''}
|
||||
onChange={onClientIdChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{clientSecretConfigured ? (
|
||||
)}
|
||||
{getSubscriptions && onDefaultSubscriptionChange && (
|
||||
<>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel className="width-12">Client Secret</InlineFormLabel>
|
||||
<Input className="width-25" placeholder="configured" disabled={true} />
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<div className="max-width-30 gf-form-inline">
|
||||
<Button variant="secondary" type="button" onClick={onResetClientSecret}>
|
||||
reset
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel className="width-12">Client Secret</InlineFormLabel>
|
||||
<div className="width-15">
|
||||
<Input
|
||||
className="width-30"
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value={clientSecret || ''}
|
||||
onChange={onClientSecretChange}
|
||||
<InlineFormLabel className="width-12">Default Subscription</InlineFormLabel>
|
||||
<div className="width-25">
|
||||
<Select
|
||||
value={subscriptions.find((opt) => opt.value === defaultSubscription)}
|
||||
options={subscriptions}
|
||||
onChange={onSubscriptionChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{hasSubscriptions && (
|
||||
<>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel className="width-12">Default Subscription</InlineFormLabel>
|
||||
<div className="width-25">
|
||||
<Select
|
||||
value={subscriptionOptions!.find((subscription) => subscription.value === selectedSubscription)}
|
||||
options={subscriptionOptions}
|
||||
defaultValue={selectedSubscription}
|
||||
onChange={onSubscriptionSelectChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<div className="max-width-30 gf-form-inline">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
type="button"
|
||||
onClick={onLoadSubscriptions}
|
||||
disabled={!hasRequiredFields}
|
||||
>
|
||||
Load Subscriptions
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<div className="max-width-30 gf-form-inline">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
type="button"
|
||||
onClick={onLoadSubscriptions}
|
||||
disabled={!hasRequiredFields}
|
||||
>
|
||||
Load Subscriptions
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AzureCredentialsForm;
|
||||
|
||||
+64
-207
@@ -1,43 +1,35 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import {
|
||||
SelectableValue,
|
||||
DataSourcePluginOptionsEditorProps,
|
||||
SelectableValue,
|
||||
updateDatasourcePluginJsonDataOption,
|
||||
updateDatasourcePluginOption,
|
||||
updateDatasourcePluginResetOption,
|
||||
updateDatasourcePluginJsonDataOption,
|
||||
updateDatasourcePluginSecureJsonDataOption,
|
||||
} from '@grafana/data';
|
||||
import { MonitorConfig } from './MonitorConfig';
|
||||
import { AnalyticsConfig } from './AnalyticsConfig';
|
||||
import { getBackendSrv, TemplateSrv, getTemplateSrv } from '@grafana/runtime';
|
||||
import { getBackendSrv, getTemplateSrv, TemplateSrv } from '@grafana/runtime';
|
||||
import { InsightsConfig } from './InsightsConfig';
|
||||
import ResponseParser from '../azure_monitor/response_parser';
|
||||
import { AzureDataSourceJsonData, AzureDataSourceSecureJsonData, AzureDataSourceSettings } from '../types';
|
||||
import { makePromiseCancelable, CancelablePromise } from 'app/core/utils/CancelablePromise';
|
||||
import { getAzureCloud } from '../credentials';
|
||||
import { getLogAnalyticsManagementApiRoute, getManagementApiRoute } from '../api/routes';
|
||||
|
||||
export type Props = DataSourcePluginOptionsEditorProps<AzureDataSourceJsonData, AzureDataSourceSecureJsonData>;
|
||||
|
||||
export interface State {
|
||||
subscriptions: SelectableValue[];
|
||||
logAnalyticsSubscriptions: SelectableValue[];
|
||||
logAnalyticsWorkspaces: SelectableValue[];
|
||||
subscriptionId: string;
|
||||
logAnalyticsSubscriptionId: string;
|
||||
unsaved: boolean;
|
||||
}
|
||||
|
||||
export class ConfigEditor extends PureComponent<Props, State> {
|
||||
initPromise: CancelablePromise<any> | null = null;
|
||||
templateSrv: TemplateSrv = getTemplateSrv();
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
subscriptions: [],
|
||||
logAnalyticsSubscriptions: [],
|
||||
logAnalyticsWorkspaces: [],
|
||||
subscriptionId: '',
|
||||
logAnalyticsSubscriptionId: '',
|
||||
unsaved: false,
|
||||
};
|
||||
|
||||
if (this.props.options.id) {
|
||||
@@ -45,115 +37,31 @@ export class ConfigEditor extends PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.initPromise = makePromiseCancelable(this.init());
|
||||
this.initPromise.promise.catch(({ isCanceled }) => {
|
||||
if (isCanceled) {
|
||||
console.warn('Azure Monitor ConfigEditor has unmounted, intialization was canceled');
|
||||
}
|
||||
});
|
||||
}
|
||||
private updateOptions = (optionsFunc: (options: AzureDataSourceSettings) => AzureDataSourceSettings): void => {
|
||||
const updated = optionsFunc(this.props.options);
|
||||
this.props.onOptionsChange(updated);
|
||||
|
||||
componentWillUnmount() {
|
||||
this.initPromise!.cancel();
|
||||
}
|
||||
this.setState({ unsaved: true });
|
||||
};
|
||||
|
||||
init = async () => {
|
||||
await this.getSubscriptions();
|
||||
private saveOptions = async (): Promise<void> => {
|
||||
if (this.state.unsaved) {
|
||||
await getBackendSrv()
|
||||
.put(`/api/datasources/${this.props.options.id}`, this.props.options)
|
||||
.then((result: { datasource: AzureDataSourceSettings }) => {
|
||||
updateDatasourcePluginOption(this.props, 'version', result.datasource.version);
|
||||
});
|
||||
|
||||
if (!this.props.options.jsonData.azureLogAnalyticsSameAs) {
|
||||
await this.getLogAnalyticsSubscriptions();
|
||||
this.setState({ unsaved: false });
|
||||
}
|
||||
};
|
||||
|
||||
updateJsonDataOption = (key: keyof AzureDataSourceJsonData, val: any) => {
|
||||
updateDatasourcePluginJsonDataOption(this.props, key, val);
|
||||
};
|
||||
private getSubscriptions = async (): Promise<Array<SelectableValue<string>>> => {
|
||||
await this.saveOptions();
|
||||
|
||||
updateSecureJsonDataOption = (key: keyof AzureDataSourceSecureJsonData, val: any) => {
|
||||
updateDatasourcePluginSecureJsonDataOption(this.props, key, val);
|
||||
};
|
||||
|
||||
resetSecureKey = (key: keyof AzureDataSourceSecureJsonData) => {
|
||||
updateDatasourcePluginResetOption(this.props, key);
|
||||
};
|
||||
|
||||
onUpdateJsonDataOption = (key: keyof AzureDataSourceJsonData) => (
|
||||
event: React.SyntheticEvent<HTMLInputElement | HTMLSelectElement>
|
||||
) => {
|
||||
this.updateJsonDataOption(key, event.currentTarget.value);
|
||||
};
|
||||
|
||||
onUpdateSecureJsonDataOption = (key: keyof AzureDataSourceSecureJsonData) => (
|
||||
event: React.SyntheticEvent<HTMLInputElement | HTMLSelectElement>
|
||||
) => {
|
||||
this.updateSecureJsonDataOption(key, event.currentTarget.value);
|
||||
};
|
||||
|
||||
makeSameAs = (updatedClientSecret?: string) => {
|
||||
const { options } = this.props;
|
||||
const clientSecret = updatedClientSecret || options.secureJsonData!.clientSecret;
|
||||
|
||||
this.props.onOptionsChange({
|
||||
...options,
|
||||
jsonData: {
|
||||
...options.jsonData,
|
||||
azureLogAnalyticsSameAs: true,
|
||||
logAnalyticsSubscriptionId: options.jsonData.subscriptionId,
|
||||
logAnalyticsTenantId: options.jsonData.tenantId,
|
||||
logAnalyticsClientId: options.jsonData.clientId,
|
||||
},
|
||||
secureJsonData: {
|
||||
...options.secureJsonData,
|
||||
clientSecret,
|
||||
logAnalyticsClientSecret: clientSecret,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
hasNecessaryCredentials = () => {
|
||||
if (!this.props.options.secureJsonFields.clientSecret && !this.props.options.secureJsonData!.clientSecret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.props.options.jsonData.clientId || !this.props.options.jsonData.tenantId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
logAnalyticsHasNecessaryCredentials = () => {
|
||||
if (
|
||||
!this.props.options.secureJsonFields.logAnalyticsClientSecret &&
|
||||
!this.props.options.secureJsonData!.logAnalyticsClientSecret
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.props.options.jsonData.logAnalyticsClientId || !this.props.options.jsonData.logAnalyticsTenantId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
onLoadSubscriptions = async (type?: string) => {
|
||||
await getBackendSrv()
|
||||
.put(`/api/datasources/${this.props.options.id}`, this.props.options)
|
||||
.then((result: AzureDataSourceSettings) => {
|
||||
updateDatasourcePluginOption(this.props, 'version', result.version);
|
||||
});
|
||||
|
||||
if (type && type === 'workspacesloganalytics') {
|
||||
this.getLogAnalyticsSubscriptions();
|
||||
} else {
|
||||
this.getSubscriptions();
|
||||
}
|
||||
};
|
||||
|
||||
loadSubscriptions = async (route?: string) => {
|
||||
const url = `/${route || this.props.options.jsonData.cloudName}/subscriptions?api-version=2019-03-01`;
|
||||
const cloud = getAzureCloud(this.props.options);
|
||||
const route = getManagementApiRoute(cloud);
|
||||
const url = `/${route}/subscriptions?api-version=2019-03-01`;
|
||||
|
||||
const result = await getBackendSrv().datasourceRequest({
|
||||
url: this.props.options.url + url,
|
||||
@@ -163,122 +71,71 @@ export class ConfigEditor extends PureComponent<Props, State> {
|
||||
return ResponseParser.parseSubscriptionsForSelect(result);
|
||||
};
|
||||
|
||||
loadWorkspaces = async (subscription: string) => {
|
||||
const { azureLogAnalyticsSameAs, cloudName, logAnalyticsSubscriptionId } = this.props.options.jsonData;
|
||||
let azureMonitorUrl = '',
|
||||
subscriptionId = this.templateSrv.replace(subscription || this.props.options.jsonData.subscriptionId);
|
||||
private getLogAnalyticsSubscriptions = async (): Promise<Array<SelectableValue<string>>> => {
|
||||
await this.saveOptions();
|
||||
|
||||
if (azureLogAnalyticsSameAs) {
|
||||
const azureCloud = cloudName || 'azuremonitor';
|
||||
azureMonitorUrl = `/${azureCloud}/subscriptions`;
|
||||
} else {
|
||||
subscriptionId = logAnalyticsSubscriptionId!;
|
||||
azureMonitorUrl = `/workspacesloganalytics/subscriptions`;
|
||||
}
|
||||
|
||||
const workspaceListUrl =
|
||||
azureMonitorUrl +
|
||||
`/${subscriptionId}/providers/Microsoft.OperationalInsights/workspaces?api-version=2017-04-26-preview`;
|
||||
const cloud = getAzureCloud(this.props.options);
|
||||
const route = getLogAnalyticsManagementApiRoute(cloud);
|
||||
const url = `/${route}/subscriptions?api-version=2019-03-01`;
|
||||
|
||||
const result = await getBackendSrv().datasourceRequest({
|
||||
url: this.props.options.url + workspaceListUrl,
|
||||
url: this.props.options.url + url,
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
return ResponseParser.parseSubscriptionsForSelect(result);
|
||||
};
|
||||
|
||||
private getWorkspaces = async (subscriptionId: string): Promise<Array<SelectableValue<string>>> => {
|
||||
await this.saveOptions();
|
||||
|
||||
const cloud = getAzureCloud(this.props.options);
|
||||
const route = getLogAnalyticsManagementApiRoute(cloud);
|
||||
const url = `/${route}/subscriptions/${subscriptionId}/providers/Microsoft.OperationalInsights/workspaces?api-version=2017-04-26-preview`;
|
||||
|
||||
const result = await getBackendSrv().datasourceRequest({
|
||||
url: this.props.options.url + url,
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
return ResponseParser.parseWorkspacesForSelect(result);
|
||||
};
|
||||
|
||||
getSubscriptions = async () => {
|
||||
if (!this.hasNecessaryCredentials()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const subscriptions = ((await this.loadSubscriptions()) || []) as SelectableValue[];
|
||||
|
||||
if (subscriptions && subscriptions.length > 0) {
|
||||
this.setState({ subscriptions });
|
||||
|
||||
this.updateJsonDataOption('subscriptionId', this.props.options.jsonData.subscriptionId || subscriptions[0].value);
|
||||
}
|
||||
|
||||
if (this.props.options.jsonData.subscriptionId && this.props.options.jsonData.azureLogAnalyticsSameAs) {
|
||||
await this.getWorkspaces();
|
||||
}
|
||||
// TODO: Used only by InsightsConfig
|
||||
private onUpdateJsonDataOption = (key: keyof AzureDataSourceJsonData) => (
|
||||
event: React.SyntheticEvent<HTMLInputElement | HTMLSelectElement>
|
||||
) => {
|
||||
updateDatasourcePluginJsonDataOption(this.props, key, event.currentTarget.value);
|
||||
};
|
||||
|
||||
getLogAnalyticsSubscriptions = async () => {
|
||||
if (!this.logAnalyticsHasNecessaryCredentials()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const logAnalyticsSubscriptions = ((await this.loadSubscriptions('workspacesloganalytics')) ||
|
||||
[]) as SelectableValue[];
|
||||
|
||||
if (logAnalyticsSubscriptions && logAnalyticsSubscriptions.length > 0) {
|
||||
this.setState({ logAnalyticsSubscriptions });
|
||||
|
||||
this.updateJsonDataOption(
|
||||
'logAnalyticsSubscriptionId',
|
||||
this.props.options.jsonData.logAnalyticsSubscriptionId || logAnalyticsSubscriptions[0].value
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.options.jsonData.logAnalyticsSubscriptionId) {
|
||||
await this.getWorkspaces();
|
||||
}
|
||||
// TODO: Used only by InsightsConfig
|
||||
private onUpdateSecureJsonDataOption = (key: keyof AzureDataSourceSecureJsonData) => (
|
||||
event: React.SyntheticEvent<HTMLInputElement | HTMLSelectElement>
|
||||
) => {
|
||||
updateDatasourcePluginSecureJsonDataOption(this.props, key, event.currentTarget.value);
|
||||
};
|
||||
|
||||
getWorkspaces = async () => {
|
||||
const { subscriptionId, azureLogAnalyticsSameAs, logAnalyticsSubscriptionId } = this.props.options.jsonData;
|
||||
const subscriptionIdToUse = azureLogAnalyticsSameAs ? subscriptionId : logAnalyticsSubscriptionId;
|
||||
|
||||
if (!subscriptionIdToUse) {
|
||||
return;
|
||||
}
|
||||
|
||||
const logAnalyticsWorkspaces = await this.loadWorkspaces(subscriptionIdToUse);
|
||||
|
||||
if (logAnalyticsWorkspaces.length > 0) {
|
||||
this.setState({ logAnalyticsWorkspaces });
|
||||
|
||||
this.updateJsonDataOption(
|
||||
'logAnalyticsDefaultWorkspace',
|
||||
this.props.options.jsonData.logAnalyticsDefaultWorkspace || logAnalyticsWorkspaces[0].value
|
||||
);
|
||||
}
|
||||
// TODO: Used only by InsightsConfig
|
||||
private resetSecureKey = (key: keyof AzureDataSourceSecureJsonData) => {
|
||||
updateDatasourcePluginResetOption(this.props, key);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { subscriptions, logAnalyticsSubscriptions, logAnalyticsWorkspaces } = this.state;
|
||||
// TODO: Clean up
|
||||
const { options } = this.props;
|
||||
|
||||
options.jsonData.cloudName = options.jsonData.cloudName || 'azuremonitor';
|
||||
// This is bad, causes so many messy typing issues everwhere..
|
||||
options.secureJsonData = (options.secureJsonData || {}) as AzureDataSourceSecureJsonData;
|
||||
|
||||
return (
|
||||
<>
|
||||
<MonitorConfig
|
||||
options={options}
|
||||
subscriptions={subscriptions}
|
||||
makeSameAs={this.makeSameAs}
|
||||
onLoadSubscriptions={this.onLoadSubscriptions}
|
||||
onUpdateJsonDataOption={this.updateJsonDataOption}
|
||||
onUpdateSecureJsonDataOption={this.updateSecureJsonDataOption}
|
||||
onResetOptionKey={this.resetSecureKey}
|
||||
/>
|
||||
<MonitorConfig options={options} updateOptions={this.updateOptions} getSubscriptions={this.getSubscriptions} />
|
||||
|
||||
<AnalyticsConfig
|
||||
options={options}
|
||||
workspaces={logAnalyticsWorkspaces}
|
||||
subscriptions={logAnalyticsSubscriptions}
|
||||
makeSameAs={this.makeSameAs}
|
||||
onUpdateDatasourceOptions={this.props.onOptionsChange}
|
||||
onUpdateJsonDataOption={this.updateJsonDataOption}
|
||||
onUpdateSecureJsonDataOption={this.updateSecureJsonDataOption}
|
||||
onResetOptionKey={this.resetSecureKey}
|
||||
onLoadSubscriptions={this.onLoadSubscriptions}
|
||||
onLoadWorkspaces={this.getWorkspaces}
|
||||
updateOptions={this.updateOptions}
|
||||
getSubscriptions={this.getLogAnalyticsSubscriptions}
|
||||
getWorkspaces={this.getWorkspaces}
|
||||
/>
|
||||
|
||||
<InsightsConfig
|
||||
|
||||
+46
-61
@@ -1,7 +1,8 @@
|
||||
import React, { PureComponent, ChangeEvent } from 'react';
|
||||
import React, { FunctionComponent, useMemo } from 'react';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { AzureCredentialsForm } from './AzureCredentialsForm';
|
||||
import { AzureDataSourceSettings } from '../types';
|
||||
import { AzureDataSourceSettings, AzureCredentials } from '../types';
|
||||
import { getCredentials, updateCredentials, isLogAnalyticsSameAs } from '../credentials';
|
||||
|
||||
const azureClouds = [
|
||||
{ value: 'azuremonitor', label: 'Azure' },
|
||||
@@ -12,70 +13,54 @@ const azureClouds = [
|
||||
|
||||
export interface Props {
|
||||
options: AzureDataSourceSettings;
|
||||
subscriptions: SelectableValue[];
|
||||
makeSameAs: (updatedClientSecret?: string) => void;
|
||||
onUpdateJsonDataOption: (key: string, val: any) => void;
|
||||
onUpdateSecureJsonDataOption: (key: string, val: any) => void;
|
||||
onResetOptionKey: (key: string) => void;
|
||||
onLoadSubscriptions: () => void;
|
||||
updateOptions: (optionsFunc: (options: AzureDataSourceSettings) => AzureDataSourceSettings) => void;
|
||||
getSubscriptions: () => Promise<Array<SelectableValue<string>>>;
|
||||
}
|
||||
|
||||
export class MonitorConfig extends PureComponent<Props> {
|
||||
onAzureCloudSelect = (cloudName: SelectableValue<string>) => {
|
||||
this.props.onUpdateJsonDataOption('cloudName', cloudName.value);
|
||||
export const MonitorConfig: FunctionComponent<Props> = (props: Props) => {
|
||||
const { updateOptions, getSubscriptions } = props;
|
||||
const credentials = useMemo(() => getCredentials(props.options), [props.options]);
|
||||
const subscriptionId = props.options.jsonData.subscriptionId;
|
||||
|
||||
const onCredentialsChange = (credentials: AzureCredentials): void => {
|
||||
updateOptions((options) => updateCredentials(options, credentials));
|
||||
};
|
||||
|
||||
onTenantIdChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.props.onUpdateJsonDataOption('tenantId', event.target.value);
|
||||
const onDefaultSubscriptionChange = (subscriptionId: string | undefined) => {
|
||||
updateOptions((options) => {
|
||||
options = {
|
||||
...options,
|
||||
jsonData: {
|
||||
...options.jsonData,
|
||||
subscriptionId: subscriptionId || '',
|
||||
},
|
||||
};
|
||||
if (isLogAnalyticsSameAs(options)) {
|
||||
options = {
|
||||
...options,
|
||||
jsonData: {
|
||||
...options.jsonData,
|
||||
logAnalyticsSubscriptionId: subscriptionId || '',
|
||||
},
|
||||
};
|
||||
}
|
||||
return options;
|
||||
});
|
||||
};
|
||||
|
||||
onClientIdChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.props.onUpdateJsonDataOption('clientId', event.target.value);
|
||||
};
|
||||
|
||||
onClientSecretChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const { options, makeSameAs } = this.props;
|
||||
|
||||
if (options.jsonData.azureLogAnalyticsSameAs && event.target.value) {
|
||||
makeSameAs(event.target.value);
|
||||
} else {
|
||||
this.props.onUpdateSecureJsonDataOption('clientSecret', event.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
onResetClientSecret = () => {
|
||||
this.props.onResetOptionKey('clientSecret');
|
||||
};
|
||||
|
||||
onSubscriptionSelect = (subscription: SelectableValue<string>) => {
|
||||
this.props.onUpdateJsonDataOption('subscriptionId', subscription.value);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { options, subscriptions } = this.props;
|
||||
return (
|
||||
<>
|
||||
<h3 className="page-heading">Azure Monitor Metrics Details</h3>
|
||||
<AzureCredentialsForm
|
||||
selectedAzureCloud={options.jsonData.cloudName || 'azuremonitor'}
|
||||
azureCloudOptions={azureClouds}
|
||||
subscriptionOptions={subscriptions}
|
||||
selectedSubscription={options.jsonData.subscriptionId}
|
||||
tenantId={options.jsonData.tenantId}
|
||||
clientId={options.jsonData.clientId}
|
||||
clientSecret={options.secureJsonData?.clientSecret}
|
||||
clientSecretConfigured={options.secureJsonFields.clientSecret}
|
||||
onAzureCloudChange={this.onAzureCloudSelect}
|
||||
onSubscriptionSelectChange={this.onSubscriptionSelect}
|
||||
onTenantIdChange={this.onTenantIdChange}
|
||||
onClientIdChange={this.onClientIdChange}
|
||||
onClientSecretChange={this.onClientSecretChange}
|
||||
onResetClientSecret={this.onResetClientSecret}
|
||||
onLoadSubscriptions={this.props.onLoadSubscriptions}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<h3 className="page-heading">Azure Monitor Metrics Details</h3>
|
||||
<AzureCredentialsForm
|
||||
credentials={credentials}
|
||||
defaultSubscription={subscriptionId}
|
||||
azureCloudOptions={azureClouds}
|
||||
onCredentialsChange={onCredentialsChange}
|
||||
onDefaultSubscriptionChange={onDefaultSubscriptionChange}
|
||||
getSubscriptions={getSubscriptions}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MonitorConfig;
|
||||
|
||||
+1
-4
@@ -38,9 +38,7 @@ const SubscriptionField: React.FC<SubscriptionFieldProps> = ({
|
||||
if (!newSubscription && query.queryType === AzureQueryType.AzureMonitor) {
|
||||
newSubscription = datasource.azureMonitorDatasource.subscriptionId;
|
||||
} else if (!query.subscription && query.queryType === AzureQueryType.LogAnalytics) {
|
||||
newSubscription =
|
||||
datasource.azureLogAnalyticsDatasource.logAnalyticsSubscriptionId ||
|
||||
datasource.azureLogAnalyticsDatasource.subscriptionId;
|
||||
newSubscription = datasource.azureLogAnalyticsDatasource.subscriptionId;
|
||||
}
|
||||
|
||||
if (!newSubscription && newSubscriptions.length > 0) {
|
||||
@@ -55,7 +53,6 @@ const SubscriptionField: React.FC<SubscriptionFieldProps> = ({
|
||||
})
|
||||
.catch((err) => setError(ERROR_SOURCE, err));
|
||||
}, [
|
||||
datasource.azureLogAnalyticsDatasource?.logAnalyticsSubscriptionId,
|
||||
datasource.azureLogAnalyticsDatasource?.subscriptionId,
|
||||
datasource.azureMonitorDatasource,
|
||||
onQueryChange,
|
||||
|
||||
+25
-36
@@ -8,21 +8,10 @@ exports[`Render should disable log analytics credentials form 1`] = `
|
||||
Azure Monitor Logs Details
|
||||
</h3>
|
||||
<Switch
|
||||
checked={false}
|
||||
checked={true}
|
||||
label="Same details as Azure Monitor API"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
<AzureCredentialsForm
|
||||
clientSecret=""
|
||||
clientSecretConfigured={false}
|
||||
onClientIdChange={[Function]}
|
||||
onClientSecretChange={[Function]}
|
||||
onLoadSubscriptions={[Function]}
|
||||
onResetClientSecret={[Function]}
|
||||
onSubscriptionSelectChange={[Function]}
|
||||
onTenantIdChange={[Function]}
|
||||
subscriptionOptions={Array []}
|
||||
tenantId=""
|
||||
tooltip="Workspaces are pulled from default subscription selected above."
|
||||
/>
|
||||
<div
|
||||
className="gf-form-group"
|
||||
@@ -56,7 +45,6 @@ exports[`Render should disable log analytics credentials form 1`] = `
|
||||
"SingleValue": [Function],
|
||||
}
|
||||
}
|
||||
defaultValue=""
|
||||
isClearable={false}
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
@@ -109,16 +97,18 @@ exports[`Render should enable azure log analytics load workspaces button 1`] = `
|
||||
onChange={[Function]}
|
||||
/>
|
||||
<AzureCredentialsForm
|
||||
clientSecret=""
|
||||
clientSecretConfigured={false}
|
||||
onClientIdChange={[Function]}
|
||||
onClientSecretChange={[Function]}
|
||||
onLoadSubscriptions={[Function]}
|
||||
onResetClientSecret={[Function]}
|
||||
onSubscriptionSelectChange={[Function]}
|
||||
onTenantIdChange={[Function]}
|
||||
subscriptionOptions={Array []}
|
||||
tenantId=""
|
||||
credentials={
|
||||
Object {
|
||||
"azureCloud": "azuremonitor",
|
||||
"clientId": "44693801-6ee6-49de-9b2d-9106972f9572",
|
||||
"clientSecret": undefined,
|
||||
"tenantId": "e7f3f661-a933-4b3f-8176-51c4f982ec48",
|
||||
}
|
||||
}
|
||||
defaultSubscription="e3fe4fde-ad5e-4d60-9974-e2f3562ffdf2"
|
||||
getSubscriptions={[MockFunction]}
|
||||
onCredentialsChange={[Function]}
|
||||
onDefaultSubscriptionChange={[Function]}
|
||||
/>
|
||||
<div
|
||||
className="gf-form-group"
|
||||
@@ -152,7 +142,6 @@ exports[`Render should enable azure log analytics load workspaces button 1`] = `
|
||||
"SingleValue": [Function],
|
||||
}
|
||||
}
|
||||
defaultValue=""
|
||||
isClearable={false}
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
@@ -205,16 +194,17 @@ exports[`Render should render component 1`] = `
|
||||
onChange={[Function]}
|
||||
/>
|
||||
<AzureCredentialsForm
|
||||
clientSecret=""
|
||||
clientSecretConfigured={false}
|
||||
onClientIdChange={[Function]}
|
||||
onClientSecretChange={[Function]}
|
||||
onLoadSubscriptions={[Function]}
|
||||
onResetClientSecret={[Function]}
|
||||
onSubscriptionSelectChange={[Function]}
|
||||
onTenantIdChange={[Function]}
|
||||
subscriptionOptions={Array []}
|
||||
tenantId=""
|
||||
credentials={
|
||||
Object {
|
||||
"azureCloud": "azuremonitor",
|
||||
"clientId": undefined,
|
||||
"clientSecret": undefined,
|
||||
"tenantId": "",
|
||||
}
|
||||
}
|
||||
getSubscriptions={[MockFunction]}
|
||||
onCredentialsChange={[Function]}
|
||||
onDefaultSubscriptionChange={[Function]}
|
||||
/>
|
||||
<div
|
||||
className="gf-form-group"
|
||||
@@ -248,7 +238,6 @@ exports[`Render should render component 1`] = `
|
||||
"SingleValue": [Function],
|
||||
}
|
||||
}
|
||||
defaultValue=""
|
||||
isClearable={false}
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
|
||||
+18
-24
@@ -31,14 +31,13 @@ exports[`Render should disable azure monitor secret input 1`] = `
|
||||
"SingleValue": [Function],
|
||||
}
|
||||
}
|
||||
defaultValue="azuremonitor"
|
||||
isClearable={false}
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
isMulti={false}
|
||||
isSearchable={true}
|
||||
maxMenuHeight={300}
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
openMenuOnFocus={false}
|
||||
options={
|
||||
Array [
|
||||
@@ -86,7 +85,7 @@ exports[`Render should disable azure monitor secret input 1`] = `
|
||||
>
|
||||
<Input
|
||||
className="width-30"
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value="e7f3f661-a933-3h3f-0294-31c4f962ec48"
|
||||
/>
|
||||
@@ -109,7 +108,7 @@ exports[`Render should disable azure monitor secret input 1`] = `
|
||||
>
|
||||
<Input
|
||||
className="width-30"
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value="34509fad-c0r9-45df-9e25-f1ee34af6900"
|
||||
/>
|
||||
@@ -140,7 +139,7 @@ exports[`Render should disable azure monitor secret input 1`] = `
|
||||
className="max-width-30 gf-form-inline"
|
||||
>
|
||||
<Button
|
||||
onClick={[MockFunction]}
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
variant="secondary"
|
||||
>
|
||||
@@ -177,14 +176,13 @@ exports[`Render should disable azure monitor secret input 1`] = `
|
||||
"SingleValue": [Function],
|
||||
}
|
||||
}
|
||||
defaultValue="44987801-6nn6-49he-9b2d-9106972f9789"
|
||||
isClearable={false}
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
isMulti={false}
|
||||
isSearchable={true}
|
||||
maxMenuHeight={300}
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
openMenuOnFocus={false}
|
||||
options={Array []}
|
||||
tabSelectsValue={true}
|
||||
@@ -203,7 +201,7 @@ exports[`Render should disable azure monitor secret input 1`] = `
|
||||
>
|
||||
<Button
|
||||
disabled={false}
|
||||
onClick={[MockFunction]}
|
||||
onClick={[Function]}
|
||||
size="sm"
|
||||
type="button"
|
||||
variant="secondary"
|
||||
@@ -248,14 +246,13 @@ exports[`Render should enable azure monitor load subscriptions button 1`] = `
|
||||
"SingleValue": [Function],
|
||||
}
|
||||
}
|
||||
defaultValue="azuremonitor"
|
||||
isClearable={false}
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
isMulti={false}
|
||||
isSearchable={true}
|
||||
maxMenuHeight={300}
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
openMenuOnFocus={false}
|
||||
options={
|
||||
Array [
|
||||
@@ -303,7 +300,7 @@ exports[`Render should enable azure monitor load subscriptions button 1`] = `
|
||||
>
|
||||
<Input
|
||||
className="width-30"
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value="e7f3f661-a933-3h3f-0294-31c4f962ec48"
|
||||
/>
|
||||
@@ -326,7 +323,7 @@ exports[`Render should enable azure monitor load subscriptions button 1`] = `
|
||||
>
|
||||
<Input
|
||||
className="width-30"
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value="34509fad-c0r9-45df-9e25-f1ee34af6900"
|
||||
/>
|
||||
@@ -349,7 +346,7 @@ exports[`Render should enable azure monitor load subscriptions button 1`] = `
|
||||
>
|
||||
<Input
|
||||
className="width-30"
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value="e7f3f661-a933-4b3f-8176-51c4f982ec48"
|
||||
/>
|
||||
@@ -384,14 +381,13 @@ exports[`Render should enable azure monitor load subscriptions button 1`] = `
|
||||
"SingleValue": [Function],
|
||||
}
|
||||
}
|
||||
defaultValue="44987801-6nn6-49he-9b2d-9106972f9789"
|
||||
isClearable={false}
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
isMulti={false}
|
||||
isSearchable={true}
|
||||
maxMenuHeight={300}
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
openMenuOnFocus={false}
|
||||
options={Array []}
|
||||
tabSelectsValue={true}
|
||||
@@ -410,7 +406,7 @@ exports[`Render should enable azure monitor load subscriptions button 1`] = `
|
||||
>
|
||||
<Button
|
||||
disabled={false}
|
||||
onClick={[MockFunction]}
|
||||
onClick={[Function]}
|
||||
size="sm"
|
||||
type="button"
|
||||
variant="secondary"
|
||||
@@ -455,14 +451,13 @@ exports[`Render should render component 1`] = `
|
||||
"SingleValue": [Function],
|
||||
}
|
||||
}
|
||||
defaultValue="azuremonitor"
|
||||
isClearable={false}
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
isMulti={false}
|
||||
isSearchable={true}
|
||||
maxMenuHeight={300}
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
openMenuOnFocus={false}
|
||||
options={
|
||||
Array [
|
||||
@@ -510,7 +505,7 @@ exports[`Render should render component 1`] = `
|
||||
>
|
||||
<Input
|
||||
className="width-30"
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value="e7f3f661-a933-3h3f-0294-31c4f962ec48"
|
||||
/>
|
||||
@@ -533,7 +528,7 @@ exports[`Render should render component 1`] = `
|
||||
>
|
||||
<Input
|
||||
className="width-30"
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value="34509fad-c0r9-45df-9e25-f1ee34af6900"
|
||||
/>
|
||||
@@ -556,7 +551,7 @@ exports[`Render should render component 1`] = `
|
||||
>
|
||||
<Input
|
||||
className="width-30"
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value=""
|
||||
/>
|
||||
@@ -591,14 +586,13 @@ exports[`Render should render component 1`] = `
|
||||
"SingleValue": [Function],
|
||||
}
|
||||
}
|
||||
defaultValue="44987801-6nn6-49he-9b2d-9106972f9789"
|
||||
isClearable={false}
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
isMulti={false}
|
||||
isSearchable={true}
|
||||
maxMenuHeight={300}
|
||||
onChange={[MockFunction]}
|
||||
onChange={[Function]}
|
||||
openMenuOnFocus={false}
|
||||
options={Array []}
|
||||
tabSelectsValue={true}
|
||||
@@ -617,7 +611,7 @@ exports[`Render should render component 1`] = `
|
||||
>
|
||||
<Button
|
||||
disabled={true}
|
||||
onClick={[MockFunction]}
|
||||
onClick={[Function]}
|
||||
size="sm"
|
||||
type="button"
|
||||
variant="secondary"
|
||||
|
||||
+5
-55
@@ -3,11 +3,7 @@
|
||||
exports[`Render should render component 1`] = `
|
||||
<Fragment>
|
||||
<MonitorConfig
|
||||
makeSameAs={[Function]}
|
||||
onLoadSubscriptions={[Function]}
|
||||
onResetOptionKey={[Function]}
|
||||
onUpdateJsonDataOption={[Function]}
|
||||
onUpdateSecureJsonDataOption={[Function]}
|
||||
getSubscriptions={[Function]}
|
||||
options={
|
||||
Object {
|
||||
"access": "proxy",
|
||||
@@ -38,56 +34,11 @@ exports[`Render should render component 1`] = `
|
||||
"withCredentials": false,
|
||||
}
|
||||
}
|
||||
subscriptions={Array []}
|
||||
updateOptions={[Function]}
|
||||
/>
|
||||
<AnalyticsConfig
|
||||
makeSameAs={[Function]}
|
||||
onLoadSubscriptions={[Function]}
|
||||
onLoadWorkspaces={[Function]}
|
||||
onResetOptionKey={[Function]}
|
||||
onUpdateDatasourceOptions={
|
||||
[MockFunction] {
|
||||
"calls": Array [
|
||||
Array [
|
||||
Object {
|
||||
"access": "proxy",
|
||||
"basicAuth": false,
|
||||
"basicAuthPassword": "",
|
||||
"basicAuthUser": "",
|
||||
"database": "",
|
||||
"id": 21,
|
||||
"isDefault": false,
|
||||
"jsonData": Object {
|
||||
"azureLogAnalyticsSameAs": true,
|
||||
"cloudName": "azuremonitor",
|
||||
"subscriptionId": "44987801-6nn6-49he-9b2d-9106972f9789",
|
||||
},
|
||||
"name": "Azure Monitor-10-10",
|
||||
"orgId": 1,
|
||||
"password": "",
|
||||
"readOnly": false,
|
||||
"secureJsonFields": Object {},
|
||||
"type": "grafana-azure-monitor-datasource",
|
||||
"typeLogoUrl": "",
|
||||
"typeName": "Azure",
|
||||
"uid": "y",
|
||||
"url": "/api/datasources/proxy/21",
|
||||
"user": "",
|
||||
"version": 1,
|
||||
"withCredentials": false,
|
||||
},
|
||||
],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
onUpdateJsonDataOption={[Function]}
|
||||
onUpdateSecureJsonDataOption={[Function]}
|
||||
getSubscriptions={[Function]}
|
||||
getWorkspaces={[Function]}
|
||||
options={
|
||||
Object {
|
||||
"access": "proxy",
|
||||
@@ -118,8 +69,7 @@ exports[`Render should render component 1`] = `
|
||||
"withCredentials": false,
|
||||
}
|
||||
}
|
||||
subscriptions={Array []}
|
||||
workspaces={Array []}
|
||||
updateOptions={[Function]}
|
||||
/>
|
||||
<InsightsConfig
|
||||
onResetOptionKey={[Function]}
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
import { AzureCredentials, AzureDataSourceInstanceSettings, AzureDataSourceSettings, ConcealedSecret } from './types';
|
||||
|
||||
const concealed: ConcealedSecret = Symbol('Concealed client secret');
|
||||
|
||||
export function getAzureCloud(options: AzureDataSourceSettings | AzureDataSourceInstanceSettings): string {
|
||||
return options.jsonData.cloudName || 'azuremonitor';
|
||||
}
|
||||
|
||||
function getSecret(options: AzureDataSourceSettings): undefined | string | ConcealedSecret {
|
||||
if (options.secureJsonFields.clientSecret) {
|
||||
// The secret is concealed on server
|
||||
return concealed;
|
||||
} else {
|
||||
const secret = options.secureJsonData?.clientSecret;
|
||||
return typeof secret === 'string' && secret.length > 0 ? secret : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getLogAnalyticsSecret(options: AzureDataSourceSettings): undefined | string | ConcealedSecret {
|
||||
if (options.secureJsonFields.logAnalyticsClientSecret) {
|
||||
// The secret is concealed on server
|
||||
return concealed;
|
||||
} else {
|
||||
const secret = options.secureJsonData?.logAnalyticsClientSecret;
|
||||
return typeof secret === 'string' && secret.length > 0 ? secret : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function isLogAnalyticsSameAs(options: AzureDataSourceSettings): boolean {
|
||||
return typeof options.jsonData.azureLogAnalyticsSameAs !== 'boolean' || options.jsonData.azureLogAnalyticsSameAs;
|
||||
}
|
||||
|
||||
export function isCredentialsComplete(credentials: AzureCredentials) {
|
||||
return !!(credentials.tenantId && credentials.clientId && credentials.clientSecret);
|
||||
}
|
||||
|
||||
export function getCredentials(options: AzureDataSourceSettings): AzureCredentials {
|
||||
return {
|
||||
azureCloud: getAzureCloud(options),
|
||||
tenantId: options.jsonData.tenantId,
|
||||
clientId: options.jsonData.clientId,
|
||||
clientSecret: getSecret(options),
|
||||
};
|
||||
}
|
||||
|
||||
export function getLogAnalyticsCredentials(options: AzureDataSourceSettings): AzureCredentials | undefined {
|
||||
if (isLogAnalyticsSameAs(options)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
azureCloud: getAzureCloud(options),
|
||||
tenantId: options.jsonData.logAnalyticsTenantId,
|
||||
clientId: options.jsonData.logAnalyticsClientId,
|
||||
clientSecret: getLogAnalyticsSecret(options),
|
||||
};
|
||||
}
|
||||
|
||||
export function updateCredentials(
|
||||
options: AzureDataSourceSettings,
|
||||
credentials: AzureCredentials
|
||||
): AzureDataSourceSettings {
|
||||
options = {
|
||||
...options,
|
||||
jsonData: {
|
||||
...options.jsonData,
|
||||
cloudName: credentials.azureCloud || 'azuremonitor',
|
||||
tenantId: credentials.tenantId,
|
||||
clientId: credentials.clientId,
|
||||
},
|
||||
secureJsonData: {
|
||||
...options.secureJsonData,
|
||||
clientSecret:
|
||||
typeof credentials.clientSecret === 'string' && credentials.clientSecret.length > 0
|
||||
? credentials.clientSecret
|
||||
: undefined,
|
||||
},
|
||||
secureJsonFields: {
|
||||
...options.secureJsonFields,
|
||||
clientSecret: typeof credentials.clientSecret === 'symbol',
|
||||
},
|
||||
};
|
||||
|
||||
if (isLogAnalyticsSameAs(options)) {
|
||||
options = updateLogAnalyticsCredentials(options, credentials);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
export function updateLogAnalyticsCredentials(
|
||||
options: AzureDataSourceSettings,
|
||||
credentials: AzureCredentials
|
||||
): AzureDataSourceSettings {
|
||||
options = {
|
||||
...options,
|
||||
jsonData: {
|
||||
...options.jsonData,
|
||||
logAnalyticsTenantId: credentials.tenantId,
|
||||
logAnalyticsClientId: credentials.clientId,
|
||||
},
|
||||
secureJsonData: {
|
||||
...options.secureJsonData,
|
||||
logAnalyticsClientSecret:
|
||||
typeof credentials.clientSecret === 'string' && credentials.clientSecret.length > 0
|
||||
? credentials.clientSecret
|
||||
: undefined,
|
||||
},
|
||||
secureJsonFields: {
|
||||
...options.secureJsonFields,
|
||||
logAnalyticsClientSecret: typeof credentials.clientSecret === 'symbol',
|
||||
},
|
||||
};
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
export function updateLogAnalyticsSameAs(options: AzureDataSourceSettings, sameAs: boolean): AzureDataSourceSettings {
|
||||
if (sameAs !== isLogAnalyticsSameAs(options)) {
|
||||
// Update the 'Same As' switch
|
||||
options = {
|
||||
...options,
|
||||
jsonData: {
|
||||
...options.jsonData,
|
||||
azureLogAnalyticsSameAs: sameAs,
|
||||
},
|
||||
};
|
||||
|
||||
if (sameAs) {
|
||||
// Get the primary credentials
|
||||
let credentials = getCredentials(options);
|
||||
|
||||
// Check whether the client secret is concealed
|
||||
if (typeof credentials.clientSecret === 'symbol') {
|
||||
// Log Analytics credentials need to be synchronized but the client secret is concealed,
|
||||
// so we have to reset the primary client secret to ensure that user enters a new secret
|
||||
credentials.clientSecret = undefined;
|
||||
options = updateCredentials(options, credentials);
|
||||
}
|
||||
|
||||
// Synchronize the Log Analytics credentials with primary credentials
|
||||
options = updateLogAnalyticsCredentials(options, credentials);
|
||||
|
||||
// Synchronize default subscription
|
||||
options = {
|
||||
...options,
|
||||
jsonData: {
|
||||
...options.jsonData,
|
||||
logAnalyticsSubscriptionId: options.jsonData.subscriptionId,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
@@ -320,7 +320,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
if (!this.target.subscription && this.target.queryType === 'Azure Monitor') {
|
||||
this.target.subscription = this.datasource.azureMonitorDatasource.subscriptionId;
|
||||
} else if (!this.target.subscription && this.target.queryType === 'Azure Log Analytics') {
|
||||
this.target.subscription = this.datasource.azureLogAnalyticsDatasource.logAnalyticsSubscriptionId;
|
||||
this.target.subscription = this.datasource.azureLogAnalyticsDatasource.subscriptionId;
|
||||
}
|
||||
|
||||
if (!this.target.subscription && this.subscriptions.length > 0) {
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { DataQuery, DataSourceJsonData, DataSourceSettings, TableData } from '@grafana/data';
|
||||
import {
|
||||
DataQuery,
|
||||
DataSourceInstanceSettings,
|
||||
DataSourceJsonData,
|
||||
DataSourceSettings,
|
||||
TableData,
|
||||
} from '@grafana/data';
|
||||
import Datasource from '../datasource';
|
||||
|
||||
export type AzureDataSourceSettings = DataSourceSettings<AzureDataSourceJsonData, AzureDataSourceSecureJsonData>;
|
||||
export type AzureDataSourceInstanceSettings = DataSourceInstanceSettings<AzureDataSourceJsonData>;
|
||||
|
||||
export type AzureResultFormat = 'time_series' | 'table';
|
||||
|
||||
@@ -23,19 +30,28 @@ export interface AzureMonitorQuery extends DataQuery {
|
||||
insightsAnalytics: InsightsAnalyticsQuery;
|
||||
}
|
||||
|
||||
export type ConcealedSecret = symbol;
|
||||
|
||||
export interface AzureCredentials {
|
||||
azureCloud?: string;
|
||||
tenantId?: string;
|
||||
clientId?: string;
|
||||
clientSecret?: string | ConcealedSecret;
|
||||
}
|
||||
|
||||
export interface AzureDataSourceJsonData extends DataSourceJsonData {
|
||||
subscriptionId: string;
|
||||
cloudName: string;
|
||||
|
||||
// monitor
|
||||
tenantId?: string;
|
||||
clientId?: string;
|
||||
subscriptionId: string;
|
||||
|
||||
// logs
|
||||
logAnalyticsSubscriptionId?: string;
|
||||
azureLogAnalyticsSameAs?: boolean;
|
||||
logAnalyticsTenantId?: string;
|
||||
logAnalyticsClientId?: string;
|
||||
azureLogAnalyticsSameAs?: boolean;
|
||||
logAnalyticsSubscriptionId?: string;
|
||||
logAnalyticsDefaultWorkspace?: string;
|
||||
|
||||
// App Insights
|
||||
|
||||
Reference in New Issue
Block a user