Azure monitor: Make sure alert rule editor is not enabled when template variables are being used (#41335)

* check if variable exist in query

* add tests

* pr feedback
This commit is contained in:
Erik Sundell 2021-11-05 12:21:28 +01:00 committed by GitHub
parent cbc00babe4
commit 07754351b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 186 additions and 56 deletions

View File

@ -0,0 +1,51 @@
import { CustomVariableModel, initialVariableModelState, VariableHide } from 'app/features/variables/types';
export const subscriptionsVariable: CustomVariableModel = {
...initialVariableModelState,
id: 'subs',
name: 'subs',
index: 3,
current: { value: ['sub-foo', 'sub-baz'], text: 'sub-foo + sub-baz', selected: true },
options: [
{ selected: true, value: 'sub-foo', text: 'sub-foo' },
{ selected: false, value: 'sub-bar', text: 'sub-bar' },
{ selected: true, value: 'sub-baz', text: 'sub-baz' },
],
multi: true,
includeAll: false,
query: '',
hide: VariableHide.dontHide,
type: 'custom',
};
export const singleVariable: 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',
};
export const multiVariable: 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',
};

View File

@ -2,8 +2,10 @@ import AzureMonitorDatasource from '../datasource';
import AzureLogAnalyticsDatasource from './azure_log_analytics_datasource'; import AzureLogAnalyticsDatasource from './azure_log_analytics_datasource';
import FakeSchemaData from './__mocks__/schema'; import FakeSchemaData from './__mocks__/schema';
import { TemplateSrv } from 'app/features/templating/template_srv'; import { TemplateSrv } from 'app/features/templating/template_srv';
import { AzureMonitorQuery, DatasourceValidationResult } from '../types'; import { AzureMonitorQuery, AzureQueryType, DatasourceValidationResult } from '../types';
import { toUtc } from '@grafana/data'; import { toUtc } from '@grafana/data';
import createMockQuery from '../__mocks__/query';
import { singleVariable } from '../__mocks__/variables';
const templateSrv = new TemplateSrv(); const templateSrv = new TemplateSrv();
@ -227,6 +229,37 @@ describe('AzureLogAnalyticsDatasource', () => {
}); });
}); });
describe('When performing targetContainsTemplate', () => {
it('should return false when no variable is being used', () => {
const query = createMockQuery();
const ds = new AzureMonitorDatasource(ctx.instanceSettings);
query.queryType = AzureQueryType.LogAnalytics;
expect(ds.targetContainsTemplate(query)).toEqual(false);
});
it('should return true when resource field is using a variable', () => {
const templateSrv = new TemplateSrv();
const query = createMockQuery();
templateSrv.init([singleVariable]);
const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv);
query.queryType = AzureQueryType.LogAnalytics;
query.azureLogAnalytics = { resource: `$${singleVariable.name}` };
expect(ds.targetContainsTemplate(query)).toEqual(true);
});
it('should return false when a variable is used in a different part of the query', () => {
const templateSrv = new TemplateSrv();
const query = createMockQuery();
templateSrv.init([singleVariable]);
const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv);
query.queryType = AzureQueryType.LogAnalytics;
query.azureResourceGraph = { query: `$${singleVariable.name}` };
expect(ds.targetContainsTemplate(query)).toEqual(false);
});
});
describe('When performing filterQuery', () => { describe('When performing filterQuery', () => {
const ctx: any = {}; const ctx: any = {};
let laDatasource: AzureLogAnalyticsDatasource; let laDatasource: AzureLogAnalyticsDatasource;

View File

@ -2,7 +2,9 @@ import AzureMonitorDatasource from '../datasource';
import { TemplateSrv } from 'app/features/templating/template_srv'; import { TemplateSrv } from 'app/features/templating/template_srv';
import { DataSourceInstanceSettings } from '@grafana/data'; import { DataSourceInstanceSettings } from '@grafana/data';
import { AzureDataSourceJsonData, DatasourceValidationResult } from '../types'; import { AzureDataSourceJsonData, AzureQueryType, DatasourceValidationResult } from '../types';
import createMockQuery from '../__mocks__/query';
import { singleVariable, subscriptionsVariable } from '../__mocks__/variables';
const templateSrv = new TemplateSrv(); const templateSrv = new TemplateSrv();
@ -485,6 +487,36 @@ describe('AzureMonitorDatasource', () => {
}); });
}); });
describe('When performing targetContainsTemplate', () => {
it('should return false when no variable is being used', () => {
const query = createMockQuery();
query.queryType = AzureQueryType.AzureMonitor;
expect(ctx.ds.targetContainsTemplate(query)).toEqual(false);
});
it('should return true when subscriptions field is using a variable', () => {
const query = createMockQuery();
const templateSrv = new TemplateSrv();
templateSrv.init([subscriptionsVariable]);
const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv);
query.queryType = AzureQueryType.AzureMonitor;
query.subscription = `$${subscriptionsVariable.name}`;
expect(ds.targetContainsTemplate(query)).toEqual(true);
});
it('should return false when a variable is used in a different part of the query', () => {
const query = createMockQuery();
const templateSrv = new TemplateSrv();
templateSrv.init([singleVariable]);
const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv);
query.queryType = AzureQueryType.AzureMonitor;
query.azureLogAnalytics = { resource: `$${singleVariable.name}` };
expect(ds.targetContainsTemplate(query)).toEqual(false);
});
});
it('should return an empty array for a Metric that does not have dimensions', () => { it('should return an empty array for a Metric that does not have dimensions', () => {
return ctx.ds return ctx.ds
.getMetricMetadata( .getMetricMetadata(

View File

@ -1,64 +1,17 @@
import { TemplateSrv } from 'app/features/templating/template_srv'; import { TemplateSrv } from 'app/features/templating/template_srv';
import { backendSrv } from 'app/core/services/backend_srv'; import { backendSrv } from 'app/core/services/backend_srv';
import AzureResourceGraphDatasource from './azure_resource_graph_datasource'; import AzureResourceGraphDatasource from './azure_resource_graph_datasource';
import { CustomVariableModel, initialVariableModelState, VariableHide } from 'app/features/variables/types'; import { multiVariable, singleVariable, subscriptionsVariable } from '../__mocks__/variables';
import { AzureQueryType } from '../types';
const single: CustomVariableModel = { import AzureMonitorDatasource from '../datasource';
...initialVariableModelState, import createMockQuery from '../__mocks__/query';
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',
};
const subs: CustomVariableModel = {
...initialVariableModelState,
id: 'subs',
name: 'subs',
index: 3,
current: { value: ['sub-foo', 'sub-baz'], text: 'sub-foo + sub-baz', selected: true },
options: [
{ selected: true, value: 'sub-foo', text: 'sub-foo' },
{ selected: false, value: 'sub-bar', text: 'sub-bar' },
{ selected: true, value: 'sub-baz', text: 'sub-baz' },
],
multi: true,
includeAll: false,
query: '',
hide: VariableHide.dontHide,
type: 'custom',
};
const templateSrv = new TemplateSrv({ const templateSrv = new TemplateSrv({
getVariables: () => [subs, single, multi], getVariables: () => [subscriptionsVariable, singleVariable, multiVariable],
getVariableWithName: jest.fn(), getVariableWithName: jest.fn(),
getFilteredVariables: jest.fn(), getFilteredVariables: jest.fn(),
}); });
templateSrv.init([subs, single, multi]); templateSrv.init([subscriptionsVariable, singleVariable, multiVariable]);
jest.mock('app/core/services/backend_srv'); jest.mock('app/core/services/backend_srv');
jest.mock('@grafana/runtime', () => ({ jest.mock('@grafana/runtime', () => ({
@ -80,6 +33,7 @@ describe('AzureResourceGraphDatasource', () => {
beforeEach(() => { beforeEach(() => {
ctx.instanceSettings = { ctx.instanceSettings = {
url: 'http://azureresourcegraphapi', url: 'http://azureresourcegraphapi',
jsonData: { subscriptionId: '9935389e-9122-4ef9-95f9-1513dd24753f', cloudName: 'azuremonitor' },
}; };
ctx.ds = new AzureResourceGraphDatasource(ctx.instanceSettings); ctx.ds = new AzureResourceGraphDatasource(ctx.instanceSettings);
@ -138,4 +92,47 @@ describe('AzureResourceGraphDatasource', () => {
subscriptions: ['sub-foo', 'sub-baz'], subscriptions: ['sub-foo', 'sub-baz'],
}); });
}); });
describe('When performing targetContainsTemplate', () => {
it('should return false when no variable is being used', () => {
const query = createMockQuery();
const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv);
query.queryType = AzureQueryType.AzureResourceGraph;
expect(ds.targetContainsTemplate(query)).toEqual(false);
});
it('should return true when resource field is using a variable', () => {
const query = createMockQuery();
const templateSrv = new TemplateSrv();
templateSrv.init([singleVariable]);
const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv);
query.queryType = AzureQueryType.AzureResourceGraph;
query.azureResourceGraph = { query: `$${singleVariable.name}` };
expect(ds.targetContainsTemplate(query)).toEqual(true);
});
it('should return true when resource field is using a variable in the subscriptions field', () => {
const query = createMockQuery();
const templateSrv = new TemplateSrv();
templateSrv.init([multiVariable]);
const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv);
query.queryType = AzureQueryType.AzureResourceGraph;
query.subscriptions = [multiVariable.name];
query.azureResourceGraph = { query: `$${multiVariable.name}` };
expect(ds.targetContainsTemplate(query)).toEqual(true);
});
it('should return false when a variable is used in a different part of the query', () => {
const query = createMockQuery();
const templateSrv = new TemplateSrv();
templateSrv.init([singleVariable]);
const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv);
query.queryType = AzureQueryType.AzureResourceGraph;
query.azureMonitor = { metricName: `$${singleVariable.name}` };
expect(ds.targetContainsTemplate(query)).toEqual(false);
});
});
}); });

View File

@ -14,7 +14,7 @@ import {
ScopedVars, ScopedVars,
} from '@grafana/data'; } from '@grafana/data';
import { forkJoin, Observable, of } from 'rxjs'; import { forkJoin, Observable, of } from 'rxjs';
import { getTemplateSrv, TemplateSrv } from '@grafana/runtime'; import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv';
import InsightsAnalyticsDatasource from './insights_analytics/insights_analytics_datasource'; import InsightsAnalyticsDatasource from './insights_analytics/insights_analytics_datasource';
import { datasourceMigrations } from './utils/migrateQuery'; import { datasourceMigrations } from './utils/migrateQuery';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
@ -131,6 +131,23 @@ export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDa
return of({ state: LoadingState.Done, data: [] }); return of({ state: LoadingState.Done, data: [] });
} }
targetContainsTemplate(query: AzureMonitorQuery) {
if (query.subscription && this.templateSrv.variableExists(query.subscription)) {
return true;
}
let subQuery;
if (query.queryType === AzureQueryType.AzureMonitor) {
subQuery = JSON.stringify(query.azureMonitor);
} else if (query.queryType === AzureQueryType.LogAnalytics) {
subQuery = JSON.stringify(query.azureLogAnalytics);
} else if (query.queryType === AzureQueryType.AzureResourceGraph) {
subQuery = JSON.stringify([query.azureResourceGraph, query.subscriptions]);
}
return !!subQuery && this.templateSrv.variableExists(subQuery);
}
async annotationQuery(options: any) { async annotationQuery(options: any) {
return this.azureLogAnalyticsDatasource.annotationQuery(options); return this.azureLogAnalyticsDatasource.annotationQuery(options);
} }