Azure Monitor: Improved error messages for variable queries (#43213)

This commit is contained in:
Erik Sundell 2022-01-17 15:51:56 +01:00 committed by GitHub
parent 715166baf3
commit 5ae5a2e0d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 16 deletions

View File

@ -20,3 +20,15 @@ export function invalidNamespaceError() {
}, },
}; };
} }
export function invalidSubscriptionError() {
return {
status: 400,
data: {
error: {
code: 'InvalidSubscriptionId',
message: "The provided subscription identifier 'abc' is malformed or invalid.",
},
},
};
}

View File

@ -6,8 +6,6 @@ import createMockDatasource from '../../__mocks__/datasource';
import { AzureMonitorQuery, AzureQueryType } from '../../types'; import { AzureMonitorQuery, AzureQueryType } from '../../types';
import { select } from 'react-select-event'; import { select } from 'react-select-event';
import * as ui from '@grafana/ui'; import * as ui from '@grafana/ui';
// eslint-disable-next-line lodash/import-scope
import _ from 'lodash';
// Have to mock CodeEditor because it doesnt seem to work in tests??? // Have to mock CodeEditor because it doesnt seem to work in tests???
jest.mock('@grafana/ui', () => ({ jest.mock('@grafana/ui', () => ({
@ -17,11 +15,6 @@ jest.mock('@grafana/ui', () => ({
}, },
})); }));
jest.mock('lodash', () => ({
...jest.requireActual<typeof _>('lodash'),
debounce: jest.fn((fn: unknown) => fn),
}));
describe('VariableEditor:', () => { describe('VariableEditor:', () => {
it('can select a query type', async () => { it('can select a query type', async () => {
const onChange = jest.fn(); const onChange = jest.fn();
@ -141,7 +134,6 @@ describe('VariableEditor:', () => {
} as AzureMonitorQuery, } as AzureMonitorQuery,
onChange: jest.fn(), onChange: jest.fn(),
datasource: createMockDatasource(), datasource: createMockDatasource(),
debounceTime: 1,
}; };
render(<VariableEditor {...props} />); render(<VariableEditor {...props} />);
await waitFor(() => screen.queryByText('Grafana template variable function')); await waitFor(() => screen.queryByText('Grafana template variable function'));

View File

@ -1,13 +1,12 @@
import { SelectableValue } from '@grafana/data'; import { SelectableValue } from '@grafana/data';
import { Alert, InlineField, Input, Select } from '@grafana/ui'; import { Alert, InlineField, Input, Select } from '@grafana/ui';
import React, { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react'; import React, { ChangeEvent, useCallback, useEffect, useState } from 'react';
import { AzureMonitorQuery, AzureQueryType } from '../../types'; import { AzureMonitorQuery, AzureQueryType } from '../../types';
import LogsQueryEditor from '../LogsQueryEditor'; import LogsQueryEditor from '../LogsQueryEditor';
import DataSource from '../../datasource'; import DataSource from '../../datasource';
import useLastError from '../../utils/useLastError'; import useLastError from '../../utils/useLastError';
import { Space } from '../Space'; import { Space } from '../Space';
import { migrateStringQueriesToObjectQueries } from '../../grafanaTemplateVariableFns'; import { migrateStringQueriesToObjectQueries } from '../../grafanaTemplateVariableFns';
import { debounce } from 'lodash';
const AZURE_QUERY_VARIABLE_TYPE_OPTIONS = [ const AZURE_QUERY_VARIABLE_TYPE_OPTIONS = [
{ label: 'Grafana Query Function', value: AzureQueryType.GrafanaTemplateVariableFn }, { label: 'Grafana Query Function', value: AzureQueryType.GrafanaTemplateVariableFn },
@ -46,18 +45,18 @@ const GrafanaTemplateVariableFnInput = ({
}, },
[datasource, query, updateQuery] [datasource, query, updateQuery]
); );
const debouncedRunQuery = useMemo(() => debounce(onRunQuery, 500), [onRunQuery]);
const onChange = (event: ChangeEvent<HTMLInputElement>) => { const onChange = (event: ChangeEvent<HTMLInputElement>) => {
setInputVal(event.target.value); setInputVal(event.target.value);
debouncedRunQuery(event.target.value);
}; };
return ( return (
<InlineField label="Grafana template variable function"> <InlineField label="Grafana template variable function">
<Input <Input
placeholder={'type a grafana template variable function, ex: Subscriptions()'} placeholder={'type a grafana template variable function, ex: Subscriptions()'}
value={inputVal} value={inputVal}
onChange={onChange} onChange={onChange}
onBlur={() => onRunQuery(inputVal)}
/> />
</InlineField> </InlineField>
); );

View File

@ -3,6 +3,7 @@ import { from } from 'rxjs';
import { AzureMonitorQuery, AzureQueryType } from './types'; import { AzureMonitorQuery, AzureQueryType } from './types';
import { VariableSupport } from './variables'; import { VariableSupport } from './variables';
import createMockDatasource from './__mocks__/datasource'; import createMockDatasource from './__mocks__/datasource';
import { invalidSubscriptionError } from './__mocks__/errors';
jest.mock('@grafana/runtime', () => ({ jest.mock('@grafana/runtime', () => ({
...((jest.requireActual('@grafana/runtime') as unknown) as object), ...((jest.requireActual('@grafana/runtime') as unknown) as object),
@ -499,4 +500,33 @@ describe('VariableSupport', () => {
done(); done();
}); });
}); });
it('should handle http error', (done) => {
const error = invalidSubscriptionError();
const variableSupport = new VariableSupport(
createMockDatasource({
azureLogAnalyticsDatasource: {
defaultSubscriptionId: 'defaultSubscriptionId',
},
getResourceGroups: jest.fn().mockRejectedValue(error),
})
);
const mockRequest = {
targets: [
{
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: {
kind: 'ResourceGroupsQuery',
rawQuery: 'ResourceGroups()',
},
} as AzureMonitorQuery,
],
} as DataQueryRequest<AzureMonitorQuery>;
const observables = variableSupport.query(mockRequest);
observables.subscribe((result: DataQueryResponseData) => {
expect(result.error?.message).toBe(error.data.error.message);
done();
});
});
}); });

View File

@ -12,6 +12,7 @@ import { AzureQueryType, AzureMonitorQuery } from './types';
import { getTemplateSrv } from '@grafana/runtime'; import { getTemplateSrv } from '@grafana/runtime';
import { migrateStringQueriesToObjectQueries } from './grafanaTemplateVariableFns'; import { migrateStringQueriesToObjectQueries } from './grafanaTemplateVariableFns';
import { GrafanaTemplateVariableQuery } from './types/templateVariables'; import { GrafanaTemplateVariableQuery } from './types/templateVariables';
import messageFromError from './utils/messageFromError';
export class VariableSupport extends CustomVariableSupport<DataSource, AzureMonitorQuery> { export class VariableSupport extends CustomVariableSupport<DataSource, AzureMonitorQuery> {
constructor(private readonly datasource: DataSource) { constructor(private readonly datasource: DataSource) {
super(); super();
@ -26,10 +27,14 @@ export class VariableSupport extends CustomVariableSupport<DataSource, AzureMoni
const queryObj = await migrateStringQueriesToObjectQueries(request.targets[0], { datasource: this.datasource }); const queryObj = await migrateStringQueriesToObjectQueries(request.targets[0], { datasource: this.datasource });
if (queryObj.queryType === AzureQueryType.GrafanaTemplateVariableFn && queryObj.grafanaTemplateVariableFn) { if (queryObj.queryType === AzureQueryType.GrafanaTemplateVariableFn && queryObj.grafanaTemplateVariableFn) {
const templateVariablesResults = await this.callGrafanaTemplateVariableFn(queryObj.grafanaTemplateVariableFn); try {
return { const templateVariablesResults = await this.callGrafanaTemplateVariableFn(queryObj.grafanaTemplateVariableFn);
data: templateVariablesResults ? [toDataFrame(templateVariablesResults)] : [], return {
}; data: templateVariablesResults ? [toDataFrame(templateVariablesResults)] : [],
};
} catch (err) {
return { data: [], error: { message: messageFromError(err) } };
}
} }
request.targets[0] = queryObj; request.targets[0] = queryObj;
return lastValueFrom(this.datasource.query(request)); return lastValueFrom(this.datasource.query(request));