mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AzureMonitor: Add Azure Resource Graph (#33293)
* Add Azure Resource Graph in Azure Plugin * fix lodash import * Fix mock queries * use "backend" sdk * Address comments * add converter for object type * Query error should cause 400 & apply template var * fix backend test & add documentation * update image * Address comments * marshal body from map * use interpolated query instead of raw query * fix test * filter out empty queries * fix go linting problem * use new query field language name * improve variable tests * add better tests for interpolate variable Co-authored-by: joshhunt <josh@trtr.co> Co-authored-by: Erik Sundell <erik.sundell87@gmail.com>
This commit is contained in:
@@ -11,6 +11,11 @@ export default function createMockQuery(): AzureMonitorQuery {
|
||||
workspace: 'e3fe4fde-ad5e-4d60-9974-e2f3562ffdf2',
|
||||
},
|
||||
|
||||
azureResourceGraph: {
|
||||
query: 'Resources | summarize count()',
|
||||
resultFormat: 'time_series',
|
||||
},
|
||||
|
||||
azureMonitor: {
|
||||
// aggOptions: [],
|
||||
aggregation: 'Average',
|
||||
@@ -35,8 +40,8 @@ export default function createMockQuery(): AzureMonitorQuery {
|
||||
|
||||
queryType: AzureQueryType.AzureMonitor,
|
||||
refId: 'A',
|
||||
subscription: 'abc-123',
|
||||
|
||||
subscription: '99999999-cccc-bbbb-aaaa-9106972f9572',
|
||||
subscriptions: ['99999999-cccc-bbbb-aaaa-9106972f9572'],
|
||||
format: 'dunno lol', // unsure what this value should be. It's not there at runtime, but it's in the ts interface
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { backendSrv } from 'app/core/services/backend_srv';
|
||||
import AzureResourceGraphDatasource from './azure_resource_graph_datasource';
|
||||
import { CustomVariableModel, initialVariableModelState, VariableHide } from 'app/features/variables/types';
|
||||
import { initialCustomVariableModelState } from 'app/features/variables/custom/reducer';
|
||||
|
||||
const templateSrv = new TemplateSrv();
|
||||
|
||||
const single: CustomVariableModel = {
|
||||
...initialVariableModelState,
|
||||
id: 'var1',
|
||||
name: 'var1',
|
||||
index: 0,
|
||||
current: { value: 'var1-foo', text: 'var1-foo', selected: true },
|
||||
options: [{ value: 'var1-foo', text: 'var1-foo', selected: true }],
|
||||
multi: false,
|
||||
includeAll: false,
|
||||
query: '',
|
||||
hide: VariableHide.dontHide,
|
||||
type: 'custom',
|
||||
};
|
||||
|
||||
const multi: CustomVariableModel = {
|
||||
...initialVariableModelState,
|
||||
id: 'var3',
|
||||
name: 'var3',
|
||||
index: 2,
|
||||
current: { value: ['var3-foo', 'var3-baz'], text: 'var3-foo + var3-baz', selected: true },
|
||||
options: [
|
||||
{ selected: true, value: 'var3-foo', text: 'var3-foo' },
|
||||
{ selected: false, value: 'var3-bar', text: 'var3-bar' },
|
||||
{ selected: true, value: 'var3-baz', text: 'var3-baz' },
|
||||
],
|
||||
multi: true,
|
||||
includeAll: false,
|
||||
query: '',
|
||||
hide: VariableHide.dontHide,
|
||||
type: 'custom',
|
||||
};
|
||||
|
||||
templateSrv.init([single, multi]);
|
||||
|
||||
jest.mock('app/core/services/backend_srv');
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...((jest.requireActual('@grafana/runtime') as unknown) as object),
|
||||
getBackendSrv: () => backendSrv,
|
||||
getTemplateSrv: () => templateSrv,
|
||||
}));
|
||||
|
||||
describe('AzureResourceGraphDatasource', () => {
|
||||
const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest');
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
datasourceRequestMock.mockImplementation(jest.fn());
|
||||
});
|
||||
|
||||
const ctx: any = {};
|
||||
|
||||
beforeEach(() => {
|
||||
ctx.instanceSettings = {
|
||||
url: 'http://azureresourcegraphapi',
|
||||
};
|
||||
|
||||
ctx.ds = new AzureResourceGraphDatasource(ctx.instanceSettings);
|
||||
});
|
||||
|
||||
describe('When applying template variables', () => {
|
||||
it('should expand single value template variable', () => {
|
||||
const target = {
|
||||
azureResourceGraph: {
|
||||
query: 'Resources | $var1',
|
||||
resultFormat: '',
|
||||
},
|
||||
};
|
||||
expect(ctx.ds.applyTemplateVariables(target)).toStrictEqual({
|
||||
azureResourceGraph: { query: 'Resources | var1-foo', resultFormat: 'table' },
|
||||
format: undefined,
|
||||
queryType: 'Azure Resource Graph',
|
||||
refId: undefined,
|
||||
subscriptions: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('should expand multi value template variable', () => {
|
||||
const target = {
|
||||
azureResourceGraph: {
|
||||
query: 'resources | where $__contains(name, $var3)',
|
||||
resultFormat: '',
|
||||
},
|
||||
};
|
||||
expect(ctx.ds.applyTemplateVariables(target)).toStrictEqual({
|
||||
azureResourceGraph: {
|
||||
query: `resources | where $__contains(name, 'var3-foo','var3-baz')`,
|
||||
resultFormat: 'table',
|
||||
},
|
||||
format: undefined,
|
||||
queryType: 'Azure Resource Graph',
|
||||
refId: undefined,
|
||||
subscriptions: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('When interpolating variables', () => {
|
||||
beforeEach(() => {
|
||||
ctx.variable = { ...initialCustomVariableModelState };
|
||||
});
|
||||
|
||||
describe('and value is a string', () => {
|
||||
it('should return an unquoted value', () => {
|
||||
expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual('abc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and value is a number', () => {
|
||||
it('should return an unquoted value', () => {
|
||||
expect(ctx.ds.interpolateVariable(1000, ctx.variable)).toEqual(1000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and value is an array of strings', () => {
|
||||
it('should return comma separated quoted values', () => {
|
||||
expect(ctx.ds.interpolateVariable(['a', 'b', 'c'], ctx.variable)).toEqual("'a','b','c'");
|
||||
});
|
||||
});
|
||||
|
||||
describe('and variable allows multi-value and value is a string', () => {
|
||||
it('should return a quoted value', () => {
|
||||
ctx.variable.multi = true;
|
||||
expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'");
|
||||
});
|
||||
});
|
||||
|
||||
describe('and variable contains single quote', () => {
|
||||
it('should return a quoted value', () => {
|
||||
ctx.variable.multi = true;
|
||||
expect(ctx.ds.interpolateVariable("a'bc", ctx.variable)).toEqual("'a'bc'");
|
||||
});
|
||||
});
|
||||
|
||||
describe('and variable allows all and value is a string', () => {
|
||||
it('should return a quoted value', () => {
|
||||
ctx.variable.includeAll = true;
|
||||
expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
// eslint-disable-next-line lodash/import-scope
|
||||
import _ from 'lodash';
|
||||
import { AzureMonitorQuery, AzureDataSourceJsonData, AzureQueryType } from '../types';
|
||||
import { ScopedVars } from '@grafana/data';
|
||||
import { getTemplateSrv, DataSourceWithBackend } from '@grafana/runtime';
|
||||
|
||||
export default class AzureResourceGraphDatasource extends DataSourceWithBackend<
|
||||
AzureMonitorQuery,
|
||||
AzureDataSourceJsonData
|
||||
> {
|
||||
filterQuery(item: AzureMonitorQuery): boolean {
|
||||
return !!item.azureResourceGraph?.query;
|
||||
}
|
||||
|
||||
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): Record<string, any> {
|
||||
const item = target.azureResourceGraph;
|
||||
|
||||
const templateSrv = getTemplateSrv();
|
||||
|
||||
const query = templateSrv.replace(item.query, scopedVars, this.interpolateVariable);
|
||||
|
||||
return {
|
||||
refId: target.refId,
|
||||
format: target.format,
|
||||
queryType: AzureQueryType.AzureResourceGraph,
|
||||
subscriptions: target.subscriptions,
|
||||
azureResourceGraph: {
|
||||
resultFormat: 'table',
|
||||
query,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
interpolateVariable(value: string, variable: { multi: any; includeAll: any }) {
|
||||
if (typeof value === 'string') {
|
||||
if (variable.multi || variable.includeAll) {
|
||||
return "'" + value + "'";
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
|
||||
const quotedValues = _.map(value, (val) => {
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
|
||||
return "'" + val + "'";
|
||||
});
|
||||
return quotedValues.join(',');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
import { AzureMonitorErrorish, AzureMonitorOption, AzureMonitorQuery } from '../../types';
|
||||
import Datasource from '../../datasource';
|
||||
import { InlineFieldRow } from '@grafana/ui';
|
||||
import SubscriptionField from '../SubscriptionField';
|
||||
import QueryField from './QueryField';
|
||||
|
||||
interface LogsQueryEditorProps {
|
||||
query: AzureMonitorQuery;
|
||||
datasource: Datasource;
|
||||
subscriptionId: string;
|
||||
onChange: (newQuery: AzureMonitorQuery) => void;
|
||||
variableOptionGroup: { label: string; options: AzureMonitorOption[] };
|
||||
setError: (source: string, error: AzureMonitorErrorish | undefined) => void;
|
||||
}
|
||||
|
||||
const ArgQueryEditor: React.FC<LogsQueryEditorProps> = ({
|
||||
query,
|
||||
datasource,
|
||||
subscriptionId,
|
||||
variableOptionGroup,
|
||||
onChange,
|
||||
setError,
|
||||
}) => {
|
||||
return (
|
||||
<div data-testid="azure-monitor-logs-query-editor">
|
||||
<InlineFieldRow>
|
||||
<SubscriptionField
|
||||
multiSelect
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
</InlineFieldRow>
|
||||
|
||||
<QueryField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ArgQueryEditor;
|
||||
@@ -0,0 +1,32 @@
|
||||
import { CodeEditor } from '@grafana/ui';
|
||||
import React, { useCallback } from 'react';
|
||||
import { AzureQueryEditorFieldProps } from '../../types';
|
||||
|
||||
const QueryField: React.FC<AzureQueryEditorFieldProps> = ({ query, onQueryChange }) => {
|
||||
const onChange = useCallback(
|
||||
(newQuery: string) => {
|
||||
onQueryChange({
|
||||
...query,
|
||||
azureResourceGraph: {
|
||||
...query.azureResourceGraph,
|
||||
query: newQuery,
|
||||
},
|
||||
});
|
||||
},
|
||||
[onQueryChange, query]
|
||||
);
|
||||
|
||||
return (
|
||||
<CodeEditor
|
||||
value={query.azureResourceGraph.query}
|
||||
language="kusto"
|
||||
height={200}
|
||||
width={1000}
|
||||
showMiniMap={false}
|
||||
onBlur={onChange}
|
||||
onSave={onChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default QueryField;
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './ArgQueryEditor';
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { Select } from '@grafana/ui';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { AzureMonitorOption, AzureQueryEditorFieldProps, AzureResultFormat } from '../../types';
|
||||
import { findOption } from '../../utils/common';
|
||||
import { Field } from '../Field';
|
||||
|
||||
@@ -6,6 +6,7 @@ import MetricsQueryEditor from '../MetricsQueryEditor';
|
||||
import QueryTypeField from './QueryTypeField';
|
||||
import useLastError from '../../utils/useLastError';
|
||||
import LogsQueryEditor from '../LogsQueryEditor';
|
||||
import ArgQueryEditor from '../ArgQueryEditor';
|
||||
import ApplicationInsightsEditor from '../ApplicationInsightsEditor';
|
||||
import InsightsAnalyticsEditor from '../InsightsAnalyticsEditor';
|
||||
import { Space } from '../Space';
|
||||
@@ -93,6 +94,18 @@ const EditorForQueryType: React.FC<EditorForQueryTypeProps> = ({
|
||||
|
||||
case AzureQueryType.InsightsAnalytics:
|
||||
return <InsightsAnalyticsEditor query={query} />;
|
||||
|
||||
case AzureQueryType.AzureResourceGraph:
|
||||
return (
|
||||
<ArgQueryEditor
|
||||
subscriptionId={subscriptionId}
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
onChange={onChange}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
setError={setError}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -10,6 +10,7 @@ const QUERY_TYPES = [
|
||||
{ value: AzureQueryType.LogAnalytics, label: 'Logs' },
|
||||
{ value: AzureQueryType.ApplicationInsights, label: 'Application Insights' },
|
||||
{ value: AzureQueryType.InsightsAnalytics, label: 'Insights Analytics' },
|
||||
{ value: AzureQueryType.AzureResourceGraph, label: 'Azure Resource Graph' },
|
||||
];
|
||||
|
||||
interface QueryTypeFieldProps {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import React, { useCallback, useEffect, useState, useMemo } from 'react';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { Select } from '@grafana/ui';
|
||||
import { Select, MultiSelect } from '@grafana/ui';
|
||||
|
||||
import { AzureMonitorQuery, AzureQueryType, AzureQueryEditorFieldProps, AzureMonitorOption } from '../types';
|
||||
import { findOption } from '../utils/common';
|
||||
import { findOption, findOptions } from '../utils/common';
|
||||
import { Field } from './Field';
|
||||
|
||||
interface SubscriptionFieldProps extends AzureQueryEditorFieldProps {
|
||||
onQueryChange: (newQuery: AzureMonitorQuery) => void;
|
||||
multiSelect?: boolean;
|
||||
}
|
||||
|
||||
const ERROR_SOURCE = 'metrics-subscription';
|
||||
@@ -17,6 +18,7 @@ const SubscriptionField: React.FC<SubscriptionFieldProps> = ({
|
||||
variableOptionGroup,
|
||||
onQueryChange,
|
||||
setError,
|
||||
multiSelect = false,
|
||||
}) => {
|
||||
const [subscriptions, setSubscriptions] = useState<AzureMonitorOption[]>([]);
|
||||
|
||||
@@ -92,9 +94,33 @@ const SubscriptionField: React.FC<SubscriptionFieldProps> = ({
|
||||
[query, onQueryChange]
|
||||
);
|
||||
|
||||
const onSubscriptionsChange = useCallback(
|
||||
(change: Array<SelectableValue<string>>) => {
|
||||
if (!change) {
|
||||
return;
|
||||
}
|
||||
|
||||
query.subscriptions = change.map((c) => c.value ?? '');
|
||||
|
||||
onQueryChange(query);
|
||||
},
|
||||
[query, onQueryChange]
|
||||
);
|
||||
|
||||
const options = useMemo(() => [...subscriptions, variableOptionGroup], [subscriptions, variableOptionGroup]);
|
||||
|
||||
return (
|
||||
return multiSelect ? (
|
||||
<Field label="Subscriptions">
|
||||
<MultiSelect
|
||||
isClearable
|
||||
value={findOptions(subscriptions, query.subscriptions)}
|
||||
inputId="azure-monitor-subscriptions-field"
|
||||
onChange={onSubscriptionsChange}
|
||||
options={options}
|
||||
width={38}
|
||||
/>
|
||||
</Field>
|
||||
) : (
|
||||
<Field label="Subscription">
|
||||
<Select
|
||||
value={findOption(subscriptions, query.subscription)}
|
||||
|
||||
@@ -17,12 +17,14 @@ import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/run
|
||||
import InsightsAnalyticsDatasource from './insights_analytics/insights_analytics_datasource';
|
||||
import { migrateMetricsDimensionFilters } from './query_ctrl';
|
||||
import { map } from 'rxjs/operators';
|
||||
import AzureResourceGraphDatasource from './azure_resource_graph/azure_resource_graph_datasource';
|
||||
|
||||
export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDataSourceJsonData> {
|
||||
azureMonitorDatasource: AzureMonitorDatasource;
|
||||
appInsightsDatasource: AppInsightsDatasource;
|
||||
azureLogAnalyticsDatasource: AzureLogAnalyticsDatasource;
|
||||
insightsAnalyticsDatasource: InsightsAnalyticsDatasource;
|
||||
azureResourceGraphDatasource: AzureResourceGraphDatasource;
|
||||
|
||||
pseudoDatasource: Record<AzureQueryType, DataSourceWithBackend>;
|
||||
optionsKey: Record<AzureQueryType, string>;
|
||||
@@ -36,12 +38,14 @@ export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDa
|
||||
this.appInsightsDatasource = new AppInsightsDatasource(instanceSettings);
|
||||
this.azureLogAnalyticsDatasource = new AzureLogAnalyticsDatasource(instanceSettings);
|
||||
this.insightsAnalyticsDatasource = new InsightsAnalyticsDatasource(instanceSettings);
|
||||
this.azureResourceGraphDatasource = new AzureResourceGraphDatasource(instanceSettings);
|
||||
|
||||
const pseudoDatasource: any = {};
|
||||
pseudoDatasource[AzureQueryType.ApplicationInsights] = this.appInsightsDatasource;
|
||||
pseudoDatasource[AzureQueryType.AzureMonitor] = this.azureMonitorDatasource;
|
||||
pseudoDatasource[AzureQueryType.InsightsAnalytics] = this.insightsAnalyticsDatasource;
|
||||
pseudoDatasource[AzureQueryType.LogAnalytics] = this.azureLogAnalyticsDatasource;
|
||||
pseudoDatasource[AzureQueryType.AzureResourceGraph] = this.azureResourceGraphDatasource;
|
||||
this.pseudoDatasource = pseudoDatasource;
|
||||
|
||||
const optionsKey: any = {};
|
||||
@@ -49,6 +53,7 @@ export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDa
|
||||
optionsKey[AzureQueryType.AzureMonitor] = 'azureMonitor';
|
||||
optionsKey[AzureQueryType.InsightsAnalytics] = 'insightsAnalytics';
|
||||
optionsKey[AzureQueryType.LogAnalytics] = 'azureLogAnalytics';
|
||||
optionsKey[AzureQueryType.AzureResourceGraph] = 'azureResourceGraph';
|
||||
this.optionsKey = optionsKey;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,74 @@
|
||||
},
|
||||
"headers": [{ "name": "x-ms-app", "content": "Grafana" }]
|
||||
},
|
||||
{
|
||||
"path": "azureresourcegraph",
|
||||
"method": "POST",
|
||||
"url": "https://management.azure.com",
|
||||
"authType": "azure",
|
||||
"tokenAuth": {
|
||||
"scopes": ["https://management.azure.com/.default"],
|
||||
"params": {
|
||||
"azure_auth_type": "{{.JsonData.azureAuthType}}",
|
||||
"azure_cloud": "AzureCloud",
|
||||
"tenant_id": "{{.JsonData.tenantId}}",
|
||||
"client_id": "{{.JsonData.clientId}}",
|
||||
"client_secret": "{{.SecureJsonData.clientSecret}}"
|
||||
}
|
||||
},
|
||||
"headers": [{ "name": "x-ms-app", "content": "Grafana" }]
|
||||
},
|
||||
{
|
||||
"path": "chinaazureresourcegraph",
|
||||
"method": "POST",
|
||||
"url": "https://management.azure.com",
|
||||
"authType": "azure",
|
||||
"tokenAuth": {
|
||||
"scopes": ["https://management.chinacloudapi.cn/.default"],
|
||||
"params": {
|
||||
"azure_auth_type": "{{.JsonData.azureAuthType}}",
|
||||
"azure_cloud": "AzureChinaCloud",
|
||||
"tenant_id": "{{.JsonData.tenantId}}",
|
||||
"client_id": "{{.JsonData.clientId}}",
|
||||
"client_secret": "{{.SecureJsonData.clientSecret}}"
|
||||
}
|
||||
},
|
||||
"headers": [{ "name": "x-ms-app", "content": "Grafana" }]
|
||||
},
|
||||
{
|
||||
"path": "govazureresourcegraph",
|
||||
"method": "POST",
|
||||
"url": "https://management.usgovcloudapi.net",
|
||||
"authType": "azure",
|
||||
"tokenAuth": {
|
||||
"scopes": ["https://management.usgovcloudapi.net/.default"],
|
||||
"params": {
|
||||
"azure_auth_type": "{{.JsonData.azureAuthType}}",
|
||||
"azure_cloud": "AzureUSGovernment",
|
||||
"tenant_id": "{{.JsonData.tenantId}}",
|
||||
"client_id": "{{.JsonData.clientId}}",
|
||||
"client_secret": "{{.SecureJsonData.clientSecret}}"
|
||||
}
|
||||
},
|
||||
"headers": [{ "name": "x-ms-app", "content": "Grafana" }]
|
||||
},
|
||||
{
|
||||
"path": "germanyazureresourcegraph",
|
||||
"method": "POST",
|
||||
"url": "https://management.microsoftazure.de",
|
||||
"authType": "azure",
|
||||
"tokenAuth": {
|
||||
"scopes": ["https://management.microsoftazure.de/.default"],
|
||||
"params": {
|
||||
"azure_auth_type": "{{.JsonData.azureAuthType}}",
|
||||
"azure_cloud": "AzureGermanCloud",
|
||||
"tenant_id": "{{.JsonData.tenantId}}",
|
||||
"client_id": "{{.JsonData.clientId}}",
|
||||
"client_secret": "{{.SecureJsonData.clientSecret}}"
|
||||
}
|
||||
},
|
||||
"headers": [{ "name": "x-ms-app", "content": "Grafana" }]
|
||||
},
|
||||
{
|
||||
"path": "appinsights",
|
||||
"method": "GET",
|
||||
|
||||
@@ -27,12 +27,14 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
{ id: AzureQueryType.LogAnalytics, label: 'Logs' },
|
||||
{ id: AzureQueryType.ApplicationInsights, label: 'Application Insights' },
|
||||
{ id: AzureQueryType.InsightsAnalytics, label: 'Insights Analytics' },
|
||||
{ id: AzureQueryType.AzureResourceGraph, label: 'Azure Resource Graph' },
|
||||
];
|
||||
|
||||
// Query types that have been migrated to React
|
||||
reactQueryEditors = [
|
||||
AzureQueryType.AzureMonitor,
|
||||
AzureQueryType.LogAnalytics,
|
||||
AzureQueryType.AzureResourceGraph,
|
||||
// AzureQueryType.ApplicationInsights,
|
||||
// AzureQueryType.InsightsAnalytics,
|
||||
];
|
||||
@@ -44,12 +46,17 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
refId: string;
|
||||
queryType: AzureQueryType;
|
||||
subscription: string;
|
||||
subscriptions: string[];
|
||||
azureMonitor: AzureMetricQuery;
|
||||
azureLogAnalytics: {
|
||||
query: string;
|
||||
resultFormat: string;
|
||||
workspace: string;
|
||||
};
|
||||
azureResourceGraph: {
|
||||
query: string;
|
||||
resultFormat: string;
|
||||
};
|
||||
appInsights: {
|
||||
// metric style query when rawQuery == false
|
||||
metricName: string;
|
||||
@@ -105,6 +112,9 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
? this.datasource.azureLogAnalyticsDatasource.defaultOrFirstWorkspace
|
||||
: '',
|
||||
},
|
||||
azureResourceGraph: {
|
||||
resultFormat: 'table',
|
||||
},
|
||||
appInsights: {
|
||||
metricName: this.defaultDropdownValue,
|
||||
// dimension: [],
|
||||
@@ -327,6 +337,10 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
this.target.subscription = this.subscriptions[0].value;
|
||||
}
|
||||
|
||||
if (!this.target.subscriptions) {
|
||||
this.target.subscriptions = subscriptions.map((sub) => sub.value);
|
||||
}
|
||||
|
||||
return this.subscriptions;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,17 +17,20 @@ export enum AzureQueryType {
|
||||
ApplicationInsights = 'Application Insights',
|
||||
InsightsAnalytics = 'Insights Analytics',
|
||||
LogAnalytics = 'Azure Log Analytics',
|
||||
AzureResourceGraph = 'Azure Resource Graph',
|
||||
}
|
||||
|
||||
export interface AzureMonitorQuery extends DataQuery {
|
||||
queryType: AzureQueryType;
|
||||
format: string;
|
||||
subscription: string;
|
||||
subscriptions: string[];
|
||||
|
||||
azureMonitor: AzureMetricQuery;
|
||||
azureLogAnalytics: AzureLogsQuery;
|
||||
appInsights?: ApplicationInsightsQuery;
|
||||
insightsAnalytics: InsightsAnalyticsQuery;
|
||||
azureResourceGraph: AzureResourceGraphQuery;
|
||||
}
|
||||
|
||||
export type ConcealedSecret = symbol;
|
||||
@@ -91,6 +94,11 @@ export interface AzureLogsQuery {
|
||||
workspace: string;
|
||||
}
|
||||
|
||||
export interface AzureResourceGraphQuery {
|
||||
query: string;
|
||||
resultFormat: string;
|
||||
}
|
||||
|
||||
export interface ApplicationInsightsQuery {
|
||||
metricName: string;
|
||||
timeGrain: string;
|
||||
|
||||
@@ -6,6 +6,17 @@ import { AzureMonitorOption } from '../types';
|
||||
export const findOption = (options: AzureMonitorOption[], value: string | undefined) =>
|
||||
value ? options.find((v) => v.value === value) ?? { value, label: value } : null;
|
||||
|
||||
export const findOptions = (options: AzureMonitorOption[], values: string[] = []) => {
|
||||
if (values.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const set = values.reduce((accum, item) => {
|
||||
accum.add(item);
|
||||
return accum;
|
||||
}, new Set());
|
||||
return options.filter((option) => set.has(option.value));
|
||||
};
|
||||
|
||||
export const toOption = (v: { text: string; value: string }) => ({ value: v.value, label: v.text });
|
||||
|
||||
export function convertTimeGrainsToMs<T extends { value: string }>(timeGrains: T[]) {
|
||||
|
||||
Reference in New Issue
Block a user