AzureMonitor: Make all query properties optional (#36701)

* start to make all query fields optional

* fix other types

* fix optional types in fields

* Pfeedback!

* Clean up types
This commit is contained in:
Josh Hunt 2021-07-14 11:36:11 +01:00 committed by GitHub
parent 10c892fa5b
commit 43ee7acecd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 480 additions and 510 deletions

View File

@ -13,7 +13,6 @@ export default function createMockQuery(): AzureMonitorQuery {
azureResourceGraph: {
query: 'Resources | summarize count()',
resultFormat: 'time_series',
},
azureMonitor: {
@ -42,6 +41,5 @@ export default function createMockQuery(): AzureMonitorQuery {
refId: 'A',
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
};
}

View File

@ -61,14 +61,18 @@ export default class AppInsightsDatasource extends DataSourceWithBackend<AzureMo
};
}
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): Record<string, any> {
const item = target.appInsights!;
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): AzureMonitorQuery {
const item = target.appInsights;
if (!item) {
return target;
}
const old: any = item;
// fix for timeGrainUnit which is a deprecated/removed field name
if (old.timeGrainCount) {
item.timeGrain = TimegrainConverter.createISO8601Duration(old.timeGrainCount, item.timeGrainUnit);
} else if (item.timeGrainUnit && item.timeGrain !== 'auto') {
} else if (item.timeGrain && item.timeGrainUnit && item.timeGrain !== 'auto') {
item.timeGrain = TimegrainConverter.createISO8601Duration(item.timeGrain, item.timeGrainUnit);
}
@ -95,9 +99,7 @@ export default class AppInsightsDatasource extends DataSourceWithBackend<AzureMo
const templateSrv = getTemplateSrv();
return {
type: 'timeSeriesQuery',
refId: target.refId,
format: target.format,
queryType: AzureQueryType.ApplicationInsights,
appInsights: {
timeGrain: templateSrv.replace((item.timeGrain || '').toString(), scopedVars),
@ -106,7 +108,6 @@ export default class AppInsightsDatasource extends DataSourceWithBackend<AzureMo
dimension: item.dimension.map((d) => templateSrv.replace(d, scopedVars)),
dimensionFilter: templateSrv.replace(item.dimensionFilter, scopedVars),
alias: item.alias,
format: target.format,
},
};
}

View File

@ -112,8 +112,11 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
return transformMetadataToKustoSchema(metadata, resourceUri);
}
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): Record<string, any> {
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): AzureMonitorQuery {
const item = target.azureLogAnalytics;
if (!item) {
return target;
}
const templateSrv = getTemplateSrv();
const resource = templateSrv.replace(item.resource, scopedVars);
@ -123,21 +126,19 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
workspace = this.defaultOrFirstWorkspace;
}
const subscriptionId = templateSrv.replace(target.subscription || this.defaultSubscriptionId, scopedVars);
const query = templateSrv.replace(item.query, scopedVars, this.interpolateVariable);
return {
refId: target.refId,
format: target.format,
queryType: AzureQueryType.LogAnalytics,
subscriptionId: subscriptionId,
azureLogAnalytics: {
resultFormat: item.resultFormat,
query: query,
query,
resource,
// TODO: Workspace is deprecated and should be migrated to Resources
workspace: workspace,
// Workspace was removed in Grafana 8, but remains for backwards compat
workspace,
},
};
}

View File

@ -77,6 +77,7 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
filterQuery(item: AzureMonitorQuery): boolean {
return !!(
item.hide !== true &&
item.azureMonitor &&
item.azureMonitor.resourceGroup &&
item.azureMonitor.resourceGroup !== defaultDropdownValue &&
item.azureMonitor.resourceName &&
@ -110,22 +111,24 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
if (res.data) {
for (const df of res.data) {
const metricQuery = metricQueries[df.refId];
if (metricQuery && metricQuery.azureMonitor) {
const url = this.buildAzurePortalUrl(
metricQuery.azureMonitor,
metricQuery.subscription,
this.timeSrv.timeRange()
);
if (!metricQuery.azureMonitor || !metricQuery.subscription) {
continue;
}
for (const field of df.fields) {
field.config.links = [
{
url: url,
title: 'View in Azure Portal',
targetBlank: true,
},
];
}
const url = this.buildAzurePortalUrl(
metricQuery.azureMonitor,
metricQuery.subscription,
this.timeSrv.timeRange()
);
for (const field of df.fields) {
field.config.links = [
{
url: url,
title: 'View in Azure Portal',
targetBlank: true,
},
];
}
}
}
@ -172,11 +175,16 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
return `${this.azurePortalUrl}/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/${timeContext}/ChartDefinition/${chartDef}`;
}
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): Record<string, any> {
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): AzureMonitorQuery {
const item = target.azureMonitor;
if (!item) {
// return target;
throw new Error('Query is not a valid Azure Monitor Metrics query');
}
// fix for timeGrainUnit which is a deprecated/removed field name
if (item.timeGrainUnit && item.timeGrain !== 'auto') {
if (item.timeGrain && item.timeGrainUnit && item.timeGrain !== 'auto') {
item.timeGrain = TimegrainConverter.createISO8601Duration(item.timeGrain, item.timeGrainUnit);
}
@ -191,7 +199,7 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
const aggregation = templateSrv.replace(item.aggregation, scopedVars);
const top = templateSrv.replace(item.top || '', scopedVars);
const dimensionFilters = item.dimensionFilters
const dimensionFilters = (item.dimensionFilters ?? [])
.filter((f) => f.dimension && f.dimension !== 'None')
.map((f) => {
const filter = templateSrv.replace(f.filter ?? '', scopedVars);
@ -219,7 +227,6 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
dimensionFilters,
top: top || '10',
alias: item.alias,
format: target.format,
},
};
}

View File

@ -75,7 +75,6 @@ describe('AzureResourceGraphDatasource', () => {
};
expect(ctx.ds.applyTemplateVariables(target)).toStrictEqual({
azureResourceGraph: { query: 'Resources | var1-foo', resultFormat: 'table' },
format: undefined,
queryType: 'Azure Resource Graph',
refId: undefined,
subscriptions: undefined,
@ -94,7 +93,6 @@ describe('AzureResourceGraphDatasource', () => {
query: `resources | where $__contains(name, 'var3-foo','var3-baz')`,
resultFormat: 'table',
},
format: undefined,
queryType: 'Azure Resource Graph',
refId: undefined,
subscriptions: undefined,

View File

@ -12,16 +12,17 @@ export default class AzureResourceGraphDatasource extends DataSourceWithBackend<
return !!item.azureResourceGraph?.query;
}
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): Record<string, any> {
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): AzureMonitorQuery {
const item = target.azureResourceGraph;
if (!item) {
return target;
}
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: {

View File

@ -18,7 +18,7 @@ const QueryField: React.FC<AzureQueryEditorFieldProps> = ({ query, onQueryChange
return (
<CodeEditor
value={query.azureResourceGraph.query}
value={query.azureResourceGraph?.query ?? ''}
language="kusto"
height={200}
width={1000}

View File

@ -19,7 +19,7 @@ const InsightsAnalyticsEditor: React.FC<InsightsAnalyticsEditorProps> = ({ query
<div data-testid="azure-monitor-insights-analytics-query-editor">
<CodeEditor
language="kusto"
value={query.insightsAnalytics.query}
value={query.insightsAnalytics?.query ?? ''}
height={200}
width="100%"
readOnly={true}
@ -29,7 +29,7 @@ const InsightsAnalyticsEditor: React.FC<InsightsAnalyticsEditorProps> = ({ query
<Field label="Format as">
<Select
inputId="azure-monitor-logs-workspaces-field"
value={findOption(FORMAT_OPTIONS, query.insightsAnalytics.resultFormat)}
value={findOption(FORMAT_OPTIONS, query.insightsAnalytics?.resultFormat)}
disabled={true}
options={FORMAT_OPTIONS}
onChange={() => {}}

View File

@ -35,7 +35,7 @@ const FormatAsField: React.FC<AzureQueryEditorFieldProps> = ({ query, variableOp
<Field label="Format as">
<Select
inputId="azure-monitor-logs-workspaces-field"
value={findOption(FORMAT_OPTIONS, query.azureLogAnalytics.resultFormat)}
value={findOption(FORMAT_OPTIONS, query.azureLogAnalytics?.resultFormat)}
onChange={handleChange}
options={options}
width={38}

View File

@ -31,7 +31,7 @@ const QueryField: React.FC<AzureQueryEditorFieldProps> = ({ query, datasource, o
}
useEffect(() => {
if (!query.azureLogAnalytics.resource) {
if (!query.azureLogAnalytics?.resource) {
return;
}
@ -54,7 +54,7 @@ const QueryField: React.FC<AzureQueryEditorFieldProps> = ({ query, datasource, o
});
});
});
}, [datasource.azureLogAnalyticsDatasource, query.azureLogAnalytics.resource]);
}, [datasource.azureLogAnalyticsDatasource, query.azureLogAnalytics?.resource]);
const handleEditorMount = useCallback((editor: MonacoEditor, monaco: Monaco) => {
monacoPromiseRef.current?.resolve?.({ editor, monaco });
@ -75,7 +75,7 @@ const QueryField: React.FC<AzureQueryEditorFieldProps> = ({ query, datasource, o
return (
<CodeEditor
value={query.azureLogAnalytics.query}
value={query.azureLogAnalytics?.query ?? ''}
language="kusto"
height={200}
width="100%"

View File

@ -25,7 +25,7 @@ function parseResourceDetails(resourceURI: string) {
const ResourceField: React.FC<AzureQueryEditorFieldProps> = ({ query, datasource, onQueryChange }) => {
const styles = useStyles2(getStyles);
const { resource } = query.azureLogAnalytics;
const { resource } = query.azureLogAnalytics ?? {};
const [pickerIsOpen, setPickerIsOpen] = useState(false);
@ -58,7 +58,7 @@ const ResourceField: React.FC<AzureQueryEditorFieldProps> = ({ query, datasource
<Modal className={styles.modal} title="Select a resource" isOpen={pickerIsOpen} onDismiss={closePicker}>
<ResourcePicker
resourcePickerData={datasource.resourcePickerData}
resourceURI={query.azureLogAnalytics.resource!}
resourceURI={resource}
templateVariables={templateVariables}
onApply={handleApply}
onCancel={closePicker}

View File

@ -1,65 +0,0 @@
import { SelectableValue } from '@grafana/data';
import { Select } from '@grafana/ui';
import React, { useEffect, useState, useMemo, useCallback } from 'react';
import { AzureQueryEditorFieldProps, AzureMonitorOption } from '../../types';
import { findOption, toOption } from '../../utils/common';
import { Field } from '../Field';
const ERROR_SOURCE = 'logs-workspaces';
const WorkspaceField: React.FC<AzureQueryEditorFieldProps> = ({
query,
datasource,
subscriptionId,
variableOptionGroup,
onQueryChange,
setError,
}) => {
const [workspaces, setWorkspaces] = useState<AzureMonitorOption[]>([]);
useEffect(() => {
if (!subscriptionId) {
workspaces.length > 0 && setWorkspaces([]);
return;
}
datasource
.getAzureLogAnalyticsWorkspaces(subscriptionId)
.then((results) => {
setWorkspaces(results.map(toOption));
})
.catch((err) => setError(ERROR_SOURCE, err));
}, [datasource, setError, subscriptionId, workspaces.length]);
const handleChange = useCallback(
(change: SelectableValue<string>) => {
if (!change.value) {
return;
}
onQueryChange({
...query,
azureLogAnalytics: {
...query.azureLogAnalytics,
workspace: change.value,
},
});
},
[onQueryChange, query]
);
const options = useMemo(() => [...workspaces, variableOptionGroup], [workspaces, variableOptionGroup]);
return (
<Field label="Workspace">
<Select
inputId="azure-monitor-logs-workspaces-field"
value={findOption(workspaces, query.azureLogAnalytics.workspace)}
onChange={handleChange}
options={options}
width={38}
/>
</Field>
);
};
export default WorkspaceField;

View File

@ -8,7 +8,7 @@ async function migrateWorkspaceQueryToResourceQuery(
query: AzureMonitorQuery,
onChange: (newQuery: AzureMonitorQuery) => void
) {
if (query.azureLogAnalytics.workspace !== undefined && !query.azureLogAnalytics.resource) {
if (query.azureLogAnalytics?.workspace !== undefined && !query.azureLogAnalytics.resource) {
const isWorkspaceGUID = isGUIDish(query.azureLogAnalytics.workspace);
let resource: string;

View File

@ -44,7 +44,7 @@ const AggregationField: React.FC<AggregationFieldProps> = ({
<Field label="Aggregation">
<Select
inputId="azure-monitor-metrics-aggregation-field"
value={findOption(aggregationOptions, query.azureMonitor.aggregation)}
value={findOption(aggregationOptions, query.azureMonitor?.aggregation)}
onChange={handleChange}
options={options}
width={38}

View File

@ -1,4 +1,4 @@
import React, { useCallback } from 'react';
import React, { useCallback, useMemo } from 'react';
import { Button, Select, Input, HorizontalGroup, VerticalGroup, InlineLabel } from '@grafana/ui';
import { Field } from '../Field';
@ -10,6 +10,10 @@ interface DimensionFieldsProps extends AzureQueryEditorFieldProps {
}
const DimensionFields: React.FC<DimensionFieldsProps> = ({ query, dimensionOptions, onQueryChange }) => {
const dimensionFilters = useMemo(() => query.azureMonitor?.dimensionFilters ?? [], [
query.azureMonitor?.dimensionFilters,
]);
const setDimensionFilters = useCallback(
(newFilters: AzureMetricDimension[]) => {
onQueryChange({
@ -25,17 +29,17 @@ const DimensionFields: React.FC<DimensionFieldsProps> = ({ query, dimensionOptio
const addFilter = useCallback(() => {
setDimensionFilters([
...query.azureMonitor.dimensionFilters,
...dimensionFilters,
{
dimension: '',
operator: 'eq',
filter: '',
},
]);
}, [query.azureMonitor.dimensionFilters, setDimensionFilters]);
}, [dimensionFilters, setDimensionFilters]);
const removeFilter = (index: number) => {
const newFilters = [...query.azureMonitor.dimensionFilters];
const newFilters = [...dimensionFilters];
newFilters.splice(index, 1);
setDimensionFilters(newFilters);
};
@ -45,7 +49,7 @@ const DimensionFields: React.FC<DimensionFieldsProps> = ({ query, dimensionOptio
fieldName: Key,
value: AzureMetricDimension[Key]
) => {
const newFilters = [...query.azureMonitor.dimensionFilters];
const newFilters = [...dimensionFilters];
const newFilter = newFilters[filterIndex];
newFilter[fieldName] = value;
setDimensionFilters(newFilters);
@ -60,7 +64,7 @@ const DimensionFields: React.FC<DimensionFieldsProps> = ({ query, dimensionOptio
return (
<Field label="Dimension">
<VerticalGroup spacing="xs">
{query.azureMonitor.dimensionFilters.map((filter, index) => (
{dimensionFilters.map((filter, index) => (
<HorizontalGroup key={index} spacing="xs">
<Select
placeholder="Field"

View File

@ -5,7 +5,7 @@ import { Field } from '../Field';
import { AzureQueryEditorFieldProps } from '../../types';
const LegendFormatField: React.FC<AzureQueryEditorFieldProps> = ({ onQueryChange, query }) => {
const [value, setValue] = useState<string>(query.azureMonitor.alias ?? '');
const [value, setValue] = useState<string>(query.azureMonitor?.alias ?? '');
// As calling onQueryChange initiates a the datasource refresh, we only want to call it once
// the field loses focus

View File

@ -19,7 +19,7 @@ const MetricName: React.FC<AzureQueryEditorFieldProps> = ({
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const { resourceGroup, metricDefinition, resourceName, metricNamespace } = query.azureMonitor;
const { resourceGroup, metricDefinition, resourceName, metricNamespace } = query.azureMonitor ?? {};
if (!(subscriptionId && resourceGroup && metricDefinition && resourceName && metricNamespace)) {
metricNames.length > 0 && setMetricNames([]);
return;
@ -61,7 +61,7 @@ const MetricName: React.FC<AzureQueryEditorFieldProps> = ({
<Field label="Metric">
<Select
inputId="azure-monitor-metrics-metric-field"
value={findOption(metricNames, query.azureMonitor.metricName)}
value={findOption(metricNames, query.azureMonitor?.metricName)}
onChange={handleChange}
options={options}
width={38}

View File

@ -19,7 +19,7 @@ const MetricNamespaceField: React.FC<AzureQueryEditorFieldProps> = ({
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const { resourceGroup, metricDefinition, resourceName } = query.azureMonitor;
const { resourceGroup, metricDefinition, resourceName } = query.azureMonitor ?? {};
if (!(subscriptionId && resourceGroup && metricDefinition && resourceName)) {
metricNamespaces.length > 0 && setMetricNamespaces([]);
return;
@ -73,7 +73,7 @@ const MetricNamespaceField: React.FC<AzureQueryEditorFieldProps> = ({
<Field label="Metric namespace">
<Select
inputId="azure-monitor-metrics-metric-namespace-field"
value={findOption(metricNamespaces, query.azureMonitor.metricNamespace)}
value={findOption(metricNamespaces, query.azureMonitor?.metricNamespace)}
onChange={handleChange}
options={options}
width={38}

View File

@ -32,7 +32,7 @@ describe('Azure Monitor QueryEditor', () => {
const mockDatasource = createMockDatasource();
const onChange = jest.fn();
const mockQuery = createMockQuery();
mockQuery.azureMonitor.metricName = undefined;
(mockQuery.azureMonitor ?? {}).metricName = undefined;
mockDatasource.azureMonitorDatasource.getSubscriptions = jest.fn().mockResolvedValueOnce([
{
value: 'abc-123',
@ -117,7 +117,7 @@ describe('Azure Monitor QueryEditor', () => {
const mockDatasource = createMockDatasource();
const onChange = jest.fn();
const mockQuery = createMockQuery();
mockQuery.azureMonitor.aggregation = undefined;
(mockQuery.azureMonitor ?? {}).aggregation = undefined;
mockDatasource.getMetricNames = jest.fn().mockResolvedValue([
{
value: 'metric-a',

View File

@ -62,7 +62,7 @@ const ResourceGroupsField: React.FC<AzureQueryEditorFieldProps> = ({
<Field label="Resource group">
<Select
inputId="azure-monitor-metrics-resource-group-field"
value={findOption(resourceGroups, query.azureMonitor.resourceGroup)}
value={findOption(resourceGroups, query.azureMonitor?.resourceGroup)}
onChange={handleChange}
options={options}
width={38}

View File

@ -18,7 +18,7 @@ const ResourceNameField: React.FC<AzureQueryEditorFieldProps> = ({
const [resourceNames, setResourceNames] = useState<AzureMonitorOption[]>([]);
useEffect(() => {
const { resourceGroup, metricDefinition } = query.azureMonitor;
const { resourceGroup, metricDefinition } = query.azureMonitor ?? {};
if (!(subscriptionId && resourceGroup && metricDefinition)) {
resourceNames.length > 0 && setResourceNames([]);
@ -56,7 +56,7 @@ const ResourceNameField: React.FC<AzureQueryEditorFieldProps> = ({
const options = useMemo(() => [...resourceNames, variableOptionGroup], [resourceNames, variableOptionGroup]);
const selectedResourceNameValue = findOption(resourceNames, query.azureMonitor.resourceName);
const selectedResourceNameValue = findOption(resourceNames, query.azureMonitor?.resourceName);
return (
<Field label="Resource name">
<Select

View File

@ -18,7 +18,7 @@ const NamespaceField: React.FC<AzureQueryEditorFieldProps> = ({
const [namespaces, setNamespaces] = useState<AzureMonitorOption[]>([]);
useEffect(() => {
const { resourceGroup } = query.azureMonitor;
const { resourceGroup } = query.azureMonitor ?? {};
if (!(subscriptionId && resourceGroup)) {
namespaces.length && setNamespaces([]);
@ -61,7 +61,7 @@ const NamespaceField: React.FC<AzureQueryEditorFieldProps> = ({
{/* It's expected that the label reads Resource type but the property is metricDefinition */}
<Select
inputId="azure-monitor-metrics-resource-type-field"
value={findOption(namespaces, query.azureMonitor.metricDefinition)}
value={findOption(namespaces, query.azureMonitor?.metricDefinition)}
onChange={handleChange}
options={options}
width={38}

View File

@ -58,7 +58,7 @@ const TimeGrainField: React.FC<TimeGrainFieldProps> = ({
<Field label="Time grain">
<Select
inputId="azure-monitor-metrics-time-grain-field"
value={findOption(timeGrainOptions, query.azureMonitor.timeGrain)}
value={findOption(timeGrainOptions, query.azureMonitor?.timeGrain)}
onChange={handleChange}
options={timeGrains}
width={38}

View File

@ -5,7 +5,7 @@ import { Field } from '../Field';
import { AzureQueryEditorFieldProps } from '../../types';
const TopField: React.FC<AzureQueryEditorFieldProps> = ({ onQueryChange, query }) => {
const [value, setValue] = useState<string>(query.azureMonitor.top ?? '');
const [value, setValue] = useState<string>(query.azureMonitor?.top ?? '');
// As calling onQueryChange initiates a the datasource refresh, we only want to call it once
// the field loses focus

View File

@ -18,11 +18,6 @@ jest.mock('@grafana/ui', () => ({
},
}));
const variableOptionGroup = {
label: 'Template variables',
options: [],
};
describe('Azure Monitor QueryEditor', () => {
it('renders the Metrics query editor when the query type is Metrics', async () => {
const mockDatasource = createMockDatasource();
@ -31,14 +26,7 @@ describe('Azure Monitor QueryEditor', () => {
queryType: AzureQueryType.AzureMonitor,
};
render(
<QueryEditor
query={mockQuery}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={() => {}}
/>
);
render(<QueryEditor query={mockQuery} datasource={mockDatasource} onChange={() => {}} onRunQuery={() => {}} />);
await waitFor(() => expect(screen.getByTestId('azure-monitor-metrics-query-editor')).toBeInTheDocument());
});
@ -49,14 +37,7 @@ describe('Azure Monitor QueryEditor', () => {
queryType: AzureQueryType.LogAnalytics,
};
render(
<QueryEditor
query={mockQuery}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={() => {}}
/>
);
render(<QueryEditor query={mockQuery} datasource={mockDatasource} onChange={() => {}} onRunQuery={() => {}} />);
await waitFor(() => expect(screen.queryByTestId('azure-monitor-logs-query-editor')).toBeInTheDocument());
});
@ -78,14 +59,7 @@ describe('Azure Monitor QueryEditor', () => {
},
};
render(
<QueryEditor
query={mockQuery}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={() => {}}
/>
);
render(<QueryEditor query={mockQuery} datasource={mockDatasource} onChange={() => {}} onRunQuery={() => {}} />);
await waitFor(() =>
expect(screen.queryByTestId('azure-monitor-application-insights-query-editor')).toBeInTheDocument()
);
@ -99,14 +73,7 @@ describe('Azure Monitor QueryEditor', () => {
const mockDatasource = createMockDatasource();
const mockQuery = createMockQuery();
const onChange = jest.fn();
render(
<QueryEditor
query={mockQuery}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
/>
);
render(<QueryEditor query={mockQuery} datasource={mockDatasource} onChange={onChange} onRunQuery={() => {}} />);
await waitFor(() => expect(screen.getByTestId('azure-monitor-query-editor')).toBeInTheDocument());
const metrics = await screen.findByLabelText('Service');
@ -122,12 +89,7 @@ describe('Azure Monitor QueryEditor', () => {
const mockDatasource = createMockDatasource();
mockDatasource.azureMonitorDatasource.getSubscriptions = jest.fn().mockRejectedValue(invalidNamespaceError());
render(
<QueryEditor
query={createMockQuery()}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={() => {}}
/>
<QueryEditor query={createMockQuery()} datasource={mockDatasource} onChange={() => {}} onRunQuery={() => {}} />
);
await waitFor(() => expect(screen.getByTestId('azure-monitor-query-editor')).toBeInTheDocument());
@ -141,14 +103,7 @@ describe('Azure Monitor QueryEditor', () => {
queryType: AzureQueryType.AzureMonitor,
};
render(
<QueryEditor
query={mockQuery}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={() => {}}
/>
);
render(<QueryEditor query={mockQuery} datasource={mockDatasource} onChange={() => {}} onRunQuery={() => {}} />);
await waitFor(() => expect(screen.getByTestId('azure-monitor-metrics-query-editor')).toBeInTheDocument());
const metrics = await screen.findByLabelText('Service');
@ -164,14 +119,7 @@ describe('Azure Monitor QueryEditor', () => {
queryType: AzureQueryType.ApplicationInsights,
};
render(
<QueryEditor
query={mockQuery}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={() => {}}
/>
);
render(<QueryEditor query={mockQuery} datasource={mockDatasource} onChange={() => {}} onRunQuery={() => {}} />);
await waitFor(() =>
expect(screen.getByTestId('azure-monitor-application-insights-query-editor')).toBeInTheDocument()
);

View File

@ -1,7 +1,14 @@
import { Alert } from '@grafana/ui';
import { QueryEditorProps } from '@grafana/data';
import React from 'react';
import Datasource from '../../datasource';
import { AzureMonitorQuery, AzureQueryType, AzureMonitorOption, AzureMonitorErrorish } from '../../types';
import AzureMonitorDatasource from '../../datasource';
import {
AzureMonitorQuery,
AzureQueryType,
AzureMonitorOption,
AzureMonitorErrorish,
AzureDataSourceJsonData,
} from '../../types';
import MetricsQueryEditor from '../MetricsQueryEditor';
import QueryTypeField from './QueryTypeField';
import useLastError from '../../utils/useLastError';
@ -11,14 +18,13 @@ import ApplicationInsightsEditor from '../ApplicationInsightsEditor';
import InsightsAnalyticsEditor from '../InsightsAnalyticsEditor';
import { Space } from '../Space';
interface BaseQueryEditorProps {
query: AzureMonitorQuery;
datasource: Datasource;
onChange: (newQuery: AzureMonitorQuery) => void;
variableOptionGroup: { label: string; options: AzureMonitorOption[] };
}
export type AzureMonitorQueryEditorProps = QueryEditorProps<
AzureMonitorDatasource,
AzureMonitorQuery,
AzureDataSourceJsonData
>;
const QueryEditor: React.FC<BaseQueryEditorProps> = ({ query, datasource, onChange }) => {
const QueryEditor: React.FC<AzureMonitorQueryEditorProps> = ({ query, datasource, onChange }) => {
const [errorMessage, setError] = useLastError();
const subscriptionId = query.subscription || datasource.azureMonitorDatasource.defaultSubscriptionId;
const variableOptionGroup = {
@ -51,8 +57,9 @@ const QueryEditor: React.FC<BaseQueryEditorProps> = ({ query, datasource, onChan
);
};
interface EditorForQueryTypeProps extends BaseQueryEditorProps {
interface EditorForQueryTypeProps extends Omit<AzureMonitorQueryEditorProps, 'onRunQuery'> {
subscriptionId?: string;
variableOptionGroup: { label: string; options: AzureMonitorOption[] };
setError: (source: string, error: AzureMonitorErrorish | undefined) => void;
}
@ -65,6 +72,7 @@ const EditorForQueryType: React.FC<EditorForQueryTypeProps> = ({
setError,
}) => {
switch (query.queryType) {
case undefined:
case AzureQueryType.AzureMonitor:
return (
<MetricsQueryEditor

View File

@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState, useMemo } from 'react';
import { SelectableValue } from '@grafana/data';
import { Select, MultiSelect } from '@grafana/ui';
import { AzureMonitorQuery, AzureQueryType, AzureQueryEditorFieldProps, AzureMonitorOption } from '../types';
import { AzureMonitorQuery, AzureQueryEditorFieldProps, AzureMonitorOption, AzureQueryType } from '../types';
import { findOption, findOptions } from '../utils/common';
import { Field } from './Field';
@ -64,8 +64,6 @@ const SubscriptionField: React.FC<SubscriptionFieldProps> = ({
};
if (query.queryType === AzureQueryType.AzureMonitor) {
// TODO: set the fields to undefined so we don't
// get "resource group select could not be found" errors
newQuery.azureMonitor = {
...newQuery.azureMonitor,
resourceGroup: undefined,

View File

@ -28,6 +28,7 @@ export function useMetricsMetadata(
if (
!(
subscriptionId &&
query.azureMonitor &&
query.azureMonitor.resourceGroup &&
query.azureMonitor.metricDefinition &&
query.azureMonitor.resourceName &&
@ -53,10 +54,10 @@ export function useMetricsMetadata(
azureMonitor: {
...query.azureMonitor,
aggregation:
query.azureMonitor.aggregation && metadata.supportedAggTypes.includes(query.azureMonitor.aggregation)
query.azureMonitor?.aggregation && metadata.supportedAggTypes.includes(query.azureMonitor.aggregation)
? query.azureMonitor.aggregation
: metadata.primaryAggType,
timeGrain: query.azureMonitor.timeGrain || 'auto',
timeGrain: query.azureMonitor?.timeGrain || 'auto', // TODO: move this default value somewhere better?
allowedTimeGrainsMs: convertTimeGrainsToMs(metadata.supportedTimeGrains),
},
});
@ -80,11 +81,11 @@ export function useMetricsMetadata(
});
}, [
subscriptionId,
query.azureMonitor.resourceGroup,
query.azureMonitor.metricDefinition,
query.azureMonitor.resourceName,
query.azureMonitor.metricNamespace,
query.azureMonitor.metricName,
query.azureMonitor?.resourceGroup,
query.azureMonitor?.metricDefinition,
query.azureMonitor?.resourceName,
query.azureMonitor?.metricNamespace,
query.azureMonitor?.metricName,
query,
datasource,
onQueryChange,

View File

@ -20,7 +20,7 @@ import {
ScopedVars,
} from '@grafana/data';
import { forkJoin, Observable, of } from 'rxjs';
import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime';
import { getTemplateSrv, TemplateSrv } from '@grafana/runtime';
import InsightsAnalyticsDatasource from './insights_analytics/insights_analytics_datasource';
import { migrateMetricsDimensionFilters } from './query_ctrl';
import { map } from 'rxjs/operators';
@ -37,7 +37,15 @@ export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDa
/** @deprecated */
insightsAnalyticsDatasource?: InsightsAnalyticsDatasource;
pseudoDatasource: Record<AzureQueryType, DataSourceWithBackend>;
pseudoDatasource: {
[key in AzureQueryType]?:
| AzureMonitorDatasource
| AzureLogAnalyticsDatasource
| AzureResourceGraphDatasource
| AppInsightsDatasource
| InsightsAnalyticsDatasource;
} = {};
optionsKey: Record<AzureQueryType, string>;
constructor(
@ -50,42 +58,33 @@ export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDa
this.azureResourceGraphDatasource = new AzureResourceGraphDatasource(instanceSettings);
this.resourcePickerData = new ResourcePickerData(instanceSettings);
const pseudoDatasource: any = {};
pseudoDatasource[AzureQueryType.AzureMonitor] = this.azureMonitorDatasource;
pseudoDatasource[AzureQueryType.LogAnalytics] = this.azureLogAnalyticsDatasource;
pseudoDatasource[AzureQueryType.AzureResourceGraph] = this.azureResourceGraphDatasource;
this.pseudoDatasource = {
[AzureQueryType.AzureMonitor]: this.azureMonitorDatasource,
[AzureQueryType.LogAnalytics]: this.azureLogAnalyticsDatasource,
[AzureQueryType.AzureResourceGraph]: this.azureResourceGraphDatasource,
};
const cloud = getAzureCloud(instanceSettings);
if (cloud === 'azuremonitor' || cloud === 'chinaazuremonitor') {
// AppInsights and InsightAnalytics are only supported for Public and Azure China clouds
this.appInsightsDatasource = new AppInsightsDatasource(instanceSettings);
this.insightsAnalyticsDatasource = new InsightsAnalyticsDatasource(instanceSettings);
pseudoDatasource[AzureQueryType.ApplicationInsights] = this.appInsightsDatasource;
pseudoDatasource[AzureQueryType.InsightsAnalytics] = this.insightsAnalyticsDatasource;
this.pseudoDatasource[AzureQueryType.ApplicationInsights] = this.appInsightsDatasource;
this.pseudoDatasource[AzureQueryType.InsightsAnalytics] = this.insightsAnalyticsDatasource;
}
this.pseudoDatasource = pseudoDatasource;
const optionsKey: any = {};
optionsKey[AzureQueryType.ApplicationInsights] = 'appInsights';
optionsKey[AzureQueryType.AzureMonitor] = 'azureMonitor';
optionsKey[AzureQueryType.InsightsAnalytics] = 'insightsAnalytics';
optionsKey[AzureQueryType.LogAnalytics] = 'azureLogAnalytics';
optionsKey[AzureQueryType.AzureResourceGraph] = 'azureResourceGraph';
this.optionsKey = optionsKey;
}
query(options: DataQueryRequest<AzureMonitorQuery>): Observable<DataQueryResponse> {
console.log('query options.targets', options.targets);
const byType = new Map<AzureQueryType, DataQueryRequest<AzureMonitorQuery>>();
for (const target of options.targets) {
// Migrate old query structure
migrateQuery(target);
// Check that we have options
const opts = (target as any)[this.optionsKey[target.queryType]];
// Skip hidden queries or ones without properties
if (target.hide || !opts) {
// Skip hidden or invalid queries or ones without properties
if (!target.queryType || target.hide || !hasQueryForType(target)) {
continue;
}
@ -102,7 +101,12 @@ export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDa
}
const observables: Array<Observable<DataQueryResponse>> = Array.from(byType.entries()).map(([queryType, req]) => {
return this.pseudoDatasource[queryType].query(req);
const ds = this.pseudoDatasource[queryType];
if (!ds) {
throw new Error('Data source not created for query type ' + queryType);
}
return ds.query(req);
});
// Single query can skip merge
@ -270,9 +274,16 @@ export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDa
}
interpolateVariablesInQueries(queries: AzureMonitorQuery[], scopedVars: ScopedVars): AzureMonitorQuery[] {
return queries.map(
(query) => this.pseudoDatasource[query.queryType].applyTemplateVariables(query, scopedVars) as AzureMonitorQuery
);
const mapped = queries.map((query) => {
if (!query.queryType) {
return query;
}
const ds = this.pseudoDatasource[query.queryType];
return ds?.applyTemplateVariables(query, scopedVars) ?? query;
});
return mapped;
}
replaceTemplateVariable(variable: string) {
@ -296,7 +307,29 @@ function migrateQuery(target: AzureMonitorQuery) {
target.queryType = AzureQueryType.AzureMonitor;
}
if (target.queryType === AzureQueryType.AzureMonitor) {
if (target.queryType === AzureQueryType.AzureMonitor && target.azureMonitor) {
migrateMetricsDimensionFilters(target.azureMonitor);
}
}
function hasQueryForType(query: AzureMonitorQuery): boolean {
switch (query.queryType) {
case AzureQueryType.AzureMonitor:
return !!query.azureMonitor;
case AzureQueryType.LogAnalytics:
return !!query.azureLogAnalytics;
case AzureQueryType.AzureResourceGraph:
return !!query.azureResourceGraph;
case AzureQueryType.ApplicationInsights:
return !!query.appInsights;
case AzureQueryType.InsightsAnalytics:
return !!query.insightsAnalytics;
default:
return false;
}
}

View File

@ -9,20 +9,19 @@ export default class InsightsAnalyticsDatasource extends AppInsightsDatasource {
super(instanceSettings);
}
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): Record<string, any> {
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): AzureMonitorQuery {
const item = target.insightsAnalytics;
// Old name migrations
const old: any = item;
if (old.rawQueryString && !item.query) {
item.query = old.rawQueryString;
if (!item) {
return target;
}
const query = item.rawQueryString && !item.query ? item.rawQueryString : item.query;
return {
refId: target.refId,
queryType: AzureQueryType.InsightsAnalytics,
insightsAnalytics: {
query: getTemplateSrv().replace(item.query, scopedVars),
query: getTemplateSrv().replace(query, scopedVars),
resultFormat: item.resultFormat,
},
};

View File

@ -206,7 +206,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
if (this.target.azureMonitor.timeGrainUnit) {
if (this.target.azureMonitor.timeGrain !== 'auto') {
this.target.azureMonitor.timeGrain = TimegrainConverter.createISO8601Duration(
this.target.azureMonitor.timeGrain,
this.target.azureMonitor.timeGrain ?? 'auto',
this.target.azureMonitor.timeGrainUnit
);
}
@ -372,7 +372,10 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
}
getAzureMonitorAutoInterval() {
return this.generateAutoUnits(this.target.azureMonitor.timeGrain, (this.target.azureMonitor as any).timeGrains);
return this.generateAutoUnits(
this.target.azureMonitor.timeGrain ?? 'auto',
(this.target.azureMonitor as any).timeGrains
);
}
getApplicationInsightAutoInterval() {
@ -380,6 +383,9 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
}
azureMonitorAddDimensionFilter() {
this.target.azureMonitor = this.target.azureMonitor ?? {};
this.target.azureMonitor.dimensionFilters = this.target.azureMonitor.dimensionFilters ?? [];
this.target.azureMonitor.dimensionFilters.push({
dimension: '',
operator: 'eq',
@ -388,6 +394,9 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
}
azureMonitorRemoveDimensionFilter(index: number) {
this.target.azureMonitor = this.target.azureMonitor ?? {};
this.target.azureMonitor.dimensionFilters = this.target.azureMonitor.dimensionFilters ?? [];
this.target.azureMonitor.dimensionFilters.splice(index, 1);
this.refresh();
}

View File

@ -1,260 +1,3 @@
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 interface DatasourceValidationResult {
status: 'success' | 'error';
message: string;
title?: string;
}
export type AzureResultFormat = 'time_series' | 'table';
export enum AzureQueryType {
AzureMonitor = 'Azure Monitor',
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;
}
/**
* Azure clouds known to Azure Monitor.
*/
export enum AzureCloud {
Public = 'AzureCloud',
China = 'AzureChinaCloud',
USGovernment = 'AzureUSGovernment',
Germany = 'AzureGermanCloud',
None = '',
}
export type AzureAuthType = 'msi' | 'clientsecret';
export type ConcealedSecret = symbol;
interface AzureCredentialsBase {
authType: AzureAuthType;
defaultSubscriptionId?: string;
}
export interface AzureManagedIdentityCredentials extends AzureCredentialsBase {
authType: 'msi';
}
export interface AzureClientSecretCredentials extends AzureCredentialsBase {
authType: 'clientsecret';
azureCloud?: string;
tenantId?: string;
clientId?: string;
clientSecret?: string | ConcealedSecret;
}
export type AzureCredentials = AzureManagedIdentityCredentials | AzureClientSecretCredentials;
export interface AzureDataSourceJsonData extends DataSourceJsonData {
cloudName: string;
azureAuthType?: AzureAuthType;
// monitor
tenantId?: string;
clientId?: string;
subscriptionId?: string;
// logs
logAnalyticsDefaultWorkspace?: string;
/** @deprecated Azure Logs credentials */
azureLogAnalyticsSameAs?: boolean;
/** @deprecated Azure Logs credentials */
logAnalyticsTenantId?: string;
/** @deprecated Azure Logs credentials */
logAnalyticsClientId?: string;
/** @deprecated Azure Logs credentials */
logAnalyticsSubscriptionId?: string;
// App Insights
appInsightsAppId?: string;
}
export interface AzureDataSourceSecureJsonData {
clientSecret?: string;
appInsightsApiKey?: string;
}
export interface AzureMetricDimension {
dimension: string;
operator: 'eq'; // future proof
filter?: string; // *
}
export interface AzureMetricQuery {
resourceGroup: string | undefined;
resourceName: string | undefined;
metricDefinition: string | undefined;
metricNamespace: string | undefined;
metricName: string | undefined;
timeGrainUnit?: string;
timeGrain: string;
allowedTimeGrainsMs: number[];
aggregation: string | undefined;
dimensionFilters: AzureMetricDimension[];
alias: string;
top: string;
}
export interface AzureLogsQuery {
query: string;
resultFormat: string;
resource?: string;
/** @deprecated Queries should be migrated to use Resource instead */
workspace?: string;
}
export interface AzureResourceGraphQuery {
query: string;
resultFormat: string;
}
export interface ApplicationInsightsQuery {
metricName: string;
timeGrain: string;
timeGrainCount: string;
timeGrainType: string;
timeGrainUnit: string;
aggregation: string;
dimension: string[]; // Was string before 7.1
// dimensions: string[]; why is this metadata stored on the object!
dimensionFilter: string;
alias: string;
}
export interface InsightsAnalyticsQuery {
query: string;
resultFormat: string;
}
// Represents an errors that come back from frontend requests.
// Not totally sure how accurate this type is.
export type AzureMonitorErrorish = Error;
// Azure Monitor API Types
export interface AzureMonitorMetricsMetadataResponse {
value: AzureMonitorMetricMetadataItem[];
}
export interface AzureMonitorMetricMetadataItem {
id: string;
resourceId: string;
primaryAggregationType: string;
supportedAggregationTypes: string[];
name: AzureMonitorLocalizedValue;
dimensions?: AzureMonitorLocalizedValue[];
metricAvailabilities?: AzureMonitorMetricAvailabilityMetadata[];
}
export interface AzureMonitorMetricAvailabilityMetadata {
timeGrain: string;
retention: string;
}
export interface AzureMonitorLocalizedValue {
value: string;
localizedValue: string;
}
export interface AzureMonitorMetricDefinitionsResponse {
data: {
value: Array<{ name: string; type: string; location?: string }>;
};
status: number;
statusText: string;
}
export interface AzureMonitorResourceGroupsResponse {
data: {
value: Array<{ name: string }>;
};
status: number;
statusText: string;
}
export interface AzureLogsVariable {
text: string;
value: string;
}
export interface AzureLogsTableData extends TableData {
columns: AzureLogsTableColumn[];
rows: any[];
type: string;
}
export interface AzureLogsTableColumn {
text: string;
type: string;
}
export interface AzureMonitorOption<T = string> {
label: string;
value: T;
}
export interface AzureQueryEditorFieldProps {
query: AzureMonitorQuery;
datasource: Datasource;
subscriptionId?: string;
variableOptionGroup: { label: string; options: AzureMonitorOption[] };
onQueryChange: (newQuery: AzureMonitorQuery) => void;
setError: (source: string, error: AzureMonitorErrorish | undefined) => void;
}
export interface AzureResourceSummaryItem {
subscriptionName: string;
resourceGroupName: string | undefined;
resourceName: string | undefined;
}
export interface RawAzureResourceGroupItem {
subscriptionURI: string;
subscriptionName: string;
resourceGroupURI: string;
resourceGroupName: string;
}
export interface RawAzureResourceItem {
id: string;
name: string;
subscriptionId: string;
resourceGroup: string;
type: string;
location: string;
}
export interface AzureGraphResponse<T = unknown> {
data: T;
}
export * from './logAnalyticsMetadata';
export * from './query';
export * from './types';

View File

@ -0,0 +1,105 @@
import { DataQuery } from '@grafana/data';
export enum AzureQueryType {
AzureMonitor = 'Azure Monitor',
ApplicationInsights = 'Application Insights',
InsightsAnalytics = 'Insights Analytics',
LogAnalytics = 'Azure Log Analytics',
AzureResourceGraph = 'Azure Resource Graph',
}
/**
* Represents the query as it moves through the frontend query editor and datasource files.
* It can represent new queries that are still being edited, so all properties are optional
*/
export interface AzureMonitorQuery extends DataQuery {
queryType?: AzureQueryType;
subscription?: string;
/** ARG uses multiple subscriptions */
subscriptions?: string[];
azureMonitor?: AzureMetricQuery;
azureLogAnalytics?: AzureLogsQuery;
azureResourceGraph?: AzureResourceGraphQuery;
/** @deprecated App Insights/Insights Analytics deprecated in v8 */
appInsights?: ApplicationInsightsQuery;
/** @deprecated App Insights/Insights Analytics deprecated in v8 */
insightsAnalytics?: InsightsAnalyticsQuery;
}
/**
* Azure Monitor Metrics sub-query properties
*/
export interface AzureMetricQuery {
resourceGroup?: string;
resourceName?: string;
metricDefinition?: string;
metricNamespace?: string;
metricName?: string;
timeGrain?: string;
allowedTimeGrainsMs?: number[];
aggregation?: string;
dimensionFilters?: AzureMetricDimension[];
alias?: string;
top?: string;
/** @deprecated */
timeGrainUnit?: string;
}
/**
* Azure Monitor Logs sub-query properties
*/
export interface AzureLogsQuery {
query?: string;
resultFormat?: string;
resource?: string;
workspace?: string;
}
/**
* Azure Monitor ARG sub-query properties
*/
export interface AzureResourceGraphQuery {
query?: string;
resultFormat?: string;
}
/**
* Azure Monitor App Insights sub-query properties
* @deprecated App Insights deprecated in v8 in favor of Metrics queries
*/
export interface ApplicationInsightsQuery {
metricName?: string;
timeGrain?: string;
timeGrainCount?: string;
timeGrainType?: string;
timeGrainUnit?: string;
aggregation?: string;
dimension?: string[]; // Was string before 7.1
dimensionFilter?: string;
alias?: string;
}
/**
* Azure Monitor Insights Analytics sub-query properties
* @deprecated Insights Analytics deprecated in v8 in favor of Logs queries
*/
export interface InsightsAnalyticsQuery {
query?: string;
resultFormat?: string;
/** @deprecated Migrate field to query */
rawQueryString?: string;
}
export interface AzureMetricDimension {
dimension: string;
operator: 'eq'; // future proof
filter?: string; // *
}

View File

@ -0,0 +1,181 @@
import { DataSourceInstanceSettings, DataSourceJsonData, DataSourceSettings, TableData } from '@grafana/data';
import Datasource from '../datasource';
import { AzureMonitorQuery } from './query';
export type AzureDataSourceSettings = DataSourceSettings<AzureDataSourceJsonData, AzureDataSourceSecureJsonData>;
export type AzureDataSourceInstanceSettings = DataSourceInstanceSettings<AzureDataSourceJsonData>;
export interface DatasourceValidationResult {
status: 'success' | 'error';
message: string;
title?: string;
}
export type AzureResultFormat = 'time_series' | 'table';
/**
* Azure clouds known to Azure Monitor.
*/
export enum AzureCloud {
Public = 'AzureCloud',
China = 'AzureChinaCloud',
USGovernment = 'AzureUSGovernment',
Germany = 'AzureGermanCloud',
None = '',
}
export type AzureAuthType = 'msi' | 'clientsecret';
export type ConcealedSecret = symbol;
interface AzureCredentialsBase {
authType: AzureAuthType;
defaultSubscriptionId?: string;
}
export interface AzureManagedIdentityCredentials extends AzureCredentialsBase {
authType: 'msi';
}
export interface AzureClientSecretCredentials extends AzureCredentialsBase {
authType: 'clientsecret';
azureCloud?: string;
tenantId?: string;
clientId?: string;
clientSecret?: string | ConcealedSecret;
}
export type AzureCredentials = AzureManagedIdentityCredentials | AzureClientSecretCredentials;
export interface AzureDataSourceJsonData extends DataSourceJsonData {
cloudName: string;
azureAuthType?: AzureAuthType;
// monitor
tenantId?: string;
clientId?: string;
subscriptionId?: string;
// logs
logAnalyticsDefaultWorkspace?: string;
/** @deprecated Azure Logs credentials */
azureLogAnalyticsSameAs?: boolean;
/** @deprecated Azure Logs credentials */
logAnalyticsTenantId?: string;
/** @deprecated Azure Logs credentials */
logAnalyticsClientId?: string;
/** @deprecated Azure Logs credentials */
logAnalyticsSubscriptionId?: string;
// App Insights
appInsightsAppId?: string;
}
export interface AzureDataSourceSecureJsonData {
clientSecret?: string;
appInsightsApiKey?: string;
}
// Represents an errors that come back from frontend requests.
// Not totally sure how accurate this type is.
export type AzureMonitorErrorish = Error;
// Azure Monitor API Types
export interface AzureMonitorMetricsMetadataResponse {
value: AzureMonitorMetricMetadataItem[];
}
export interface AzureMonitorMetricMetadataItem {
id: string;
resourceId: string;
primaryAggregationType: string;
supportedAggregationTypes: string[];
name: AzureMonitorLocalizedValue;
dimensions?: AzureMonitorLocalizedValue[];
metricAvailabilities?: AzureMonitorMetricAvailabilityMetadata[];
}
export interface AzureMonitorMetricAvailabilityMetadata {
timeGrain: string;
retention: string;
}
export interface AzureMonitorLocalizedValue {
value: string;
localizedValue: string;
}
export interface AzureMonitorMetricDefinitionsResponse {
data: {
value: Array<{ name: string; type: string; location?: string }>;
};
status: number;
statusText: string;
}
export interface AzureMonitorResourceGroupsResponse {
data: {
value: Array<{ name: string }>;
};
status: number;
statusText: string;
}
export interface AzureLogsVariable {
text: string;
value: string;
}
export interface AzureLogsTableData extends TableData {
columns: AzureLogsTableColumn[];
rows: any[];
type: string;
}
export interface AzureLogsTableColumn {
text: string;
type: string;
}
export interface AzureMonitorOption<T = string> {
label: string;
value: T;
}
export interface AzureQueryEditorFieldProps {
query: AzureMonitorQuery;
datasource: Datasource;
subscriptionId?: string;
variableOptionGroup: { label: string; options: AzureMonitorOption[] };
onQueryChange: (newQuery: AzureMonitorQuery) => void;
setError: (source: string, error: AzureMonitorErrorish | undefined) => void;
}
export interface AzureResourceSummaryItem {
subscriptionName: string;
resourceGroupName: string | undefined;
resourceName: string | undefined;
}
export interface RawAzureResourceGroupItem {
subscriptionURI: string;
subscriptionName: string;
resourceGroupURI: string;
resourceGroupName: string;
}
export interface RawAzureResourceItem {
id: string;
name: string;
subscriptionId: string;
resourceGroup: string;
type: string;
location: string;
}
export interface AzureGraphResponse<T = unknown> {
data: T;
}